From d2bec10f92199fba3279decb61ee4ee68acd4990 Mon Sep 17 00:00:00 2001 From: Ronan Collobert Date: Thu, 24 Sep 2015 18:34:07 -0700 Subject: [PATCH] switched to klib - slightly better perf - slightly better memory footprint - handles any key types (not limited to string and numbers anymore) - much better iterator (previous one was duplicating memory) --- CMakeLists.txt | 6 - README.md | 2 +- cdefs.lua | 28 +- hash.lua | 62 +- klib/khash.h | 627 +++ tds_elem.c | 85 +- tds_elem.h | 3 + tds_hash.c | 231 +- tds_hash.h | 23 +- tommyds/.gitignore | 49 - tommyds/.travis.yml | 10 - tommyds/AUTHORS | 9 - tommyds/HISTORY | 80 - tommyds/INSTALL | 8 - tommyds/LICENSE | 24 - tommyds/Makefile | 215 - tommyds/README | 31 - tommyds/benchmark.cc | 2674 ----------- tommyds/benchmark.geany | 19 - tommyds/benchmark.sln | 26 - tommyds/benchmark.vcxproj | 187 - tommyds/benchmark/gr_all.bat | 54 - tommyds/benchmark/gr_all.sh | 71 - tommyds/benchmark/gr_common.gnu | 39 - tommyds/benchmark/gr_def_random_change.gnu | 12 - tommyds/benchmark/gr_def_random_hit.gnu | 12 - tommyds/benchmark/gr_forward_change.gnu | 9 - tommyds/benchmark/gr_forward_hit.gnu | 9 - tommyds/benchmark/gr_forward_insert.gnu | 9 - tommyds/benchmark/gr_forward_miss.gnu | 9 - tommyds/benchmark/gr_forward_remove.gnu | 9 - tommyds/benchmark/gr_forward_size.gnu | 13 - tommyds/benchmark/gr_other_ck_problem.gnu | 14 - .../gr_other_googlelibchash_problem.gnu | 14 - tommyds/benchmark/gr_other_judy_problem.gnu | 14 - tommyds/benchmark/gr_random_change.gnu | 9 - tommyds/benchmark/gr_random_hit.gnu | 9 - tommyds/benchmark/gr_random_insert.gnu | 9 - tommyds/benchmark/gr_random_miss.gnu | 9 - tommyds/benchmark/gr_random_remove.gnu | 9 - tommyds/benchmark/gr_random_size.gnu | 13 - .../benchmark/lib/cpp-btree/CMakeLists.txt | 38 - tommyds/benchmark/lib/cpp-btree/COPYING | 202 - tommyds/benchmark/lib/cpp-btree/README | 20 - tommyds/benchmark/lib/cpp-btree/btree.h | 2396 ---------- .../benchmark/lib/cpp-btree/btree_bench.cc | 594 --- .../benchmark/lib/cpp-btree/btree_container.h | 350 -- tommyds/benchmark/lib/cpp-btree/btree_map.h | 130 - tommyds/benchmark/lib/cpp-btree/btree_set.h | 121 - tommyds/benchmark/lib/cpp-btree/btree_test.cc | 248 - tommyds/benchmark/lib/cpp-btree/btree_test.h | 932 ---- .../lib/cpp-btree/btree_test_flags.cc | 20 - tommyds/benchmark/lib/cpp-btree/safe_btree.h | 395 -- .../benchmark/lib/cpp-btree/safe_btree_map.h | 89 - .../benchmark/lib/cpp-btree/safe_btree_set.h | 88 - .../lib/cpp-btree/safe_btree_test.cc | 116 - .../lib/cube/binary-search-tesseract-1.0.c | 923 ---- tommyds/benchmark/lib/google/README | 14 - tommyds/benchmark/lib/google/dense_hash_map | 332 -- tommyds/benchmark/lib/google/dense_hash_set | 308 -- tommyds/benchmark/lib/google/libchash.c | 1539 ------- tommyds/benchmark/lib/google/libchash.h | 252 -- tommyds/benchmark/lib/google/sparse_hash_map | 309 -- tommyds/benchmark/lib/google/sparse_hash_set | 285 -- .../lib/google/sparsehash/densehashtable.h | 1264 ------ .../lib/google/sparsehash/hashtable-common.h | 178 - .../sparsehash/libc_allocator_with_realloc.h | 121 - .../lib/google/sparsehash/sparseconfig.h | 32 - .../lib/google/sparsehash/sparsehashtable.h | 1187 ----- tommyds/benchmark/lib/google/sparsetable | 1598 ------- tommyds/benchmark/lib/google/type_traits.h | 336 -- tommyds/benchmark/lib/judy/Judy.h | 626 --- tommyds/benchmark/lib/judy/Judy.lib | Bin 201574 -> 0 bytes tommyds/benchmark/lib/judy/build.bat | 185 - tommyds/benchmark/lib/judy/libJudyL.a | Bin 352262 -> 0 bytes tommyds/benchmark/lib/judy/libJudyMalloc.a | Bin 5852 -> 0 bytes tommyds/benchmark/lib/judyarray/judy64na.c | 2099 --------- tommyds/benchmark/lib/khash/khash.h | 315 -- tommyds/benchmark/lib/nedtries/nedtrie.h | 1642 ------- tommyds/benchmark/lib/rb/rb_new.h | 973 ---- tommyds/benchmark/lib/rb/rb_newer.h | 970 ---- tommyds/benchmark/lib/rb/rb_old.h | 532 --- tommyds/benchmark/lib/stx/README | 202 - tommyds/benchmark/lib/stx/btree | 39 - tommyds/benchmark/lib/stx/btree.dox | 255 -- tommyds/benchmark/lib/stx/btree.h | 3983 ----------------- tommyds/benchmark/lib/stx/btree_map | 39 - tommyds/benchmark/lib/stx/btree_map.h | 619 --- tommyds/benchmark/lib/stx/btree_multimap | 39 - tommyds/benchmark/lib/stx/btree_multimap.h | 610 --- tommyds/benchmark/lib/stx/btree_multiset | 39 - tommyds/benchmark/lib/stx/btree_multiset.h | 601 --- tommyds/benchmark/lib/stx/btree_set | 39 - tommyds/benchmark/lib/stx/btree_set.h | 602 --- tommyds/benchmark/lib/uthash/utarray.h | 224 - tommyds/benchmark/lib/uthash/uthash.h | 972 ---- tommyds/benchmark/lib/uthash/utlist.h | 490 -- tommyds/benchmark/lib/uthash/utstring.h | 137 - tommyds/check.c | 1373 ------ tommyds/tommy-footer.html | 2 - tommyds/tommy-header.html | 10 - tommyds/tommy.css | 803 ---- tommyds/tommy.doxygen | 1671 ------- tommyds/tommyds/tommy.c | 40 - tommyds/tommyds/tommy.h | 770 ---- tommyds/tommyds/tommyalloc.c | 140 - tommyds/tommyds/tommyalloc.h | 95 - tommyds/tommyds/tommyarray.c | 83 - tommyds/tommyds/tommyarray.h | 151 - tommyds/tommyds/tommyarrayblk.c | 82 - tommyds/tommyds/tommyarrayblk.h | 144 - tommyds/tommyds/tommyarrayblkof.c | 83 - tommyds/tommyds/tommyarrayblkof.h | 114 - tommyds/tommyds/tommyarrayof.c | 84 - tommyds/tommyds/tommyarrayof.h | 125 - tommyds/tommyds/tommychain.h | 224 - tommyds/tommyds/tommyhash.c | 167 - tommyds/tommyds/tommyhash.h | 112 - tommyds/tommyds/tommyhashdyn.c | 224 - tommyds/tommyds/tommyhashdyn.h | 295 -- tommyds/tommyds/tommyhashlin.c | 334 -- tommyds/tommyds/tommyhashlin.h | 349 -- tommyds/tommyds/tommyhashtbl.c | 142 - tommyds/tommyds/tommyhashtbl.h | 279 -- tommyds/tommyds/tommylist.c | 86 - tommyds/tommyds/tommylist.h | 372 -- tommyds/tommyds/tommytrie.c | 323 -- tommyds/tommyds/tommytrie.h | 260 -- tommyds/tommyds/tommytrieinp.c | 270 -- tommyds/tommyds/tommytrieinp.h | 239 - tommyds/tommyds/tommytypes.h | 410 -- tommyds/tommyweb-header.html | 25 - tommyds/tommyweb.doxygen | 5 - 133 files changed, 819 insertions(+), 44461 deletions(-) create mode 100644 klib/khash.h delete mode 100644 tommyds/.gitignore delete mode 100644 tommyds/.travis.yml delete mode 100644 tommyds/AUTHORS delete mode 100644 tommyds/HISTORY delete mode 100644 tommyds/INSTALL delete mode 100644 tommyds/LICENSE delete mode 100644 tommyds/Makefile delete mode 100644 tommyds/README delete mode 100644 tommyds/benchmark.cc delete mode 100644 tommyds/benchmark.geany delete mode 100644 tommyds/benchmark.sln delete mode 100644 tommyds/benchmark.vcxproj delete mode 100644 tommyds/benchmark/gr_all.bat delete mode 100644 tommyds/benchmark/gr_all.sh delete mode 100644 tommyds/benchmark/gr_common.gnu delete mode 100644 tommyds/benchmark/gr_def_random_change.gnu delete mode 100644 tommyds/benchmark/gr_def_random_hit.gnu delete mode 100644 tommyds/benchmark/gr_forward_change.gnu delete mode 100644 tommyds/benchmark/gr_forward_hit.gnu delete mode 100644 tommyds/benchmark/gr_forward_insert.gnu delete mode 100644 tommyds/benchmark/gr_forward_miss.gnu delete mode 100644 tommyds/benchmark/gr_forward_remove.gnu delete mode 100644 tommyds/benchmark/gr_forward_size.gnu delete mode 100644 tommyds/benchmark/gr_other_ck_problem.gnu delete mode 100644 tommyds/benchmark/gr_other_googlelibchash_problem.gnu delete mode 100644 tommyds/benchmark/gr_other_judy_problem.gnu delete mode 100644 tommyds/benchmark/gr_random_change.gnu delete mode 100644 tommyds/benchmark/gr_random_hit.gnu delete mode 100644 tommyds/benchmark/gr_random_insert.gnu delete mode 100644 tommyds/benchmark/gr_random_miss.gnu delete mode 100644 tommyds/benchmark/gr_random_remove.gnu delete mode 100644 tommyds/benchmark/gr_random_size.gnu delete mode 100644 tommyds/benchmark/lib/cpp-btree/CMakeLists.txt delete mode 100644 tommyds/benchmark/lib/cpp-btree/COPYING delete mode 100644 tommyds/benchmark/lib/cpp-btree/README delete mode 100644 tommyds/benchmark/lib/cpp-btree/btree.h delete mode 100644 tommyds/benchmark/lib/cpp-btree/btree_bench.cc delete mode 100644 tommyds/benchmark/lib/cpp-btree/btree_container.h delete mode 100644 tommyds/benchmark/lib/cpp-btree/btree_map.h delete mode 100644 tommyds/benchmark/lib/cpp-btree/btree_set.h delete mode 100644 tommyds/benchmark/lib/cpp-btree/btree_test.cc delete mode 100644 tommyds/benchmark/lib/cpp-btree/btree_test.h delete mode 100644 tommyds/benchmark/lib/cpp-btree/btree_test_flags.cc delete mode 100644 tommyds/benchmark/lib/cpp-btree/safe_btree.h delete mode 100644 tommyds/benchmark/lib/cpp-btree/safe_btree_map.h delete mode 100644 tommyds/benchmark/lib/cpp-btree/safe_btree_set.h delete mode 100644 tommyds/benchmark/lib/cpp-btree/safe_btree_test.cc delete mode 100644 tommyds/benchmark/lib/cube/binary-search-tesseract-1.0.c delete mode 100644 tommyds/benchmark/lib/google/README delete mode 100644 tommyds/benchmark/lib/google/dense_hash_map delete mode 100644 tommyds/benchmark/lib/google/dense_hash_set delete mode 100644 tommyds/benchmark/lib/google/libchash.c delete mode 100644 tommyds/benchmark/lib/google/libchash.h delete mode 100644 tommyds/benchmark/lib/google/sparse_hash_map delete mode 100644 tommyds/benchmark/lib/google/sparse_hash_set delete mode 100644 tommyds/benchmark/lib/google/sparsehash/densehashtable.h delete mode 100644 tommyds/benchmark/lib/google/sparsehash/hashtable-common.h delete mode 100644 tommyds/benchmark/lib/google/sparsehash/libc_allocator_with_realloc.h delete mode 100644 tommyds/benchmark/lib/google/sparsehash/sparseconfig.h delete mode 100644 tommyds/benchmark/lib/google/sparsehash/sparsehashtable.h delete mode 100644 tommyds/benchmark/lib/google/sparsetable delete mode 100644 tommyds/benchmark/lib/google/type_traits.h delete mode 100644 tommyds/benchmark/lib/judy/Judy.h delete mode 100644 tommyds/benchmark/lib/judy/Judy.lib delete mode 100644 tommyds/benchmark/lib/judy/build.bat delete mode 100644 tommyds/benchmark/lib/judy/libJudyL.a delete mode 100644 tommyds/benchmark/lib/judy/libJudyMalloc.a delete mode 100644 tommyds/benchmark/lib/judyarray/judy64na.c delete mode 100644 tommyds/benchmark/lib/khash/khash.h delete mode 100644 tommyds/benchmark/lib/nedtries/nedtrie.h delete mode 100644 tommyds/benchmark/lib/rb/rb_new.h delete mode 100644 tommyds/benchmark/lib/rb/rb_newer.h delete mode 100644 tommyds/benchmark/lib/rb/rb_old.h delete mode 100644 tommyds/benchmark/lib/stx/README delete mode 100644 tommyds/benchmark/lib/stx/btree delete mode 100644 tommyds/benchmark/lib/stx/btree.dox delete mode 100644 tommyds/benchmark/lib/stx/btree.h delete mode 100644 tommyds/benchmark/lib/stx/btree_map delete mode 100644 tommyds/benchmark/lib/stx/btree_map.h delete mode 100644 tommyds/benchmark/lib/stx/btree_multimap delete mode 100644 tommyds/benchmark/lib/stx/btree_multimap.h delete mode 100644 tommyds/benchmark/lib/stx/btree_multiset delete mode 100644 tommyds/benchmark/lib/stx/btree_multiset.h delete mode 100644 tommyds/benchmark/lib/stx/btree_set delete mode 100644 tommyds/benchmark/lib/stx/btree_set.h delete mode 100644 tommyds/benchmark/lib/uthash/utarray.h delete mode 100644 tommyds/benchmark/lib/uthash/uthash.h delete mode 100644 tommyds/benchmark/lib/uthash/utlist.h delete mode 100644 tommyds/benchmark/lib/uthash/utstring.h delete mode 100644 tommyds/check.c delete mode 100644 tommyds/tommy-footer.html delete mode 100644 tommyds/tommy-header.html delete mode 100644 tommyds/tommy.css delete mode 100644 tommyds/tommy.doxygen delete mode 100644 tommyds/tommyds/tommy.c delete mode 100644 tommyds/tommyds/tommy.h delete mode 100644 tommyds/tommyds/tommyalloc.c delete mode 100644 tommyds/tommyds/tommyalloc.h delete mode 100644 tommyds/tommyds/tommyarray.c delete mode 100644 tommyds/tommyds/tommyarray.h delete mode 100644 tommyds/tommyds/tommyarrayblk.c delete mode 100644 tommyds/tommyds/tommyarrayblk.h delete mode 100644 tommyds/tommyds/tommyarrayblkof.c delete mode 100644 tommyds/tommyds/tommyarrayblkof.h delete mode 100644 tommyds/tommyds/tommyarrayof.c delete mode 100644 tommyds/tommyds/tommyarrayof.h delete mode 100644 tommyds/tommyds/tommychain.h delete mode 100644 tommyds/tommyds/tommyhash.c delete mode 100644 tommyds/tommyds/tommyhash.h delete mode 100644 tommyds/tommyds/tommyhashdyn.c delete mode 100644 tommyds/tommyds/tommyhashdyn.h delete mode 100644 tommyds/tommyds/tommyhashlin.c delete mode 100644 tommyds/tommyds/tommyhashlin.h delete mode 100644 tommyds/tommyds/tommyhashtbl.c delete mode 100644 tommyds/tommyds/tommyhashtbl.h delete mode 100644 tommyds/tommyds/tommylist.c delete mode 100644 tommyds/tommyds/tommylist.h delete mode 100644 tommyds/tommyds/tommytrie.c delete mode 100644 tommyds/tommyds/tommytrie.h delete mode 100644 tommyds/tommyds/tommytrieinp.c delete mode 100644 tommyds/tommyds/tommytrieinp.h delete mode 100644 tommyds/tommyds/tommytypes.h delete mode 100644 tommyds/tommyweb-header.html delete mode 100644 tommyds/tommyweb.doxygen diff --git a/CMakeLists.txt b/CMakeLists.txt index 01e8826..1a1f40f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,16 +3,10 @@ cmake_policy(VERSION 2.8) set(PKGNAME tds) -include_directories(tommyds/tommyds) - add_library(${PKGNAME} MODULE "tds_utils.c" "tds_elem.c" "tds_hash.c" - "tommyds/tommyds/tommyhashlin.c" - "tommyds/tommyds/tommylist.c" - "tommyds/tommyds/tommyhash.c" - "tommyds/tommyds/tommyarrayof.c" ) install(TARGETS ${PKGNAME} LIBRARY diff --git a/README.md b/README.md index c213ab9..730f49b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Data structures which do not rely on Lua memory allocator, nor being limited by Lua garbage collector. -Under the hood, this is a LuaJIT FFI interface to Tommy DS. +Under the hood, this is a LuaJIT FFI interface to [klib](http://attractivechaos.github.io/klib). ## Example diff --git a/cdefs.lua b/cdefs.lua index 29598b4..1bdeace 100644 --- a/cdefs.lua +++ b/cdefs.lua @@ -2,9 +2,13 @@ local ffi = require 'ffi' ffi.cdef[[ +/* elem */ +typedef void (*tds_elem_pointer_free_ptrfunc)(void*); typedef struct tds_elem_ tds_elem; - +tds_elem *tds_elem_new(void); +void tds_elem_free(tds_elem *elem); uint32_t tds_elem_hashkey(tds_elem *elem); +int tds_elem_isequal(tds_elem *elem1, tds_elem *elem2); void tds_elem_set_number(tds_elem *elem, double num); void tds_elem_set_string(tds_elem *elem, const char *str, size_t size); void tds_elem_set_pointer(tds_elem *elem, void *ptr, void (*free)(void*)); @@ -12,34 +16,24 @@ double tds_elem_get_number(tds_elem *elem); const char* tds_elem_get_string(tds_elem *elem); size_t tds_elem_get_string_size(tds_elem *elem); void* tds_elem_get_pointer(tds_elem *elem); -typedef void (*tds_elem_pointer_free_ptrfunc)(void*); tds_elem_pointer_free_ptrfunc tds_elem_get_pointer_free(tds_elem *elem); char tds_elem_type(tds_elem *elem); void tds_elem_free_content(tds_elem *elem); -typedef struct tds_hash_object_ tds_hash_object; -tds_hash_object *tds_hash_object_new(void); -tds_elem* tds_hash_object_key(tds_hash_object *obj); -tds_elem* tds_hash_object_value(tds_hash_object *obj); -void tds_hash_object_free(tds_hash_object *obj); - +/* hash */ typedef struct tds_hash_ tds_hash; tds_hash* tds_hash_new(); -void tds_hash_insert(tds_hash *hash, tds_hash_object *obj); -tds_hash_object* tds_hash_search_string(tds_hash *hash, const char *str, long size); -tds_hash_object* tds_hash_remove_string(tds_hash *hash, const char *str, long size); -tds_hash_object* tds_hash_search_number(tds_hash *hash, double number); -tds_hash_object* tds_hash_remove_number(tds_hash *hash, double number); -tds_hash_object* tds_hash_search_pointer(tds_hash *hash, void* ptr); -tds_hash_object* tds_hash_remove_pointer(tds_hash *hash, void* ptr); unsigned long tds_hash_size(tds_hash *hash); -void tds_hash_remove(tds_hash *hash, tds_hash_object *obj); +void tds_hash_insert(tds_hash *hash, tds_elem *key, tds_elem *val); +int tds_hash_search(tds_hash *hash, tds_elem *key, tds_elem *val); +int tds_hash_remove(tds_hash *hash, tds_elem *key); void tds_hash_retain(tds_hash *hash); void tds_hash_free(tds_hash* hash); +/* iterator */ typedef struct tds_hash_iterator_ tds_hash_iterator; tds_hash_iterator* tds_hash_iterator_new(tds_hash* hash); -tds_hash_object* tds_hash_iterator_next(tds_hash_iterator* iterator); +int tds_hash_iterator_next(tds_hash_iterator* iterator, tds_elem *key, tds_elem *val); void tds_hash_iterator_free(tds_hash_iterator* iterator); ]] diff --git a/hash.lua b/hash.lua index ec2f37e..51c5cef 100644 --- a/hash.lua +++ b/hash.lua @@ -3,6 +3,12 @@ local tds = require 'tds.env' local elem = require 'tds.elem' local C = tds.C +-- hash-independent temporary buffers +local key__ = C.tds_elem_new() +local val__ = C.tds_elem_new() +ffi.gc(key__, C.tds_elem_free) +ffi.gc(val__, C.tds_elem_free) + local hash = {} local NULL = not jit and ffi.C.NULL or nil @@ -16,49 +22,25 @@ function hash.__new() return self end -local function findkey(self, lkey) - local obj - if type(lkey) == 'string' then - obj = C.tds_hash_search_string(self, lkey, #lkey) - elseif type(lkey) == 'number' then - obj = C.tds_hash_search_number(self, lkey) - else - error('string or number key expected') - end - return obj -end - function hash:__newindex(lkey, lval) assert(self) - local obj = findkey(self, lkey) - if obj ~= NULL then - if lval then - local val = C.tds_hash_object_value(obj) - C.tds_elem_free_content(val) - elem.set(val, lval) - else - C.tds_hash_remove(self, obj) - C.tds_hash_object_free(obj) - end - else - if lval then - local obj = C.tds_hash_object_new() - local key = C.tds_hash_object_key(obj) - local val = C.tds_hash_object_value(obj) - elem.set(val, lval) - elem.set(key, lkey) - C.tds_hash_insert(self, obj) - end + assert(lkey, 'hash index is nil') + elem.set(key__, lkey) + if lval then + elem.set(val__, lval) end + C.tds_hash_insert(self, key__, lval and val__ or NULL) end function hash:__index(lkey) + local lval assert(self) - local obj = findkey(self, lkey) - if obj ~= NULL then - local val = elem.get(C.tds_hash_object_value(obj)) - return val + assert(lkey, 'hash index is nil') + elem.set(key__, lkey) + if C.tds_hash_search(self, key__, val__) == 0 then + lval = elem.get(val__) end + return lval end function hash:__len() @@ -70,13 +52,11 @@ function hash:__pairs() assert(self) local iterator = C.tds_hash_iterator_new(self) ffi.gc(iterator, C.tds_hash_iterator_free) - return function() - local obj = C.tds_hash_iterator_next(iterator) - if obj ~= NULL then - local key = elem.get(C.tds_hash_object_key(obj)) - local val = elem.get(C.tds_hash_object_value(obj)) - return key, val + if C.tds_hash_iterator_next(iterator, key__, val__) == 0 then + local lkey = elem.get(key__) + local lval = elem.get(val__) + return lkey, lval end end end diff --git a/klib/khash.h b/klib/khash.h new file mode 100644 index 0000000..06fc7a3 --- /dev/null +++ b/klib/khash.h @@ -0,0 +1,627 @@ +/* The MIT License + + Copyright (c) 2008, 2009, 2011 by Attractive Chaos + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +/* + An example: + +#include "khash.h" +KHASH_MAP_INIT_INT(32, char) +int main() { + int ret, is_missing; + khiter_t k; + khash_t(32) *h = kh_init(32); + k = kh_put(32, h, 5, &ret); + kh_value(h, k) = 10; + k = kh_get(32, h, 10); + is_missing = (k == kh_end(h)); + k = kh_get(32, h, 5); + kh_del(32, h, k); + for (k = kh_begin(h); k != kh_end(h); ++k) + if (kh_exist(h, k)) kh_value(h, k) = 1; + kh_destroy(32, h); + return 0; +} +*/ + +/* + 2013-05-02 (0.2.8): + + * Use quadratic probing. When the capacity is power of 2, stepping function + i*(i+1)/2 guarantees to traverse each bucket. It is better than double + hashing on cache performance and is more robust than linear probing. + + In theory, double hashing should be more robust than quadratic probing. + However, my implementation is probably not for large hash tables, because + the second hash function is closely tied to the first hash function, + which reduce the effectiveness of double hashing. + + Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php + + 2011-12-29 (0.2.7): + + * Minor code clean up; no actual effect. + + 2011-09-16 (0.2.6): + + * The capacity is a power of 2. This seems to dramatically improve the + speed for simple keys. Thank Zilong Tan for the suggestion. Reference: + + - http://code.google.com/p/ulib/ + - http://nothings.org/computer/judy/ + + * Allow to optionally use linear probing which usually has better + performance for random input. Double hashing is still the default as it + is more robust to certain non-random input. + + * Added Wang's integer hash function (not used by default). This hash + function is more robust to certain non-random input. + + 2011-02-14 (0.2.5): + + * Allow to declare global functions. + + 2009-09-26 (0.2.4): + + * Improve portability + + 2008-09-19 (0.2.3): + + * Corrected the example + * Improved interfaces + + 2008-09-11 (0.2.2): + + * Improved speed a little in kh_put() + + 2008-09-10 (0.2.1): + + * Added kh_clear() + * Fixed a compiling error + + 2008-09-02 (0.2.0): + + * Changed to token concatenation which increases flexibility. + + 2008-08-31 (0.1.2): + + * Fixed a bug in kh_get(), which has not been tested previously. + + 2008-08-31 (0.1.1): + + * Added destructor +*/ + + +#ifndef __AC_KHASH_H +#define __AC_KHASH_H + +/*! + @header + + Generic hash table library. + */ + +#define AC_VERSION_KHASH_H "0.2.8" + +#include +#include +#include + +/* compiler specific configuration */ + +#if UINT_MAX == 0xffffffffu +typedef unsigned int khint32_t; +#elif ULONG_MAX == 0xffffffffu +typedef unsigned long khint32_t; +#endif + +#if ULONG_MAX == ULLONG_MAX +typedef unsigned long khint64_t; +#else +typedef unsigned long long khint64_t; +#endif + +#ifndef kh_inline +#ifdef _MSC_VER +#define kh_inline __inline +#else +#define kh_inline inline +#endif +#endif /* kh_inline */ + +#ifndef klib_unused +#if (defined __clang__ && __clang_major__ >= 3) || (defined __GNUC__ && __GNUC__ >= 3) +#define klib_unused __attribute__ ((__unused__)) +#else +#define klib_unused +#endif +#endif /* klib_unused */ + +typedef khint32_t khint_t; +typedef khint_t khiter_t; + +#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2) +#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1) +#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3) +#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1))) +#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1))) +#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1))) +#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1)) + +#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4) + +#ifndef kroundup32 +#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) +#endif + +#ifndef kcalloc +#define kcalloc(N,Z) calloc(N,Z) +#endif +#ifndef kmalloc +#define kmalloc(Z) malloc(Z) +#endif +#ifndef krealloc +#define krealloc(P,Z) realloc(P,Z) +#endif +#ifndef kfree +#define kfree(P) free(P) +#endif + +static const double __ac_HASH_UPPER = 0.77; + +#define __KHASH_TYPE(name, khkey_t, khval_t) \ + typedef struct kh_##name##_s { \ + khint_t n_buckets, size, n_occupied, upper_bound; \ + khint32_t *flags; \ + khkey_t *keys; \ + khval_t *vals; \ + } kh_##name##_t; + +#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \ + extern kh_##name##_t *kh_init_##name(void); \ + extern void kh_destroy_##name(kh_##name##_t *h); \ + extern void kh_clear_##name(kh_##name##_t *h); \ + extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \ + extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \ + extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \ + extern void kh_del_##name(kh_##name##_t *h, khint_t x); + +#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + SCOPE kh_##name##_t *kh_init_##name(void) { \ + return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \ + } \ + SCOPE void kh_destroy_##name(kh_##name##_t *h) \ + { \ + if (h) { \ + kfree((void *)h->keys); kfree(h->flags); \ + kfree((void *)h->vals); \ + kfree(h); \ + } \ + } \ + SCOPE void kh_clear_##name(kh_##name##_t *h) \ + { \ + if (h && h->flags) { \ + memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \ + h->size = h->n_occupied = 0; \ + } \ + } \ + SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \ + { \ + if (h->n_buckets) { \ + khint_t k, i, last, mask, step = 0; \ + mask = h->n_buckets - 1; \ + k = __hash_func(key); i = k & mask; \ + last = i; \ + while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ + i = (i + (++step)) & mask; \ + if (i == last) return h->n_buckets; \ + } \ + return __ac_iseither(h->flags, i)? h->n_buckets : i; \ + } else return 0; \ + } \ + SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \ + { /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \ + khint32_t *new_flags = 0; \ + khint_t j = 1; \ + { \ + kroundup32(new_n_buckets); \ + if (new_n_buckets < 4) new_n_buckets = 4; \ + if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \ + else { /* hash table size to be changed (shrink or expand); rehash */ \ + new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \ + if (!new_flags) return -1; \ + memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \ + if (h->n_buckets < new_n_buckets) { /* expand */ \ + khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \ + if (!new_keys) { kfree(new_flags); return -1; } \ + h->keys = new_keys; \ + if (kh_is_map) { \ + khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \ + if (!new_vals) { kfree(new_flags); return -1; } \ + h->vals = new_vals; \ + } \ + } /* otherwise shrink */ \ + } \ + } \ + if (j) { /* rehashing is needed */ \ + for (j = 0; j != h->n_buckets; ++j) { \ + if (__ac_iseither(h->flags, j) == 0) { \ + khkey_t key = h->keys[j]; \ + khval_t val; \ + khint_t new_mask; \ + new_mask = new_n_buckets - 1; \ + if (kh_is_map) val = h->vals[j]; \ + __ac_set_isdel_true(h->flags, j); \ + while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \ + khint_t k, i, step = 0; \ + k = __hash_func(key); \ + i = k & new_mask; \ + while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \ + __ac_set_isempty_false(new_flags, i); \ + if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \ + { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \ + if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \ + __ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \ + } else { /* write the element and jump out of the loop */ \ + h->keys[i] = key; \ + if (kh_is_map) h->vals[i] = val; \ + break; \ + } \ + } \ + } \ + } \ + if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \ + h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \ + if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \ + } \ + kfree(h->flags); /* free the working space */ \ + h->flags = new_flags; \ + h->n_buckets = new_n_buckets; \ + h->n_occupied = h->size; \ + h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \ + } \ + return 0; \ + } \ + SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \ + { \ + khint_t x; \ + if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \ + if (h->n_buckets > (h->size<<1)) { \ + if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \ + *ret = -1; return h->n_buckets; \ + } \ + } else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \ + *ret = -1; return h->n_buckets; \ + } \ + } /* TODO: to implement automatically shrinking; resize() already support shrinking */ \ + { \ + khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \ + x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \ + if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \ + else { \ + last = i; \ + while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ + if (__ac_isdel(h->flags, i)) site = i; \ + i = (i + (++step)) & mask; \ + if (i == last) { x = site; break; } \ + } \ + if (x == h->n_buckets) { \ + if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \ + else x = i; \ + } \ + } \ + } \ + if (__ac_isempty(h->flags, x)) { /* not present at all */ \ + h->keys[x] = key; \ + __ac_set_isboth_false(h->flags, x); \ + ++h->size; ++h->n_occupied; \ + *ret = 1; \ + } else if (__ac_isdel(h->flags, x)) { /* deleted */ \ + h->keys[x] = key; \ + __ac_set_isboth_false(h->flags, x); \ + ++h->size; \ + *ret = 2; \ + } else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \ + return x; \ + } \ + SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \ + { \ + if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \ + __ac_set_isdel_true(h->flags, x); \ + --h->size; \ + } \ + } + +#define KHASH_DECLARE(name, khkey_t, khval_t) \ + __KHASH_TYPE(name, khkey_t, khval_t) \ + __KHASH_PROTOTYPES(name, khkey_t, khval_t) + +#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + __KHASH_TYPE(name, khkey_t, khval_t) \ + __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) + +#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + KHASH_INIT2(name, static kh_inline klib_unused, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) + +/* --- BEGIN OF HASH FUNCTIONS --- */ + +/*! @function + @abstract Integer hash function + @param key The integer [khint32_t] + @return The hash value [khint_t] + */ +#define kh_int_hash_func(key) (khint32_t)(key) +/*! @function + @abstract Integer comparison function + */ +#define kh_int_hash_equal(a, b) ((a) == (b)) +/*! @function + @abstract 64-bit integer hash function + @param key The integer [khint64_t] + @return The hash value [khint_t] + */ +#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11) +/*! @function + @abstract 64-bit integer comparison function + */ +#define kh_int64_hash_equal(a, b) ((a) == (b)) +/*! @function + @abstract const char* hash function + @param s Pointer to a null terminated string + @return The hash value + */ +static kh_inline khint_t __ac_X31_hash_string(const char *s) +{ + khint_t h = (khint_t)*s; + if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s; + return h; +} +/*! @function + @abstract Another interface to const char* hash function + @param key Pointer to a null terminated string [const char*] + @return The hash value [khint_t] + */ +#define kh_str_hash_func(key) __ac_X31_hash_string(key) +/*! @function + @abstract Const char* comparison function + */ +#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0) + +static kh_inline khint_t __ac_Wang_hash(khint_t key) +{ + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + return key; +} +#define kh_int_hash_func2(k) __ac_Wang_hash((khint_t)key) + +/* --- END OF HASH FUNCTIONS --- */ + +/* Other convenient macros... */ + +/*! + @abstract Type of the hash table. + @param name Name of the hash table [symbol] + */ +#define khash_t(name) kh_##name##_t + +/*! @function + @abstract Initiate a hash table. + @param name Name of the hash table [symbol] + @return Pointer to the hash table [khash_t(name)*] + */ +#define kh_init(name) kh_init_##name() + +/*! @function + @abstract Destroy a hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + */ +#define kh_destroy(name, h) kh_destroy_##name(h) + +/*! @function + @abstract Reset a hash table without deallocating memory. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + */ +#define kh_clear(name, h) kh_clear_##name(h) + +/*! @function + @abstract Resize a hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param s New size [khint_t] + */ +#define kh_resize(name, h, s) kh_resize_##name(h, s) + +/*! @function + @abstract Insert a key to the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Key [type of keys] + @param r Extra return code: -1 if the operation failed; + 0 if the key is present in the hash table; + 1 if the bucket is empty (never used); 2 if the element in + the bucket has been deleted [int*] + @return Iterator to the inserted element [khint_t] + */ +#define kh_put(name, h, k, r) kh_put_##name(h, k, r) + +/*! @function + @abstract Retrieve a key from the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Key [type of keys] + @return Iterator to the found element, or kh_end(h) if the element is absent [khint_t] + */ +#define kh_get(name, h, k) kh_get_##name(h, k) + +/*! @function + @abstract Remove a key from the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Iterator to the element to be deleted [khint_t] + */ +#define kh_del(name, h, k) kh_del_##name(h, k) + +/*! @function + @abstract Test whether a bucket contains data. + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return 1 if containing data; 0 otherwise [int] + */ +#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x))) + +/*! @function + @abstract Get key given an iterator + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return Key [type of keys] + */ +#define kh_key(h, x) ((h)->keys[x]) + +/*! @function + @abstract Get value given an iterator + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return Value [type of values] + @discussion For hash sets, calling this results in segfault. + */ +#define kh_val(h, x) ((h)->vals[x]) + +/*! @function + @abstract Alias of kh_val() + */ +#define kh_value(h, x) ((h)->vals[x]) + +/*! @function + @abstract Get the start iterator + @param h Pointer to the hash table [khash_t(name)*] + @return The start iterator [khint_t] + */ +#define kh_begin(h) (khint_t)(0) + +/*! @function + @abstract Get the end iterator + @param h Pointer to the hash table [khash_t(name)*] + @return The end iterator [khint_t] + */ +#define kh_end(h) ((h)->n_buckets) + +/*! @function + @abstract Get the number of elements in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @return Number of elements in the hash table [khint_t] + */ +#define kh_size(h) ((h)->size) + +/*! @function + @abstract Get the number of buckets in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @return Number of buckets in the hash table [khint_t] + */ +#define kh_n_buckets(h) ((h)->n_buckets) + +/*! @function + @abstract Iterate over the entries in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @param kvar Variable to which key will be assigned + @param vvar Variable to which value will be assigned + @param code Block of code to execute + */ +#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \ + for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ + if (!kh_exist(h,__i)) continue; \ + (kvar) = kh_key(h,__i); \ + (vvar) = kh_val(h,__i); \ + code; \ + } } + +/*! @function + @abstract Iterate over the values in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @param vvar Variable to which value will be assigned + @param code Block of code to execute + */ +#define kh_foreach_value(h, vvar, code) { khint_t __i; \ + for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ + if (!kh_exist(h,__i)) continue; \ + (vvar) = kh_val(h,__i); \ + code; \ + } } + +/* More conenient interfaces */ + +/*! @function + @abstract Instantiate a hash set containing integer keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_INT(name) \ + KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing integer keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_INT(name, khval_t) \ + KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing 64-bit integer keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_INT64(name) \ + KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing 64-bit integer keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_INT64(name, khval_t) \ + KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal) + +typedef const char *kh_cstr_t; +/*! @function + @abstract Instantiate a hash map containing const char* keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_STR(name) \ + KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing const char* keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_STR(name, khval_t) \ + KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal) + +#endif /* __AC_KHASH_H */ diff --git a/tds_elem.c b/tds_elem.c index 040476b..27c7ab2 100644 --- a/tds_elem.c +++ b/tds_elem.c @@ -1,21 +1,98 @@ #include -#include "tommy.h" +#include + #include "tds_utils.h" #include "tds_elem.h" +/* + * Fowler/Noll/Vo hash + * + * The basis of this hash algorithm was taken from an idea sent + * as reviewer comments to the IEEE POSIX P1003.2 committee by: + * + * Phong Vo (http://www.research.att.com/info/kpv/) + * Glenn Fowler (http://www.research.att.com/~gsf/) + * + * In a subsequent ballot round: + * + * Landon Curt Noll (http://www.isthe.com/chongo/) + * + * improved on their algorithm. Some people tried this hash + * and found that it worked rather well. In an EMail message + * to Landon, they named it the `Fowler/Noll/Vo'' or FNV hash. + * + * FNV hashes are designed to be fast while maintaining a low + * collision rate. The FNV speed allows one to quickly hash lots + * of data while maintaining a reasonable collision rate. See: + * + * http://www.isthe.com/chongo/tech/comp/fnv/index.html + * + * for more details as well as other forms of the FNV hash. + */ +static uint32_t fnv32_buf(void *buf, size_t len, uint32_t hval) +{ + unsigned char *bp = (unsigned char *)buf;/* start of buffer */ + unsigned char *be = bp + len;/* beyond end of buffer */ + + /* + * FNV-1 hash each octet in the buffer + */ + while (bp < be) { + + /* multiply by the 32 bit FNV magic prime mod 2^32 */ + hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24); + + /* xor the bottom with the current octet */ + hval ^= (uint32_t)*bp++; + } + + /* return our new hash value */ + return hval; +} + + +tds_elem *tds_elem_new(void) +{ + return tds_malloc(sizeof(tds_elem)); +} + +void tds_elem_free(tds_elem *elem) +{ + tds_free(elem); +} + uint32_t tds_elem_hashkey(tds_elem *elem) { switch(elem->type) { case 'n': - return tommy_hash_u32(0, &elem->value.num, sizeof(double)); + return fnv32_buf(&elem->value.num, sizeof(double), 0); case 's': - return tommy_hash_u32(0, elem->value.str.data, elem->value.str.size); + return fnv32_buf(elem->value.str.data, elem->value.str.size, 0); case 'p': - return tommy_hash_u32(0, elem->value.ptr.data, sizeof(void*)); + return fnv32_buf(elem->value.ptr.data, sizeof(void*), 0); } return 0; } +int tds_elem_isequal(tds_elem *elem1, tds_elem *elem2) +{ + if(elem1->type != elem2->type) + return 0; + switch(elem1->type) { + case 'n': + return elem1->value.num == elem2->value.num; + case 's': + if(elem1->value.str.size != elem2->value.str.size) + return 0; + return !memcmp(elem1->value.str.data, elem2->value.str.data, elem1->value.str.size); + case 'p': + return elem1->value.ptr.data == elem2->value.ptr.data; + default: + printf("[tds hash] internal error: unknown type\n"); + return 0; + } +} + void tds_elem_set_number(tds_elem *elem, double num) { elem->type = 'n'; diff --git a/tds_elem.h b/tds_elem.h index 58110d2..51b4959 100644 --- a/tds_elem.h +++ b/tds_elem.h @@ -26,7 +26,10 @@ typedef struct tds_elem_ { } tds_elem; +tds_elem *tds_elem_new(void); +void tds_elem_free(tds_elem *elem); uint32_t tds_elem_hashkey(tds_elem *elem); +int tds_elem_isequal(tds_elem *elem1, tds_elem *elem2); void tds_elem_set_number(tds_elem *elem, double num); void tds_elem_set_string(tds_elem *elem, const char *str, size_t size); void tds_elem_set_pointer(tds_elem *elem, void *ptr, void (*free)(void*)); diff --git a/tds_hash.c b/tds_hash.c index 62a85e3..1f08a87 100644 --- a/tds_hash.c +++ b/tds_hash.c @@ -1,153 +1,88 @@ #include #include -#include "tommy.h" + +#include "klib/khash.h" #include "tds_utils.h" +#include "tds_elem.h" #include "tds_hash.h" -/* hash object */ -struct tds_hash_object_ { - tommy_node hash_node; - - tds_elem key; - tds_elem val; - -}; - -tds_hash_object *tds_hash_object_new(void) +static uint32_t tds_elem_hashkey_(tds_elem elem) { - tds_hash_object *obj = tds_malloc(sizeof(tds_hash_object)); - obj->key.type = 0; - obj->val.type = 0; - return obj; + return tds_elem_hashkey(&elem); } -tds_elem* tds_hash_object_key(tds_hash_object *obj) +static int tds_elem_isequal_(tds_elem elem1, tds_elem elem2) { - return &obj->key; + return tds_elem_isequal(&elem1, &elem2); } -tds_elem* tds_hash_object_value(tds_hash_object *obj) -{ - return &obj->val; -} - -void tds_hash_object_free(tds_hash_object *obj) -{ - tds_elem_free_content(&obj->key); - tds_elem_free_content(&obj->val); - tds_free(obj); -} +KHASH_INIT(tds_elem, tds_elem, tds_elem, 1, tds_elem_hashkey_, tds_elem_isequal_); /* hash structure */ -/* i keep it like that for now, - thinking about optimizing allocations - of elements/nodes */ struct tds_hash_ { - tommy_hashlin *hash; + khash_t(tds_elem) *hash; long refcount; }; -tds_hash* tds_hash_new() +tds_hash* tds_hash_new(void) { tds_hash *hash = tds_malloc(sizeof(tds_hash)); if(hash) { - hash->hash = tds_malloc(sizeof(tommy_hashlin)); + hash->hash = kh_init(tds_elem); if(!hash->hash) { free(hash); return NULL; } - tommy_hashlin_init(hash->hash); hash->refcount = 1; } return hash; } -void tds_hash_insert(tds_hash *hash, tds_hash_object *obj) +void tds_hash_insert(tds_hash *hash, tds_elem *key, tds_elem *val) { - tommy_hashlin_insert(hash->hash, &obj->hash_node, obj, tds_elem_hashkey(&obj->key)); + if(!val) + tds_hash_remove(hash, key); + else { + int ret; + khiter_t k = kh_put(tds_elem, hash->hash, *key, &ret); + if(ret == 0) { /* key present */ + tds_elem_free_content(key); /* as it has not been used */ + tds_elem_free_content(&kh_val(hash->hash, k)); /* as it will be overwritten */ + } + kh_val(hash->hash, k) = *val; + } } -static int tds_hash_search_string_callback(const void *arg_, const void *obj_) +int tds_hash_search(tds_hash *hash, tds_elem *key, tds_elem *val) { - const tds_elem *astr = arg_; /* must be a string */ - tds_hash_object *obj = (tds_hash_object*)obj_; - long size = astr->value.str.size; - - if(obj->key.type != 's') + khiter_t k = kh_get(tds_elem, hash->hash, *key); + tds_elem_free_content(key); /* your memory belongs to us */ + if(k == kh_end(hash->hash)) return 1; - - if(size != obj->key.value.str.size) - return 1; - - return memcmp(astr->value.str.data, obj->key.value.str.data, size); + *val = kh_val(hash->hash, k); + return 0; } -tds_hash_object* tds_hash_search_string(tds_hash *hash, const char *str, long size) +int tds_hash_remove(tds_hash *hash, tds_elem *key_) { - tds_elem tstr; - tstr.type = 's'; - tstr.value.str.data = (char*)str; /* i know what i am doing */ - tstr.value.str.size = size; - return tommy_hashlin_search(hash->hash, tds_hash_search_string_callback, &tstr, tommy_hash_u32(0, str, size)); -} - -tds_hash_object* tds_hash_remove_string(tds_hash *hash, const char *str, long size) -{ - tds_elem tstr; - tds_elem_set_string(&tstr, str, size); - return tommy_hashlin_remove(hash->hash, tds_hash_search_string_callback, &tstr, tommy_hash_u32(0, str, size)); -} - -static int tds_hash_search_number_callback(const void *arg_, const void *obj_) -{ - double anumber = *((double*)arg_); - tds_hash_object *obj = (tds_hash_object*)obj_; - - if(obj->key.type != 'n') + tds_elem *key = NULL; + tds_elem *val = NULL; + khiter_t k = kh_get(tds_elem, hash->hash, *key_); + tds_elem_free_content(key_); /* your memory belongs to us */ + if(k == kh_end(hash->hash)) return 1; - - return anumber != obj->key.value.num; -} - -tds_hash_object* tds_hash_search_number(tds_hash *hash, double number) -{ - return tommy_hashlin_search(hash->hash, tds_hash_search_number_callback, &number, tommy_hash_u32(0, &number, sizeof(double))); -} - -tds_hash_object* tds_hash_remove_number(tds_hash *hash, double number) -{ - return tommy_hashlin_remove(hash->hash, tds_hash_search_number_callback, &number, tommy_hash_u32(0, &number, sizeof(double))); -} - -static int tds_hash_search_pointer_callback(const void *arg, const void *obj_) -{ - tds_hash_object *obj = (tds_hash_object*)obj_; - - if(obj->key.type != 'p') - return 1; - - return arg != obj->key.value.ptr.data; -} - -tds_hash_object* tds_hash_search_pointer(tds_hash *hash, void* ptr) -{ - return tommy_hashlin_search(hash->hash, tds_hash_search_pointer_callback, ptr, tommy_hash_u32(0, &ptr, sizeof(void*))); -} - -tds_hash_object* tds_hash_remove_pointer(tds_hash *hash, void* ptr) -{ - return tommy_hashlin_remove(hash->hash, tds_hash_search_pointer_callback, ptr, tommy_hash_u32(0, &ptr, sizeof(void*))); + key = &kh_key(hash->hash, k); + val = &kh_val(hash->hash, k); + tds_elem_free_content(key); /* your memory belongs to us */ + tds_elem_free_content(val); /* your memory belongs to us */ + kh_del(tds_elem, hash->hash, k); + return 0; } unsigned long tds_hash_size(tds_hash *hash) { - return tommy_hashlin_count(hash->hash); -} - -void tds_hash_remove(tds_hash *hash, tds_hash_object *obj) -{ - tommy_hashlin_remove_existing(hash->hash, &obj->hash_node); + return kh_size(hash->hash); } void tds_hash_retain(tds_hash *hash) @@ -160,9 +95,14 @@ void tds_hash_free(tds_hash* hash) hash->refcount--; if(hash->refcount == 0) { - tommy_hashlin_foreach(hash->hash, (void(*)(void*))tds_hash_object_free); - tommy_hashlin_done(hash->hash); - tds_free(hash->hash); + khint_t k; + for(k = kh_begin(hash->hash); k != kh_end(hash->hash); ++k) { + if (kh_exist(hash->hash, k)) { + tds_elem_free_content(&kh_key(hash->hash, k)); /* your memory belongs to us */ + tds_elem_free_content(&kh_val(hash->hash, k)); /* your memory belongs to us */ + } + } + kh_destroy(tds_elem, hash->hash); tds_free(hash); } else if(hash->refcount < 0) @@ -170,76 +110,43 @@ void tds_hash_free(tds_hash* hash) } /* iterator */ -struct tds_hash_iterator_node { - tommy_node node; - tds_hash_object *obj; -}; - struct tds_hash_iterator_ { tds_hash *hash; - tommy_arrayof *array; - size_t index; - size_t size; + khint_t k; }; -static void tds_add_hash_node_to_list(void *iterator_, void *obj_) -{ - tds_hash_iterator *iterator = (tds_hash_iterator*)iterator_; - struct tds_hash_iterator_node *node = tommy_arrayof_ref(iterator->array, iterator->index); - tds_hash_object *obj = (tds_hash_object*)obj_; - node->obj = obj; - iterator->index++; -} - tds_hash_iterator* tds_hash_iterator_new(tds_hash* hash) { - size_t size = tds_hash_size(hash); - tommy_arrayof *array; - tds_hash_iterator *iterator; - - /* init a big array */ - iterator = tds_malloc(sizeof(tds_hash_iterator)); + tds_hash_iterator *iterator = tds_malloc(sizeof(tds_hash_iterator)); if(!iterator) return NULL; - - array = tds_malloc(sizeof(tommy_arrayof)); - if(!array) { - tds_free(iterator); - return NULL; - } - tommy_arrayof_init(array, sizeof(struct tds_hash_iterator_node)); - tommy_arrayof_grow(array, size); - - /* fill it up with hash nodes */ - iterator->array = array; - iterator->index = 0; - iterator->size = size; - tommy_hashlin_foreach_arg(hash->hash, tds_add_hash_node_to_list, iterator); - - /* reset iterator */ - iterator->index = 0; - - /* retain hash */ + iterator->k = kh_begin(hash->hash)-1; iterator->hash = hash; tds_hash_retain(hash); - return iterator; } -tds_hash_object* tds_hash_iterator_next(tds_hash_iterator* iterator) +int tds_hash_iterator_next(tds_hash_iterator* iterator, tds_elem *key, tds_elem *val) { - if(iterator->index >= iterator->size) - return NULL; - else { - iterator->index++; - return ((struct tds_hash_iterator_node*)tommy_arrayof_ref(iterator->array, iterator->index-1))->obj; - } + tds_hash *hash = iterator->hash; + + if(iterator->k == kh_end(hash->hash)) + return 1; + + do { + iterator->k++; + } while((iterator->k != kh_end(hash->hash)) && !kh_exist(hash->hash, iterator->k)); + + if(iterator->k == kh_end(hash->hash)) + return 1; + + *key = kh_key(hash->hash, iterator->k); + *val = kh_val(hash->hash, iterator->k); + return 0; } void tds_hash_iterator_free(tds_hash_iterator* iterator) { - tommy_arrayof_done(iterator->array); - tds_free(iterator->array); tds_hash_free(iterator->hash); tds_free(iterator); } diff --git a/tds_hash.h b/tds_hash.h index 9e94e96..e28d87f 100644 --- a/tds_hash.h +++ b/tds_hash.h @@ -3,33 +3,22 @@ #include "tds_elem.h" -/* hash object */ -typedef struct tds_hash_object_ tds_hash_object; - -tds_hash_object *tds_hash_object_new(void); -tds_elem* tds_hash_object_key(tds_hash_object *obj); -tds_elem* tds_hash_object_value(tds_hash_object *obj); -void tds_hash_object_free(tds_hash_object *obj); +/* note: elem *contents* (key or val) given to hash belongs to hash */ /* hash */ typedef struct tds_hash_ tds_hash; -tds_hash* tds_hash_new(); -void tds_hash_insert(tds_hash *hash, tds_hash_object *obj); -tds_hash_object* tds_hash_search_string(tds_hash *hash, const char *str, long size); -tds_hash_object* tds_hash_remove_string(tds_hash *hash, const char *str, long size); -tds_hash_object* tds_hash_search_number(tds_hash *hash, double number); -tds_hash_object* tds_hash_remove_number(tds_hash *hash, double number); -tds_hash_object* tds_hash_search_pointer(tds_hash *hash, void* ptr); -tds_hash_object* tds_hash_remove_pointer(tds_hash *hash, void* ptr); +tds_hash* tds_hash_new(void); unsigned long tds_hash_size(tds_hash *hash); -void tds_hash_remove(tds_hash *hash, tds_hash_object *obj); +void tds_hash_insert(tds_hash *hash, tds_elem *key, tds_elem *val); +int tds_hash_search(tds_hash *hash, tds_elem *key, tds_elem *val); +int tds_hash_remove(tds_hash *hash, tds_elem *key); void tds_hash_retain(tds_hash *hash); void tds_hash_free(tds_hash* hash); /* iterator */ typedef struct tds_hash_iterator_ tds_hash_iterator; tds_hash_iterator* tds_hash_iterator_new(tds_hash* hash); -tds_hash_object* tds_hash_iterator_next(tds_hash_iterator* iterator); +int tds_hash_iterator_next(tds_hash_iterator* iterator, tds_elem *key, tds_elem *val); void tds_hash_iterator_free(tds_hash_iterator* iterator); #endif diff --git a/tommyds/.gitignore b/tommyds/.gitignore deleted file mode 100644 index b8781f5..0000000 --- a/tommyds/.gitignore +++ /dev/null @@ -1,49 +0,0 @@ -# archives -*.zip -*.tar.gz - -# backups -*~ - -# autotools -Makefile -Makefile.in -aclocal.m4 -autom4te.cache/ -config.guess -config.h -config.h.in -config.log -config.status -config.sub -configure -install-sh -missing -stamp-h1 - -# objects -*.o -*.s -*.S - -# coverage -*.gcda -*.gcno -*.info -cov/ - -# project -*.dst -*.epr - -# specific -tommycheck -tommybench -contrib/ -archive/ -*.ttf -doc/ -benchmark/data/ -benchmark/exec/ - - diff --git a/tommyds/.travis.yml b/tommyds/.travis.yml deleted file mode 100644 index 32fa9b0..0000000 --- a/tommyds/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -# Travis CI configuration file - -language: c - -script: make check - -compiler: - - clang - - gcc - diff --git a/tommyds/AUTHORS b/tommyds/AUTHORS deleted file mode 100644 index 73bd705..0000000 --- a/tommyds/AUTHORS +++ /dev/null @@ -1,9 +0,0 @@ -TommyDS AUTHORS -=============== - -The author of TommyDS is Andrea Mazzoleni. - -You can contact me sending an email at: - - amadvance@users.sourceforge.net - diff --git a/tommyds/HISTORY b/tommyds/HISTORY deleted file mode 100644 index d5e79c2..0000000 --- a/tommyds/HISTORY +++ /dev/null @@ -1,80 +0,0 @@ -TommyDS HISTORY -=============== - -2.1 2014/12 BETA -================ - -2.0 2014/12 -=========== - * Fixed a Segmentation Fault bug in the trie_inplace container when inserting - duplicate elements. - * Faster array and hashlin implementation when accessing elements. - * Added new hashtable functions to iterate over all the elements. - * Added a new tommy_calloc() function used for allocating initialized memory. - If you redefined tommy_malloc(), likely you have to redefine also tommy_calloc(). - * Reached 100% code coverage in the regression test. - * Different source code organization. - * Added benchmark comparison with Binary Search Tesseract by Gregorius van - den Hoven. - -1.8 2013/12 -=========== - * Fixed build of tommy_arrayblk in C++. - * Changed the default node size of tommy_trie to fit a cache line of 64 bytes. - * Added benchmark comparison with STX BTree. - -1.7 2013/12 -=========== - * Extends tommy_hashlin_done() to work also if the hashtable is not empty. - * Removes the empty tommy_trie_done() because the real deallocation is done - by the allocator. - -1.6 2013/11 -=========== - * Added a new tommy_arrayblk and tommy_arrayblkof types to store elements - in an array minimizing memory occupation. - -1.5 2013/06 -=========== - * Fixed inline declaration to allow building with clang. - * Added a new tommy_arrayof type to store in an array elements of arbitrary - size. - -1.4 2013/03 -=========== - * Added benchmark comparison with Google BTree, and C++ map and unordered_map. - * Benchmark for Linux is now compiled with "-O3 -march=pentium4 -mtune=generic", - and the Windows one with "/Ox /GL /GS- /arch:SSE2". - -1.3 2013/02 -=========== - * Fixed a Segmentation Fault bug in the hashlin container if exact power - of 2 sizes were used. - * Removed some warnings with newer gcc. - * Minor documentation changes. - * Added benchmark comparison with the judy array implementation by Karl Malbrain. - -1.2 2012/05 -=========== - * Minor documentation changes. - * In the check application, added a speed comparison with the C qsort() - implementation. - -1.1 2012/05 -=========== - * Fixed the tommy_hashdyn_remove() function. Now it shrinks the hashtable if required. - * Minor documentation changes. - -1.0 2011/03 -=========== - * First official version of TommyDS. - * Added tommy_list_foreach functions. - -0.2 2011/03 -=========== - * Added tommy_array. A dynamic vector. - -0.1 2011/01 -=========== - * First release of Tommy. - diff --git a/tommyds/INSTALL b/tommyds/INSTALL deleted file mode 100644 index 40e55ff..0000000 --- a/tommyds/INSTALL +++ /dev/null @@ -1,8 +0,0 @@ -TommyDS INSTALL -=============== - -TommyDS doesn't need any installation. - -You have only to import the required .c and .h files into your program -and use the them. - diff --git a/tommyds/LICENSE b/tommyds/LICENSE deleted file mode 100644 index 6c86157..0000000 --- a/tommyds/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. diff --git a/tommyds/Makefile b/tommyds/Makefile deleted file mode 100644 index 5336c6c..0000000 --- a/tommyds/Makefile +++ /dev/null @@ -1,215 +0,0 @@ -############################################################################# -# Tommy Makefile - -# Version of TommyDS -VERSION = 2.1 - -# Build options for the check program -ifdef COVERAGE -CFLAGS = -O0 -g -fprofile-arcs -ftest-coverage -else -CFLAGS = -O3 -march=native -Wall -Wextra -Wshadow -Wcast-qual -g -endif - -# Build options for the benchmark -# -std=gnu++0x required by Google btree -BENCHCXXFLAGS = -m32 -O3 -march=nehalem -fpermissive -std=gnu++0x -Wall -g - -# Programs -CC ?= gcc -CXX ?= g++ -OBJDUMP ?= objdump -UNAME = $(shell uname) - -# Linux -ifeq ($(UNAME),Linux) -LIB=-lrt -BENCHLIB=benchmark/lib/judy/libJudyL.a benchmark/lib/judy/libJudyMalloc.a -EXE= -O=.o -endif - -# Darwin -ifeq ($(UNAME),Darwin) -LIB= -EXE= -O=.o -endif - -# Windows -ifeq ($(UNAME),) -BENCHLIB=benchmark/lib/judy/src/judy.lib -EXE=.exe -O=.obj -endif - -#CHECK = ./tommybench -n 1000000 -d tommy-hashlin -CHECK = ./tommycheck - -DEP = \ - tommyds/tommyalloc.c \ - tommyds/tommyalloc.h \ - tommyds/tommyarray.c \ - tommyds/tommyarray.h \ - tommyds/tommyarrayof.c \ - tommyds/tommyarrayof.h \ - tommyds/tommyarrayblk.c \ - tommyds/tommyarrayblk.h \ - tommyds/tommyarrayblkof.c \ - tommyds/tommyarrayblkof.h \ - tommyds/tommy.c \ - tommyds/tommy.h \ - tommyds/tommyhash.c \ - tommyds/tommyhashdyn.c \ - tommyds/tommyhashdyn.h \ - tommyds/tommyhash.h \ - tommyds/tommyhashlin.c \ - tommyds/tommyhashlin.h \ - tommyds/tommyhashtbl.c \ - tommyds/tommyhashtbl.h \ - tommyds/tommylist.c \ - tommyds/tommylist.h \ - tommyds/tommytrie.c \ - tommyds/tommytrie.h \ - tommyds/tommytrieinp.c \ - tommyds/tommytrieinp.h \ - tommyds/tommytypes.h \ - tommyds/tommychain.h - -DEPTEST = \ - check.c \ - benchmark.cc - -all: tommycheck$(EXE) - -bench: tommybench$(EXE) - -tommy$(O): $(DEP) - $(CC) $(CFLAGS) -c tommyds/tommy.c -o tommy$(O) - $(OBJDUMP) -S tommy$(O) > tommy.s - -tommycheck$(EXE): check.c tommy$(O) - $(CC) $(CFLAGS) check.c tommy$(O) -o tommycheck$(EXE) $(LIB) - -tommybench$(EXE): benchmark.cc $(DEP) - $(CXX) $(BENCHCXXFLAGS) benchmark.cc -o tommybench$(EXE) $(LIB) $(BENCHLIB) - -check: tommycheck$(EXE) - ./tommycheck$(EXE) - echo Check completed with success! - -lcov_reset: - lcov -d . -z - rm -f ./lcov.info - -lcov_capture: - lcov -d . --capture -o lcov.info - -lcov_html: - rm -rf ./cov - mkdir cov - genhtml -o ./cov lcov.info - -coverage: - $(MAKE) COVERAGE=1 tommycheck$(EXE) - $(MAKE) lcov_reset - ./tommycheck$(EXE) - $(MAKE) lcov_capture - $(MAKE) lcov_html - -valgrind: - valgrind \ - --tool=memcheck \ - --track-origins=yes \ - --read-var-info=yes \ - -v $(CHECK) \ - 2> valgrind.log - tail valgrind.log - -callgrind: - valgrind \ - --tool=callgrind \ - --dump-instr=yes \ - --trace-jump=yes \ - -v $(CHECK) \ - 2> callgrind.log - tail callgrind.log - -cachegrind: - valgrind \ - --tool=cachegrind \ - -v $(CHECK) \ - 2> cachegrind.log - tail cachegrind.log - -phony: - -graph: phony - cd benchmark && sh gr_all.sh - -doc: phony tommy.doxygen tommy.css $(DEP) - rm -rf doc - mkdir doc - cp -a benchmark/data/* doc/ - rm -f doc/*/*.lst - rm -f doc/*/*.gnu - doxygen tommy.doxygen - rm -f doc/doxygen.png - rm -f doc/tab_*.png - -web: phony tommyweb.doxygen tommy.css $(DEP) - rm -rf web - mkdir web - cp -a benchmark/data/* web/ - rm -f web/*/*.lst - rm -f web/*/*.gnu - doxygen tommyweb.doxygen - rm -f web/doxygen.png - rm -f web/tab_*.png - -clean: - rm -f *.log *.s *.lst *.o - rm -f *.ncb *.suo *.obj - rm -f *.gcno *.gcda lcov.info - rm -rf Debug Release x64 - rm -f callgrind.out.* - rm -f cachegrind.out.* - -distclean: clean - rm -f tommybench$(EXE) tommycheck$(EXE) - -maintainerclean: distclean - rm -rf doc web - -DIST=tommyds-$(VERSION) - -DISTFILES=\ - Makefile \ - README LICENSE AUTHORS INSTALL HISTORY \ - tommy.doxygen tommy.css tommy-header.html tommy-footer.html \ - benchmark.vcxproj benchmark.sln \ - benchmark.geany \ - benchmark.cc \ - check.c - -dist: - mkdir $(DIST) - mkdir $(DIST)/tommyds - cp $(DISTFILES) $(DIST) - cp $(DEP) $(DIST)/tommyds - cp $(DEPTEST) $(DIST) - cp -R doc $(DIST) - cp -R benchmark $(DIST)/benchmark - rm -f $(DIST)/benchmark/data/*/*.png - rm -rf $(DIST)/benchmark/data/test - rm -f $(DIST)/benchmark/arial.ttf - rm -f $(DIST).tar.gz - tar cfzo $(DIST).tar.gz $(DIST) - rm -f $(DIST).zip - zip -r $(DIST).zip $(DIST) - rm -r $(DIST) - -distcheck: dist - tar zxvf $(DIST).tar.gz - cd $(DIST) && make check - rm -rf $(DIST) diff --git a/tommyds/README b/tommyds/README deleted file mode 100644 index cdef1a6..0000000 --- a/tommyds/README +++ /dev/null @@ -1,31 +0,0 @@ -TommyDS -======= - -TommyDS is a C library of array, hashtables and tries data structures, -designed for high performance and providing an easy to use interface. - -It's faster than all the similar libraries like rbtree, judy, goodledensehash, -khash, uthash, nedtries and others. - -The data structures provided are: - - tommy_list - A double linked list. - tommy_array - A linear array. It doesn't fragment - the heap. - tommy_arrayblk - A blocked linear array. It doesn't fragment - the heap and it minimizes the space occupation. - tommy_hashtable - A fixed size chained hashtable. - tommy_hashdyn - A dynamic chained hashtable. - tommy_hashlin - A linear chained hashtable. It doesn't have the - problem of the delay when resizing and it doesn't - fragment the heap. - tommy_trie - A trie optimized for cache utilization. - tommy_trie_inplace - A trie completely inplace. - -The documentation is available in HTML format in the doc/index.html file, -and directly in the .h files. - -The official site of TommyDS is: - - http://tommyds.sourceforge.net - diff --git a/tommyds/benchmark.cc b/tommyds/benchmark.cc deleted file mode 100644 index 39bb996..0000000 --- a/tommyds/benchmark.cc +++ /dev/null @@ -1,2674 +0,0 @@ -/* - * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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. - */ - -/** - * Tommy benchmark program. - * - * Simply run it without any options. It will generate a set of *.lst files - * suitable for producing graphs with gnuplot or another program. - * - * This program is supposed to be also compiled with a C++ compiler, - * to be able to use the Google dense_hash_map templates. - * For this reason it contains a lot of unnecessary casting for C of void* pointers. - */ - -#define __STDC_LIMIT_MACROS 1 /* needed by ck */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(__linux) -#include -#include -#include -#endif - -#if defined(__MACH__) -#include -#endif - -/* Judy available on in x86 */ -#if defined(_WIN32) -#include -#if defined(_M_IX86) || defined(__i386__) -#define USE_JUDY -#endif -#else -#if defined(__i386__) -#define USE_JUDY -#endif -#endif - -/* Available only in C++ */ -#ifdef __cplusplus -#define USE_GOOGLEDENSEHASH -#define USE_GOOGLEBTREE -#define USE_STXBTREE -#define USE_CPPMAP -#define USE_CPPUNORDEREDMAP -#endif - -/******************************************************************************/ -/* data structure */ - -/* Tommy data structures */ -/* We directly include the C file to have functions automatically */ -/* expanded inline by the compiler like other implementations */ -#include "tommyds/tommy.h" -#include "tommyds/tommy.c" - -/* C++ Btree */ -#ifdef USE_CPPMAP -#include -#endif - -/* C++ Hashtable */ -#ifdef USE_CPPUNORDEREDMAP -#include -#endif - -/* Google C dense hash table */ -/* http://code.google.com/p/google-sparsehash/ in the experimental/ directory */ -/* Disabled by default because it's superseeded by the C++ version. */ -/* Note that it has a VERY BAD performance on the "Change" test, */ -/* so we disable it in the general graphs */ -/* #define USE_GOOGLELIBCHASH */ -#ifdef USE_GOOGLELIBCHASH -#define htonl(x) 0 /* used for serialization, not needed here */ -#define ntohl(x) 0 /* used for serialization, not needed here */ -#define Table(x) Dense##x /* Use google dense tables */ -#include "benchmark/lib/google/libchash.c" -#endif - -/* Google C++ dense hash table. */ -/* http://code.google.com/p/google-sparsehash/ */ -/* Note that after erasing we always call resize(0) to possibly trigger a table resize to free some space */ -/* Otherwise, it would be an unfair advantage never shrinking on deletion. */ -/* The shrink is triggered only sometimes, so the performance doesn't suffer to much */ -#ifdef USE_GOOGLEDENSEHASH -#include -#endif - -/* Google BTree */ -/* https://code.google.com/p/cpp-btree/ */ -#ifdef USE_GOOGLEBTREE -#if defined(_MSC_VER) /* Allow compilation in MSVC 2010 */ -#include -typedef size_t ssize_t; -#endif -#undef min -#undef max -#include "benchmark/lib/cpp-btree/btree_map.h" -#endif - -/* STX BTree */ -/* http://panthema.net/2007/stx-btree/ */ -#ifdef USE_STXBTREE -#include "benchmark/lib/stx/btree_map.h" -#endif - -/* UTHASH */ -/* http://uthash.sourceforge.net/ */ -#include "benchmark/lib/uthash/uthash.h" - -/* Nedtries */ -/* http://www.nedprod.com/programs/portable/nedtries/ */ -/* Note that I fixed a crash bug when inserting objects with 0 key */ -/* This fix is now in the nedtries github */ -/* https://github.com/ned14/nedtries/commit/21039696f27db4ffac70a82f89dc5d00ae74b332 */ -/* We do not use the C++ implementation as it doesn't compile with gcc 4.4.3 */ -#include "benchmark/lib/nedtries/nedtrie.h" - -/* KHash */ -/* http://attractivechaos.awardspace.com/ */ -/* It has the unfair advantage on remove by never shrinking when deleting objects. */ -#define inline __inline -#include "benchmark/lib/khash/khash.h" - -/* RBtree */ -/* http://www.canonware.com/rb/ */ -/* We are using the most recent implementation rb_never/rb.h. Not rb_old/ or rb_new/ */ -#define RB_COMPACT -#define ssize_t size_t -#define bool int -#define true 1 -#define false 0 -#include "benchmark/lib/rb/rb_newer.h" - -/* Judy */ -/* http://judy.sourceforge.net/ */ -/* Disabled on 64 bits platform in Windows as it's not supported */ -#ifdef USE_JUDY -#include "benchmark/lib/judy/Judy.h" -#endif - -/* JudyArray */ -/* http://code.google.com/p/judyarray/ */ -/* Ensure to use the version "judy64na.c". previous ones are not not working with integers key. */ -#include "benchmark/lib/judyarray/judy64na.c" -#define USE_JUDYARRAY - -/* Binary Search Cube */ -/* https://sites.google.com/site/binarysearchcube/ */ -#include "benchmark/lib/cube/binary-search-tesseract-1.0.c" -#define USE_CUBE - -/* Concurrency Kit Hash Set */ -/* http://concurrencykit.org/ */ -/* Note that it has a VERY BAD performance on the "Change" test, */ -/* so we disable it in the graphs until further investigation */ -/* #define USE_CK */ -#if defined(USE_CK) && defined(__linux) -/* if you enable it, ensure to link also with the -lck option */ -extern "C" { -#include -} -#endif - -/* default hash function used */ -#define hash(v) tommy_inthash_u32(v) - -/******************************************************************************/ -/* objects */ - -#define PAYLOAD 16 /**< Size of payload data for objects */ - -struct rbt_object { - rb_node(struct rbt_object) link; - unsigned value; - char payload[PAYLOAD]; -}; - -struct hashtable_object { - tommy_node node; - unsigned value; - char payload[PAYLOAD]; -}; - -struct uthash_object { - UT_hash_handle hh; - unsigned value; - char payload[PAYLOAD]; -}; - -struct khash_object { - unsigned value; - char payload[PAYLOAD]; -}; - -struct google_object { - unsigned value; - char payload[PAYLOAD]; -}; - -struct stx_object { - unsigned value; - char payload[PAYLOAD]; -}; - -struct trie_object { - tommy_trie_node node; - unsigned value; - char payload[PAYLOAD]; -}; - -struct trie_inplace_object { - tommy_trie_inplace_node node; - unsigned value; - char payload[PAYLOAD]; -}; - -struct judy_object { - unsigned value; - char payload[PAYLOAD]; -}; - -struct judyarray_object { - unsigned value; - char payload[PAYLOAD]; -}; - -struct nedtrie_object { - NEDTRIE_ENTRY(nedtrie_object) link; - unsigned value; - char payload[PAYLOAD]; -}; - -struct cpp_object { - unsigned value; - char payload[PAYLOAD]; -}; - -struct cube_object { - unsigned value; - char payload[PAYLOAD]; -}; - -struct ck_object { - unsigned value; - char payload[PAYLOAD]; -}; - -struct rbt_object* RBTREE; -struct hashtable_object* HASHTABLE; -struct hashtable_object* HASHDYN; -struct hashtable_object* HASHLIN; -struct trie_object* TRIE; -struct trie_inplace_object* TRIE_INPLACE; -struct khash_object* KHASH; -struct google_object* GOOGLE; -struct stx_object* STX; -struct uthash_object* UTHASH; -struct nedtrie_object* NEDTRIE; -struct cpp_object* CPP; -#ifdef USE_JUDY -struct judy_object* JUDY; -#endif -#ifdef USE_JUDYARRAY -struct judyarray_object* JUDYARRAY; -#endif -#ifdef USE_CUBE -struct cube_object* CUBE; -#endif -#ifdef USE_CK -struct ck_object* CK; - -static void* hs_malloc(size_t r) -{ - return malloc(r); -} - -static void hs_free(void* p, size_t b, bool r) -{ - (void)b; - (void)r; - free(p); -} - -static struct ck_malloc my_allocator = { - hs_malloc, - 0, - hs_free -}; - -static unsigned long hs_hash(const void* void_object, unsigned long seed) -{ - const struct ck_object* object = void_object; - (void)seed; - return hash(object->value); -} - -static bool hs_compare(const void *previous, const void *compare) -{ - const struct ck_object* object_1 = previous; - const struct ck_object* object_2 = compare; - return object_1->value == object_2->value; -} -#endif - -/******************************************************************************/ -/* containers */ - -int rbt_compare(const void* void_a, const void* void_b) -{ - const struct rbt_object* a = (const struct rbt_object*)void_a; - const struct rbt_object* b = (const struct rbt_object*)void_b; - - int va = a->value; - int vb = b->value; - - if (va < vb) - return -1; - if (va > vb) - return 1; - return 0; -} - -int tommy_hashtable_compare(const void* void_arg, const void* void_obj) -{ - const unsigned* arg = (const unsigned*)void_arg; - const struct hashtable_object* obj = (const struct hashtable_object*)void_obj; - - if (*arg == obj->value) - return 0; - - return 1; -} - -typedef rbt(struct rbt_object) rbtree_t; - -rb_gen(static, rbt_, rbtree_t, struct rbt_object, link, rbt_compare) - -NEDTRIE_HEAD(nedtrie_t, nedtrie_object); - -static size_t nedtrie_func(const struct nedtrie_object* r) -{ - return r->value; -} - -NEDTRIE_GENERATE(static, nedtrie_t, nedtrie_object, link, nedtrie_func, NEDTRIE_NOBBLEZEROS(nedtrie_t)); - -KHASH_MAP_INIT_INT(word, struct khash_object*) - -rbtree_t tree; -tommy_hashtable hashtable; -tommy_hashdyn hashdyn; -tommy_hashlin hashlin; -tommy_allocator trie_allocator; -tommy_trie trie; -tommy_trie_inplace trie_inplace; -struct uthash_object* uthash = 0; -struct nedtrie_t nedtrie; -khash_t(word)* khash; -#ifdef __cplusplus -/* use a specialized hash, otherwise the performance depends on the STL implementation used. */ -class cpp_hash { -public: - unsigned operator()(unsigned key) const { return hash(key); } -}; -#endif -#ifdef USE_CPPMAP -typedef std::map cppmap_t; -cppmap_t* cppmap; -#endif -#ifdef USE_CPPUNORDEREDMAP -typedef std::unordered_map cppunorderedmap_t; -cppunorderedmap_t* cppunorderedmap; -#endif -#ifdef USE_GOOGLELIBCHASH -struct HashTable* googlelibhash; -#endif -#ifdef USE_GOOGLEDENSEHASH -typedef google::dense_hash_map googledensehash_t; -googledensehash_t* googledensehash; -#endif -#ifdef USE_GOOGLEBTREE -typedef btree::btree_map googlebtree_t; -googlebtree_t* googlebtree; -#endif -#ifdef USE_STXBTREE -typedef stx::btree_map stxbtree_t; -stxbtree_t* stxbtree; -#endif -#ifdef USE_JUDY -Pvoid_t judy = 0; -#endif -#ifdef USE_JUDYARRAY -Judy* judyarray = 0; -#endif -#ifdef USE_CUBE -struct cube* cube = 0; -#endif -#ifdef USE_CK -ck_hs_t ck; -#endif - -/******************************************************************************/ -/* time */ - -#if defined(_WIN32) -static LARGE_INTEGER win_frequency; -#endif - -static void nano_init(void) -{ -#if defined(_WIN32) - if (!QueryPerformanceFrequency(&win_frequency)) { - win_frequency.QuadPart = 0; - } -#endif -} - -static tommy_uint64_t nano(void) -{ - tommy_uint64_t ret; -#if defined(_WIN32) - LARGE_INTEGER t; - - if (!QueryPerformanceCounter(&t)) - return 0; - - ret = (t.QuadPart / win_frequency.QuadPart) * 1000000000; - - ret += (t.QuadPart % win_frequency.QuadPart) * 1000000000 / win_frequency.QuadPart; -#elif defined(__MACH__) - mach_timebase_info_data_t info; - kern_return_t r; - tommy_uint64_t t; - - t = mach_absolute_time(); - - r = mach_timebase_info(&info); - if (r != 0) { - abort(); - } - - ret = (t / info.denom) * info.numer; - - ret += (t % info.denom) * info.numer / info.denom; -#elif defined(__linux) - struct timespec ts; - int r; - - r = clock_gettime(CLOCK_MONOTONIC, &ts); - if (r != 0) { - abort(); - } - - ret = ts.tv_sec * (tommy_uint64_t)1000000000 + ts.tv_nsec; -#else - struct timeval tv; - int r; - - r = gettimeofday(&tv, 0); - if (r != 0) { - abort(); - } - - ret = tv.tv_sec * (tommy_uint64_t)1000000000 + tv.tv_usec * 1000; -#endif - return ret; -} - -/******************************************************************************/ -/* random */ - -/** - * Pseudo random number generator. - * Note that using (rand() % max) in Visual C results in totally bogus values, - * with *strong* cache effects when accessing elements in a not really random order. - * This happen because Visual C uses a simple linear congruential generator with only 32 bits. - */ -tommy_uint64_t SEED = 0; - -unsigned rnd(unsigned max) -{ - unsigned r; - tommy_uint64_t divider; - -loop: - /* linear congruential generator from MMIX by Donald Knuth, http://en.wikipedia.org/wiki/Linear_congruential_generator */ -#ifdef _MSC_VER - divider = 0xFFFFFFFFFFFFFFFF / max; - SEED = SEED * 6364136223846793005 + 1442695040888963407; -#else - divider = 0xFFFFFFFFFFFFFFFFULL / max; - SEED = SEED * 6364136223846793005LL + 1442695040888963407LL; -#endif - - r = (unsigned)(SEED / divider); - - /* it may happen as the divider is approximated down */ - if (r >= max) - goto loop; - - return r; -} - -/******************************************************************************/ -/* test */ - -#ifdef _DEBUG -#define MAX 100000 -#else -#if defined(__x86_64__) || defined(_M_X64) -#define MAX 100000000 -#else -#define MAX 10000000 -#endif -#endif - -/** - * Max number of retries. - */ -#define RETRY_MAX 7 - -/** - * Max total number of test for retries. - */ -#define MIN_TRY 2000000 - -/** - * Operations. - */ -#define OPERATION_INSERT 0 -#define OPERATION_HIT 1 -#define OPERATION_MISS 2 -#define OPERATION_SIZE 3 -#define OPERATION_CHANGE 4 -#define OPERATION_REMOVE 5 -#define OPERATION_MAX 6 - -const char* OPERATION_NAME[OPERATION_MAX] = { - "insert", - "hit", - "miss", - "size", - "change", - "remove", -}; - -/** - * Orders. - */ -#define ORDER_FORWARD 0 -#define ORDER_RANDOM 1 -#define ORDER_MAX 2 - -const char* ORDER_NAME[ORDER_MAX] = { - "forward", - "random", -}; - -/** - * Data structures. - */ -#define DATA_HASHTABLE 0 -#define DATA_HASHDYN 1 -#define DATA_HASHLIN 2 -#define DATA_TRIE 3 -#define DATA_TRIE_INPLACE 4 -#define DATA_TREE 5 -#define DATA_NEDTRIE 6 -#define DATA_KHASH 7 -#define DATA_UTHASH 8 -#define DATA_JUDY 9 -#define DATA_JUDYARRAY 10 -#define DATA_GOOGLEDENSEHASH 11 -#define DATA_GOOGLEBTREE 12 -#define DATA_STXBTREE 13 -#define DATA_CPPUNORDEREDMAP 14 -#define DATA_CPPMAP 15 -#define DATA_CUBE 16 -#ifdef USE_GOOGLELIBCHASH -#define DATA_GOOGLELIBCHASH 17 -#endif -#ifdef USE_CK -#define DATA_CK 18 -#endif -#define DATA_MAX 19 - -const char* DATA_NAME[DATA_MAX] = { - "tommy-hashtable", - "tommy-hashdyn", - "tommy-hashlin", - "tommy-trie", - "tommy-trie-inplace", - "rbtree", - "nedtrie", - "khash", - "uthash", - "judy", - "judyarray", - "googledensehash", - "googlebtree", - "stxbtree", - "c++unorderedmap", - "c++map", - "tesseract", - "googlelibchash", - "concurrencykit", -}; - -/** - * Logged data. - */ -unsigned LOG[RETRY_MAX][DATA_MAX][ORDER_MAX][OPERATION_MAX]; - -/** - * Time limit in nanosecond. - * We stop measuring degenerated cases after this limit. - */ -#define TIME_MAX_NS 1500 - -/** - * Last measure. - * Used to stop degenerated cases. - */ -unsigned LAST[DATA_MAX][ORDER_MAX]; - -/** - * Test state. - */ -unsigned the_retry; /**< Number of retry. */ -unsigned the_data; /**< Data struture in test. */ -unsigned the_operation; /**< Operation in test. */ -unsigned the_order; /**< Order in test. */ -unsigned the_max; /**< Number of elements in test. */ -tommy_uint64_t the_time; /**< Start time of the test. */ -unsigned the_start_data; /**< Saved data structure in the last START command, simply to avoid to repeat it for STOP. */ - -/** - * Control flow state. - */ -tommy_bool_t the_log; - -/** - * If the data structure should be in the graph, even if with no data. - * It allows to have equal graphs also if in some platforms a data structure is not available. - */ -tommy_bool_t is_listed(unsigned data) -{ - (void)data; - - /* always have all the columns, we exclude them in the graphs */ - return 1; -} - -/** - * If the data structure should be measured. - */ -tommy_bool_t is_select(unsigned data) -{ - return the_data == data; -} - -tommy_bool_t start(unsigned data) -{ - the_start_data = data; - - if (!is_select(the_start_data)) - return 0; - - if (!the_log) - printf("%10s, %10s, %12s, ", ORDER_NAME[the_order], OPERATION_NAME[the_operation], DATA_NAME[data]); - - the_time = nano(); - return 1; -} - -void stop(void) -{ - tommy_uint64_t elapsed = nano() - the_time; - - if (!is_select(the_start_data)) - return; - - if (!the_log) { - printf("%4u [ns]\n", (unsigned)(elapsed / the_max)); - } - - LOG[the_retry][the_data][the_order][the_operation] = (unsigned)(elapsed / the_max); -} - -void mem(unsigned data, tommy_size_t v) -{ - if (!the_log) { - printf("%10s, %10s, %12s, ", ORDER_NAME[the_order], OPERATION_NAME[the_operation], DATA_NAME[data]); - printf("%4u [byte]\n", (unsigned)(v / the_max)); - } - - LOG[the_retry][the_data][the_order][the_operation] = (unsigned)(v / the_max); -} - -#define COND(s) if (is_select(s)) -#define START(s) if (start(s)) for(i=0;i=0;--i) { - unsigned j, t; - j = rnd(i+1); - t = RAND0[i]; - RAND0[i] = RAND0[j]; - RAND0[j] = t; - - j = rnd(i+1); - t = RAND1[i]; - RAND1[i] = RAND1[j]; - RAND1[j] = t; - } -} - -void order_done(void) -{ - free(FORWARD); - free(RAND0); - free(RAND1); -} - -void test_alloc(void) -{ - COND(DATA_TREE) { - rbt_new(&tree); - RBTREE = (struct rbt_object*)malloc(sizeof(struct rbt_object) * the_max); - } - - COND(DATA_HASHTABLE) { - tommy_hashtable_init(&hashtable, 2 * the_max); - HASHTABLE = (struct hashtable_object*)malloc(sizeof(struct hashtable_object) * the_max); - } - - COND(DATA_HASHDYN) { - tommy_hashdyn_init(&hashdyn); - HASHDYN = (struct hashtable_object*)malloc(sizeof(struct hashtable_object) * the_max); - } - - COND(DATA_HASHLIN) { - tommy_hashlin_init(&hashlin); - HASHLIN = (struct hashtable_object*)malloc(sizeof(struct hashtable_object) * the_max); - } - - COND(DATA_TRIE) { - tommy_allocator_init(&trie_allocator, TOMMY_TRIE_BLOCK_SIZE, TOMMY_TRIE_BLOCK_SIZE); - tommy_trie_init(&trie, &trie_allocator); - TRIE = (struct trie_object*)malloc(sizeof(struct trie_object) * the_max); - } - - COND(DATA_TRIE_INPLACE) { - tommy_trie_inplace_init(&trie_inplace); - TRIE_INPLACE = (struct trie_inplace_object*)malloc(sizeof(struct trie_inplace_object) * the_max); - } - - COND(DATA_KHASH) { - KHASH = (struct khash_object*)malloc(sizeof(struct khash_object) * the_max); - khash = kh_init(word); - } - -#ifdef USE_GOOGLELIBCHASH - COND(DATA_GOOGLELIBCHASH) { - GOOGLE = (struct google_object*)malloc(sizeof(struct google_object) * the_max); - googlelibhash = AllocateHashTable(sizeof(void*), 0); - } -#endif - -#ifdef USE_GOOGLEDENSEHASH - COND(DATA_GOOGLEDENSEHASH) { - GOOGLE = (struct google_object*)malloc(sizeof(struct google_object) * the_max); - googledensehash = new googledensehash_t; - googledensehash->set_empty_key(-1); - googledensehash->set_deleted_key(-2); - } -#endif - -#ifdef USE_GOOGLEBTREE - COND(DATA_GOOGLEBTREE) { - GOOGLE = (struct google_object*)malloc(sizeof(struct google_object) * the_max); - googlebtree = new googlebtree_t; - } -#endif - -#ifdef USE_STXBTREE - COND(DATA_STXBTREE) { - STX = (struct stx_object*)malloc(sizeof(struct stx_object) * the_max); - stxbtree = new stxbtree_t; - } -#endif - -#ifdef USE_CPPMAP - COND(DATA_CPPMAP) { - CPP = (struct cpp_object*)malloc(sizeof(struct cpp_object) * the_max); - cppmap = new cppmap_t; - } -#endif - -#ifdef USE_CPPUNORDEREDMAP - COND(DATA_CPPUNORDEREDMAP) { - CPP = (struct cpp_object*)malloc(sizeof(struct cpp_object) * the_max); - cppunorderedmap = new cppunorderedmap_t; - } -#endif - - COND(DATA_UTHASH) { - UTHASH = (struct uthash_object*)malloc(sizeof(struct uthash_object) * the_max); - } - - COND(DATA_NEDTRIE) { - NEDTRIE_INIT(&nedtrie); - NEDTRIE = (struct nedtrie_object*)malloc(sizeof(struct nedtrie_object) * the_max); - } - -#ifdef USE_JUDY - COND(DATA_JUDY) { - JUDY = (struct judy_object*)malloc(sizeof(struct judy_object) * the_max); - } -#endif - -#ifdef USE_JUDYARRAY - COND(DATA_JUDYARRAY) { - JUDYARRAY = (struct judyarray_object*)malloc(sizeof(struct judyarray_object) * the_max); - judyarray = (Judy*)judy_open(1024, 1); - } -#endif - -#ifdef USE_CUBE - COND(DATA_CUBE) { - CUBE = (struct cube_object*)malloc(sizeof(struct cube_object) * the_max); - cube = create_cube(); - } -#endif - -#ifdef USE_CK - COND(DATA_CK) { - CK = (struct ck_object*)malloc(sizeof(struct ck_object) * the_max); - /* Adding CK_HS_MODE_DELETE makes the performance worse */ - /* when the number of elements is near and just a little lower than a */ - /* power of 2. For example, with 63095 elements: */ -/* -63095 ck forward - forward, insert, ck, 200 [ns] - forward, change, ck, 23977 [ns] <<<<< - forward, hit, ck, 338 [ns] - forward, miss, ck, 209 [ns] - forward, remove, ck, 325 [ns] -63095 ck random - random, insert, ck, 197 [ns] - random, change, ck, 24025 [ns] <<<<< - random, hit, ck, 342 [ns] - random, miss, ck, 206 [ns] - random, remove, ck, 337 [ns] -*/ - /* Without CK_HS_MODE_DELETE performance are better, but still */ - /* very slow: */ -/* -63095 ck forward - forward, insert, ck, 193 [ns] - forward, change, ck, 3102 [ns] <<<<< - forward, hit, ck, 344 [ns] - forward, miss, ck, 3330 [ns] <<<<< - forward, remove, ck, 327 [ns] -63095 ck random - random, insert, ck, 193 [ns] - random, change, ck, 2984 [ns] <<<<< - random, hit, ck, 340 [ns] - random, miss, ck, 3261 [ns] <<<<< - random, remove, ck, 341 [ns] -*/ - ck_hs_init(&ck, CK_HS_MODE_OBJECT, hs_hash, hs_compare, &my_allocator, 32, 0); - } -#endif -} - -void test_free(void) -{ - COND(DATA_TREE) { - free(RBTREE); - } - - COND(DATA_HASHTABLE) { - if (tommy_hashtable_count(&hashtable) != 0) - abort(); - tommy_hashtable_done(&hashtable); - free(HASHTABLE); - } - - COND(DATA_HASHDYN) { - if (tommy_hashdyn_count(&hashdyn) != 0) - abort(); - tommy_hashdyn_done(&hashdyn); - free(HASHDYN); - } - - COND(DATA_HASHLIN) { - if (tommy_hashlin_count(&hashlin) != 0) - abort(); - tommy_hashlin_done(&hashlin); - free(HASHLIN); - } - - COND(DATA_TRIE) { - if (tommy_trie_count(&trie) != 0) - abort(); - tommy_allocator_done(&trie_allocator); - free(TRIE); - } - - COND(DATA_TRIE_INPLACE) { - if (tommy_trie_inplace_count(&trie_inplace) != 0) - abort(); - free(TRIE_INPLACE); - } - - COND(DATA_KHASH) { - kh_destroy(word, khash); - free(KHASH); - } - -#ifdef USE_GOOGLELIBCHASH - COND(DATA_GOOGLELIBCHASH) { - FreeHashTable(googlelibhash); - free(GOOGLE); - } -#endif - -#ifdef USE_GOOGLEDENSEHASH - COND(DATA_GOOGLEDENSEHASH) { - free(GOOGLE); - delete googledensehash; - } -#endif - -#ifdef USE_GOOGLEBTREE - COND(DATA_GOOGLEBTREE) { - free(GOOGLE); - delete googlebtree; - } -#endif - -#ifdef USE_STXBTREE - COND(DATA_STXBTREE) { - free(STX); - delete stxbtree; - } -#endif - -#ifdef USE_CPPMAP - COND(DATA_CPPMAP) { - free(CPP); - delete cppmap; - } -#endif - -#ifdef USE_CPPUNORDEREDMAP - COND(DATA_CPPUNORDEREDMAP) { - free(CPP); - delete cppunorderedmap; - } -#endif - - COND(DATA_UTHASH) { - free(UTHASH); - } - - COND(DATA_NEDTRIE) { - free(NEDTRIE); - } - -#ifdef USE_JUDY - COND(DATA_JUDY) { - free(JUDY); - } -#endif - -#ifdef USE_JUDYARRAY - COND(DATA_JUDYARRAY) { - free(JUDYARRAY); - judy_close(judyarray); - } -#endif - -#ifdef USE_CUBE - COND(DATA_CUBE) { - free(CUBE); - destroy_cube(cube); - } -#endif - -#ifdef USE_CK - COND(DATA_CK) { - free(CK); - ck_hs_destroy(&ck); - } -#endif -} - -void test_insert(unsigned* INSERT) -{ - unsigned i; - - START(DATA_TREE) { - unsigned key = INSERT[i]; - RBTREE[i].value = key; - rbt_insert(&tree, &RBTREE[i]); - } STOP(); - - START(DATA_HASHTABLE) { - unsigned key = INSERT[i]; - unsigned hash_key = hash(key); - HASHTABLE[i].value = key; - tommy_hashtable_insert(&hashtable, &HASHTABLE[i].node, &HASHTABLE[i], hash_key); - } STOP(); - - START(DATA_HASHDYN) { - unsigned key = INSERT[i]; - unsigned hash_key = hash(key); - HASHDYN[i].value = key; - tommy_hashdyn_insert(&hashdyn, &HASHDYN[i].node, &HASHDYN[i], hash_key); - } STOP(); - - START(DATA_HASHLIN) { - unsigned key = INSERT[i]; - unsigned hash_key = hash(key); - HASHLIN[i].value = key; - tommy_hashlin_insert(&hashlin, &HASHLIN[i].node, &HASHLIN[i], hash_key); - } STOP(); - - START(DATA_TRIE) { - unsigned key = INSERT[i]; - TRIE[i].value = key; - tommy_trie_insert(&trie, &TRIE[i].node, &TRIE[i], key); - } STOP(); - - START(DATA_TRIE_INPLACE) { - unsigned key = INSERT[i]; - TRIE_INPLACE[i].value = key; - tommy_trie_inplace_insert(&trie_inplace, &TRIE_INPLACE[i].node, &TRIE_INPLACE[i], key); - } STOP(); - - /* for khash we hash the key because internally khash doesn't use any hash for integer keys */ - START(DATA_KHASH) { - unsigned key = INSERT[i]; - unsigned hash_key = hash(key); - khiter_t k; - int r; - KHASH[i].value = key; - k = kh_put(word, khash, hash_key, &r); - if (!r) - abort(); - kh_value(khash, k) = &KHASH[i]; - } STOP(); - -#ifdef USE_GOOGLELIBCHASH - START(DATA_GOOGLELIBCHASH) { - unsigned key = INSERT[i]; - HTItem* r; - u_long ptr_value = (u_long)&GOOGLE[i]; - GOOGLE[i].value = key; - r = HashInsert(googlelibhash, key, ptr_value); - if (!r) - abort(); - } STOP(); -#endif - -#ifdef USE_GOOGLEDENSEHASH - START(DATA_GOOGLEDENSEHASH) { - unsigned key = INSERT[i]; - struct google_object* obj = &GOOGLE[i]; - GOOGLE[i].value = key; - (*googledensehash)[key] = obj; - } STOP(); -#endif - -#ifdef USE_GOOGLEBTREE - START(DATA_GOOGLEBTREE) { - unsigned key = INSERT[i]; - struct google_object* obj = &GOOGLE[i]; - GOOGLE[i].value = key; - (*googlebtree)[key] = obj; - } STOP(); -#endif - -#ifdef USE_STXBTREE - START(DATA_STXBTREE) { - unsigned key = INSERT[i]; - struct stx_object* obj = &STX[i]; - STX[i].value = key; - (*stxbtree)[key] = obj; - } STOP(); -#endif - -#ifdef USE_CPPMAP - START(DATA_CPPMAP) { - unsigned key = INSERT[i]; - struct cpp_object* obj = &CPP[i]; - CPP[i].value = key; - (*cppmap)[key] = obj; - } STOP(); -#endif - -#ifdef USE_CPPUNORDEREDMAP - START(DATA_CPPUNORDEREDMAP) { - unsigned key = INSERT[i]; - struct cpp_object* obj = &CPP[i]; - CPP[i].value = key; - (*cppunorderedmap)[key] = obj; - } STOP(); -#endif - - START(DATA_UTHASH) { - unsigned key = INSERT[i]; - struct uthash_object* obj = &UTHASH[i]; - obj->value = key; - HASH_ADD_INT(uthash, value, obj); - } STOP(); - - START(DATA_NEDTRIE) { - unsigned key = INSERT[i]; - NEDTRIE[i].value = key; - NEDTRIE_INSERT(nedtrie_t, &nedtrie, &NEDTRIE[i]); - } STOP(); - -#ifdef USE_JUDY - START(DATA_JUDY) { - unsigned key = INSERT[i]; - Pvoid_t PValue; - JUDY[i].value = key; - JLI(PValue, judy, key); - *(struct judy_object**)PValue = &JUDY[i]; - } STOP(); -#endif - -#ifdef USE_JUDYARRAY - START(DATA_JUDYARRAY) { - judyvalue key = INSERT[i]; - JudySlot* pvalue; - JUDYARRAY[i].value = key; - pvalue = judy_cell(judyarray, (uchar*)&key, 0); - *(struct judyarray_object**)pvalue = &JUDYARRAY[i]; - } STOP(); -#endif - -#ifdef USE_CUBE - START(DATA_CUBE) { - unsigned key = INSERT[i]; - CUBE[i].value = key; - set_key(cube, key, &CUBE[i]); - } STOP(); -#endif - -#ifdef USE_CK - START(DATA_CK) { - unsigned key = INSERT[i]; - unsigned hash_key; - CK[i].value = key; - hash_key = CK_HS_HASH(&ck, hs_hash, &CK[i]); - ck_hs_put(&ck, hash_key, &CK[i]); - } STOP(); -#endif -} - -void test_hit(unsigned* SEARCH) -{ - unsigned i; - - /* always dereference the object found. It has cache effect. */ - /* considering we are dealing with objects, it makes sense to simulate an access to it */ - /* this favorites data structures that store part of the information in the object itself */ - const int dereference = 1; - - const unsigned DELTA = 1; - - START(DATA_TREE) { - unsigned key = SEARCH[i] + DELTA; - struct rbt_object key_obj; - struct rbt_object* obj; - key_obj.value = key; - obj = rbt_search(&tree, &key_obj); - if (!obj) - abort(); - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); - - START(DATA_HASHTABLE) { - unsigned key = SEARCH[i] + DELTA; - unsigned hash_key = hash(key); - struct hashtable_object* obj; - obj = (struct hashtable_object*)tommy_hashtable_search(&hashtable, tommy_hashtable_compare, &key, hash_key); - if (!obj) - abort(); - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); - - START(DATA_HASHDYN) { - unsigned key = SEARCH[i] + DELTA; - unsigned hash_key = hash(key); - struct hashtable_object* obj; - obj = (struct hashtable_object*)tommy_hashdyn_search(&hashdyn, tommy_hashtable_compare, &key, hash_key); - if (!obj) - abort(); - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); - - START(DATA_HASHLIN) { - unsigned key = SEARCH[i] + DELTA; - unsigned hash_key = hash(key); - struct hashtable_object* obj; - obj = (struct hashtable_object*)tommy_hashlin_search(&hashlin, tommy_hashtable_compare, &key, hash_key); - if (!obj) - abort(); - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); - - START(DATA_TRIE) { - unsigned key = SEARCH[i] + DELTA; - struct trie_object* obj; - obj = (struct trie_object*)tommy_trie_search(&trie, key); - if (!obj) - abort(); - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); - - START(DATA_TRIE_INPLACE) { - unsigned key = SEARCH[i] + DELTA; - struct trie_inplace_object* obj; - obj = (struct trie_inplace_object*)tommy_trie_inplace_search(&trie_inplace, key); - if (!obj) - abort(); - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); - - START(DATA_KHASH) { - unsigned key = SEARCH[i] + DELTA; - unsigned hash_key = hash(key); - khiter_t k; - k = kh_get(word, khash, hash_key); - if (k == kh_end(khash)) - abort(); - if (dereference) { - struct khash_object* obj = kh_value(khash, k); - if (obj->value != key) - abort(); - } - } STOP(); - -#ifdef USE_GOOGLELIBCHASH - START(DATA_GOOGLELIBCHASH) { - unsigned key = SEARCH[i] + DELTA; - HTItem* ptr; - ptr = HashFind(googlelibhash, key); - if (!ptr) - abort(); - if (dereference) { - struct google_object* obj = (void*)ptr->data; - if (obj->value != key) - abort(); - } - } STOP(); -#endif - -#ifdef USE_GOOGLEDENSEHASH - START(DATA_GOOGLEDENSEHASH) { - unsigned key = SEARCH[i] + DELTA; - googledensehash_t::const_iterator ptr = googledensehash->find(key); - if (ptr == googledensehash->end()) - abort(); - if (dereference) { - struct google_object* obj = ptr->second; - if (obj->value != key) - abort(); - } - } STOP(); -#endif - -#ifdef USE_GOOGLEBTREE - START(DATA_GOOGLEBTREE) { - unsigned key = SEARCH[i] + DELTA; - googlebtree_t::const_iterator ptr = googlebtree->find(key); - if (ptr == googlebtree->end()) - abort(); - if (dereference) { - struct google_object* obj = ptr->second; - if (obj->value != key) - abort(); - } - } STOP(); -#endif - -#ifdef USE_STXBTREE - START(DATA_STXBTREE) { - unsigned key = SEARCH[i] + DELTA; - stxbtree_t::const_iterator ptr = stxbtree->find(key); - if (ptr == stxbtree->end()) - abort(); - if (dereference) { - struct stx_object* obj = ptr->second; - if (obj->value != key) - abort(); - } - } STOP(); -#endif - -#ifdef USE_CPPMAP - START(DATA_CPPMAP) { - unsigned key = SEARCH[i] + DELTA; - cppmap_t::const_iterator ptr = cppmap->find(key); - if (ptr == cppmap->end()) - abort(); - if (dereference) { - struct cpp_object* obj = ptr->second; - if (obj->value != key) - abort(); - } - } STOP(); -#endif - -#ifdef USE_CPPUNORDEREDMAP - START(DATA_CPPUNORDEREDMAP) { - unsigned key = SEARCH[i] + DELTA; - cppunorderedmap_t::const_iterator ptr = cppunorderedmap->find(key); - if (ptr == cppunorderedmap->end()) - abort(); - if (dereference) { - struct cpp_object* obj = ptr->second; - if (obj->value != key) - abort(); - } - } STOP(); -#endif - - START(DATA_UTHASH) { - unsigned key = SEARCH[i] + DELTA; - struct uthash_object* obj; - HASH_FIND_INT(uthash, &key, obj); - if (!obj) - abort(); - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); - - START(DATA_NEDTRIE) { - unsigned key = SEARCH[i] + DELTA; - struct nedtrie_object key_obj; - struct nedtrie_object* obj; - key_obj.value = key; - obj = NEDTRIE_FIND(nedtrie_t, &nedtrie, &key_obj); - if (!obj) - abort(); - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); - -#ifdef USE_JUDY - START(DATA_JUDY) { - Word_t key = SEARCH[i] + DELTA; - Pvoid_t PValue; - JLG(PValue, judy, key); - if (!PValue) - abort(); - if (dereference) { - struct judy_object* obj = *(struct judy_object**)PValue; - if (obj->value != key) - abort(); - } - } STOP(); -#endif - -#ifdef USE_JUDYARRAY - START(DATA_JUDYARRAY) { - judyvalue key = SEARCH[i] + DELTA; - JudySlot* pvalue; - pvalue = judy_slot(judyarray, (uchar*)&key, 0); - if (!pvalue) - abort(); - if (dereference) { - struct judyarray_object* obj = *(struct judyarray_object**)pvalue; - if (obj->value != key) - abort(); - } - } STOP(); -#endif - -#ifdef USE_CUBE - START(DATA_CUBE) { - unsigned key = SEARCH[i] + DELTA; - struct cube_object* obj; - obj = (struct cube_object*)get_key(cube, key); - if (!obj) - abort(); - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); -#endif - -#ifdef USE_CK - START(DATA_CK) { - unsigned key = SEARCH[i] + DELTA; - unsigned hash_key; - struct ck_object obj_key; - struct ck_object* obj; - obj_key.value = key; - hash_key = CK_HS_HASH(&ck, hs_hash, &obj_key); - obj = (struct ck_object*)ck_hs_get(&ck, hash_key, &obj_key); - if (!obj) - abort(); - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); -#endif -} - -void test_miss(unsigned* SEARCH) -{ - unsigned i; - - const unsigned DELTA = 0; - - START(DATA_TREE) { - struct rbt_object key; - key.value = SEARCH[i] + DELTA; - if (rbt_search(&tree, &key)) - abort(); - } STOP(); - - START(DATA_HASHTABLE) { - unsigned key = SEARCH[i] + DELTA; - unsigned hash_key = hash(key); - struct hashtable_object* obj; - obj = (struct hashtable_object*)tommy_hashtable_search(&hashtable, tommy_hashtable_compare, &key, hash_key); - if (obj) - abort(); - } STOP(); - - START(DATA_HASHDYN) { - unsigned key = SEARCH[i] + DELTA; - unsigned hash_key = hash(key); - struct hashtable_object* obj; - obj = (struct hashtable_object*)tommy_hashdyn_search(&hashdyn, tommy_hashtable_compare, &key, hash_key); - if (obj) - abort(); - } STOP(); - - START(DATA_HASHLIN) { - unsigned key = SEARCH[i] + DELTA; - unsigned hash_key = hash(key); - struct hashtable_object* obj; - obj = (struct hashtable_object*)tommy_hashlin_search(&hashlin, tommy_hashtable_compare, &key, hash_key); - if (obj) - abort(); - } STOP(); - - START(DATA_TRIE) { - struct trie_object* obj; - obj = (struct trie_object*)tommy_trie_search(&trie, SEARCH[i] + DELTA); - if (obj) - abort(); - } STOP(); - - START(DATA_TRIE_INPLACE) { - struct trie_inplace_object* obj; - obj = (struct trie_inplace_object*)tommy_trie_inplace_search(&trie_inplace, SEARCH[i] + DELTA); - if (obj) - abort(); - } STOP(); - - START(DATA_KHASH) { - unsigned key = SEARCH[i] + DELTA; - unsigned hash_key = hash(key); - khiter_t k; - k = kh_get(word, khash, hash_key); - if (k != kh_end(khash)) - abort(); - } STOP(); - -#ifdef USE_GOOGLELIBCHASH - START(DATA_GOOGLELIBCHASH) { - unsigned key = SEARCH[i] + DELTA; - HTItem* ptr; - ptr = HashFind(googlelibhash, key); - if (ptr) - abort(); - } STOP(); -#endif - -#ifdef USE_GOOGLEDENSEHASH - START(DATA_GOOGLEDENSEHASH) { - unsigned key = SEARCH[i] + DELTA; - googledensehash_t::const_iterator ptr = googledensehash->find(key); - if (ptr != googledensehash->end()) - abort(); - } STOP(); -#endif - -#ifdef USE_GOOGLEBTREE - START(DATA_GOOGLEBTREE) { - unsigned key = SEARCH[i] + DELTA; - googlebtree_t::const_iterator ptr = googlebtree->find(key); - if (ptr != googlebtree->end()) - abort(); - } STOP(); -#endif - -#ifdef USE_STXBTREE - START(DATA_STXBTREE) { - unsigned key = SEARCH[i] + DELTA; - stxbtree_t::const_iterator ptr = stxbtree->find(key); - if (ptr != stxbtree->end()) - abort(); - } STOP(); -#endif - - -#ifdef USE_CPPMAP - START(DATA_CPPMAP) { - unsigned key = SEARCH[i] + DELTA; - cppmap_t::const_iterator ptr = cppmap->find(key); - if (ptr != cppmap->end()) - abort(); - } STOP(); -#endif - -#ifdef USE_CPPUNORDEREDMAP - START(DATA_CPPUNORDEREDMAP) { - unsigned key = SEARCH[i] + DELTA; - cppunorderedmap_t::const_iterator ptr = cppunorderedmap->find(key); - if (ptr != cppunorderedmap->end()) - abort(); - } STOP(); -#endif - - START(DATA_UTHASH) { - unsigned key = SEARCH[i] + DELTA; - struct uthash_object* obj; - HASH_FIND_INT(uthash, &key, obj); - if (obj) - abort(); - } STOP(); - - START(DATA_NEDTRIE) { - unsigned key = SEARCH[i] + DELTA; - struct nedtrie_object key_obj; - struct nedtrie_object* obj; - key_obj.value = key; - obj = NEDTRIE_FIND(nedtrie_t, &nedtrie, &key_obj); - if (obj) - abort(); - } STOP(); - -#ifdef USE_JUDY - START(DATA_JUDY) { - Word_t key = SEARCH[i] + DELTA; - Pvoid_t PValue; - JLG(PValue, judy, key); - if (PValue) - abort(); - } STOP(); -#endif - -#ifdef USE_JUDYARRAY - START(DATA_JUDYARRAY) { - judyvalue key = SEARCH[i] + DELTA; - JudySlot* pvalue; - pvalue = judy_slot(judyarray, (uchar*)&key, 0); - if (pvalue) { - /* workaround for a judyarray bug. Sometimes it returns a pvalue pointing to NULL */ - void* obj = *(void**)pvalue; - if (obj) - abort(); - } - } STOP(); -#endif - -#ifdef USE_CUBE - START(DATA_CUBE) { - unsigned key = SEARCH[i] + DELTA; - struct cube_obj* obj; - obj = (struct cube_obj*)get_key(cube, key); - if (obj) - abort(); - } STOP(); -#endif - -#ifdef USE_CK - START(DATA_CK) { - unsigned key = SEARCH[i] + DELTA; - unsigned hash_key; - struct ck_object obj_key; - struct ck_object* obj; - obj_key.value = key; - hash_key = CK_HS_HASH(&ck, hs_hash, &obj_key); - obj = (struct ck_object*)ck_hs_get(&ck, hash_key, &obj_key); - if (obj) - abort(); - } STOP(); -#endif -} - -void test_change(unsigned* REMOVE, unsigned* INSERT) -{ - unsigned i; - - const unsigned DELTA = 1; - - START(DATA_TREE) { - unsigned key = REMOVE[i]; - struct rbt_object key_obj; - struct rbt_object* obj; - key_obj.value = REMOVE[i]; - obj = rbt_search(&tree, &key_obj); - if (!obj) - abort(); - rbt_remove(&tree, obj); - - key = INSERT[i] + DELTA; - obj->value = key; - rbt_insert(&tree, obj); - } STOP(); - - START(DATA_HASHTABLE) { - unsigned key = REMOVE[i]; - unsigned hash_key = hash(key); - struct hashtable_object* obj; - obj = (struct hashtable_object*)tommy_hashtable_remove(&hashtable, tommy_hashtable_compare, &key, hash_key); - if (!obj) - abort(); - - key = INSERT[i] + DELTA; - hash_key = hash(key); - obj->value = key; - tommy_hashtable_insert(&hashtable, &obj->node, obj, hash_key); - } STOP(); - - START(DATA_HASHDYN) { - unsigned key = REMOVE[i]; - unsigned hash_key = hash(key); - struct hashtable_object* obj; - obj = (struct hashtable_object*)tommy_hashdyn_remove(&hashdyn, tommy_hashtable_compare, &key, hash_key); - if (!obj) - abort(); - - key = INSERT[i] + DELTA; - hash_key = hash(key); - obj->value = key; - tommy_hashdyn_insert(&hashdyn, &obj->node, obj, hash_key); - } STOP(); - - START(DATA_HASHLIN) { - unsigned key = REMOVE[i]; - unsigned hash_key = hash(key); - struct hashtable_object* obj; - obj = (struct hashtable_object*)tommy_hashlin_remove(&hashlin, tommy_hashtable_compare, &key, hash_key); - if (!obj) - abort(); - - key = INSERT[i] + DELTA; - hash_key = hash(key); - obj->value = key; - tommy_hashlin_insert(&hashlin, &obj->node, obj, hash_key); - } STOP(); - - START(DATA_TRIE) { - unsigned key = REMOVE[i]; - struct trie_object* obj; - obj = (struct trie_object*)tommy_trie_remove(&trie, key); - if (!obj) - abort(); - - key = INSERT[i] + DELTA; - obj->value = key; - tommy_trie_insert(&trie, &obj->node, obj, key); - } STOP(); - - START(DATA_TRIE_INPLACE) { - unsigned key = REMOVE[i]; - struct trie_inplace_object* obj; - obj = (struct trie_inplace_object*)tommy_trie_inplace_remove(&trie_inplace, key); - if (!obj) - abort(); - - key = INSERT[i] + DELTA; - obj->value = key; - tommy_trie_inplace_insert(&trie_inplace, &obj->node, obj, key); - } STOP(); - - START(DATA_KHASH) { - unsigned key = REMOVE[i]; - unsigned hash_key = hash(key); - khiter_t k; - int r; - struct khash_object* obj; - k = kh_get(word, khash, hash_key); - if (k == kh_end(khash)) - abort(); - obj = kh_value(khash, k); - kh_del(word, khash, k); - - key = INSERT[i] + DELTA; - hash_key = hash(key); - obj->value = key; - k = kh_put(word, khash, hash_key, &r); - if (!r) - abort(); - kh_value(khash, k) = obj; - } STOP(); - -#ifdef USE_GOOGLELIBCHASH - START(DATA_GOOGLELIBCHASH) { - unsigned key = REMOVE[i]; - HTItem* ptr; - struct google_object* obj; - u_long ptr_value; - ptr = HashFind(googlelibhash, key); - if (!ptr) - abort(); - obj = (void*)ptr->data; - HashDeleteLast(googlelibhash); - - key = INSERT[i] + DELTA; - obj->value = key; - ptr_value = (u_long)obj; - ptr = HashInsert(googlelibhash, key, ptr_value); - if (!ptr) - abort(); - } STOP(); -#endif - -#ifdef USE_GOOGLEDENSEHASH - START(DATA_GOOGLEDENSEHASH) { - unsigned key = REMOVE[i]; - googledensehash_t::iterator ptr = googledensehash->find(key); - struct google_object* obj; - if (ptr == googledensehash->end()) - abort(); - obj = ptr->second; - googledensehash->erase(ptr); - - key = INSERT[i] + DELTA; - obj->value = key; - (*googledensehash)[key] = obj; - } STOP(); -#endif - -#ifdef USE_GOOGLEBTREE - START(DATA_GOOGLEBTREE) { - unsigned key = REMOVE[i]; - googlebtree_t::iterator ptr = googlebtree->find(key); - struct google_object* obj; - if (ptr == googlebtree->end()) - abort(); - obj = ptr->second; - googlebtree->erase(ptr); - - key = INSERT[i] + DELTA; - obj->value = key; - (*googlebtree)[key] = obj; - } STOP(); -#endif - -#ifdef USE_STXBTREE - START(DATA_STXBTREE) { - unsigned key = REMOVE[i]; - stxbtree_t::iterator ptr = stxbtree->find(key); - struct stx_object* obj; - if (ptr == stxbtree->end()) - abort(); - obj = ptr->second; - stxbtree->erase(ptr); - - key = INSERT[i] + DELTA; - obj->value = key; - (*stxbtree)[key] = obj; - } STOP(); -#endif - -#ifdef USE_CPPMAP - START(DATA_CPPMAP) { - unsigned key = REMOVE[i]; - cppmap_t::iterator ptr = cppmap->find(key); - struct cpp_object* obj; - if (ptr == cppmap->end()) - abort(); - obj = ptr->second; - cppmap->erase(ptr); - - key = INSERT[i] + DELTA; - obj->value = key; - (*cppmap)[key] = obj; - } STOP(); -#endif - -#ifdef USE_CPPUNORDEREDMAP - START(DATA_CPPUNORDEREDMAP) { - unsigned key = REMOVE[i]; - cppunorderedmap_t::iterator ptr = cppunorderedmap->find(key); - struct cpp_object* obj; - if (ptr == cppunorderedmap->end()) - abort(); - obj = ptr->second; - cppunorderedmap->erase(ptr); - - key = INSERT[i] + DELTA; - obj->value = key; - (*cppunorderedmap)[key] = obj; - } STOP(); -#endif - - START(DATA_UTHASH) { - unsigned key = REMOVE[i]; - struct uthash_object* obj; - HASH_FIND_INT(uthash, &key, obj); - if (!obj) - abort(); - HASH_DEL(uthash, obj); - - key = INSERT[i] + DELTA; - obj->value = key; - HASH_ADD_INT(uthash, value, obj); - } STOP(); - - START(DATA_NEDTRIE) { - unsigned key = REMOVE[i]; - struct nedtrie_object key_obj; - struct nedtrie_object* obj; - key_obj.value = key; - obj = NEDTRIE_FIND(nedtrie_t, &nedtrie, &key_obj); - if (!obj) - abort(); - NEDTRIE_REMOVE(nedtrie_t, &nedtrie, obj); - - key = INSERT[i] + DELTA; - obj->value = key; - NEDTRIE_INSERT(nedtrie_t, &nedtrie, obj); - } STOP(); - -#ifdef USE_JUDY - START(DATA_JUDY) { - Word_t key = REMOVE[i]; - struct judy_object* obj; - int r; - Pvoid_t PValue; - JLG(PValue, judy, key); - if (!PValue) - abort(); - obj = *(struct judy_object**)PValue; - JLD(r, judy, key); - if (r != 1) - abort(); - - key = INSERT[i] + DELTA; - obj->value = key; - JLI(PValue, judy, key); - *(struct judy_object**)PValue = obj; - } STOP(); -#endif - -#ifdef USE_JUDYARRAY - START(DATA_JUDYARRAY) { - judyvalue key = REMOVE[i]; - struct judyarray_object* obj; - JudySlot* pvalue; - pvalue = judy_slot(judyarray, (uchar*)&key, 0); - if (!pvalue) - abort(); - obj = *(struct judyarray_object**)pvalue; - judy_del(judyarray); - - key = INSERT[i] + DELTA; - obj->value = key; - pvalue = judy_cell(judyarray, (uchar*)&key, 0); - *(struct judyarray_object**)pvalue = obj; - } STOP(); -#endif - -#ifdef USE_CUBE - START(DATA_CUBE) { - unsigned key = REMOVE[i]; - struct cube_object* obj; - obj = (struct cube_object*)del_key(cube, key); - if (!obj) - abort(); - - key = INSERT[i] + DELTA; - obj->value = key; - set_key(cube, key, obj); - } STOP(); -#endif - -#ifdef USE_CK - START(DATA_CK) { - unsigned key = REMOVE[i]; - unsigned hash_key; - struct ck_object obj_key; - struct ck_object* obj; - obj_key.value = key; - hash_key = CK_HS_HASH(&ck, hs_hash, &obj_key); - obj = (struct ck_object*)ck_hs_remove(&ck, hash_key, &obj_key); - if (!obj) - abort(); - - key = INSERT[i] + DELTA; - obj->value = key; - hash_key = CK_HS_HASH(&ck, hs_hash, obj); - ck_hs_put(&ck, hash_key, obj); - } STOP(); -#endif -} - -void test_remove(unsigned* REMOVE) -{ - unsigned i; - - /* always dereference the object deleted. It has cache effect. */ - /* considering we are dealing with objects, it makes sense to simulate an access to it */ - /* even on deletion, because you have at least to do a free() call. */ - /* this favorites data structures that store part of the information in the object itself */ - const int dereference = 1; - - const unsigned DELTA = 1; - - START(DATA_TREE) { - unsigned key = REMOVE[i] + DELTA; - struct rbt_object key_obj; - struct rbt_object* obj; - key_obj.value = key; - obj = rbt_search(&tree, &key_obj); - if (!obj) - abort(); - if (dereference) { - if (obj->value != key) - abort(); - } - rbt_remove(&tree, obj); - } STOP(); - - START(DATA_HASHTABLE) { - unsigned key = REMOVE[i] + DELTA; - unsigned hash_key = hash(key); - struct hashtable_object* obj; - obj = (struct hashtable_object*)tommy_hashtable_remove(&hashtable, tommy_hashtable_compare, &key, hash_key); - if (!obj) - abort(); - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); - - START(DATA_HASHDYN) { - unsigned key = REMOVE[i] + DELTA; - unsigned hash_key = hash(key); - struct hashtable_object* obj; - obj = (struct hashtable_object*)tommy_hashdyn_remove(&hashdyn, tommy_hashtable_compare, &key, hash_key); - if (!obj) - abort(); - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); - - START(DATA_HASHLIN) { - unsigned key = REMOVE[i] + DELTA; - unsigned hash_key = hash(key); - struct hashtable_object* obj; - obj = (struct hashtable_object*)tommy_hashlin_remove(&hashlin, tommy_hashtable_compare, &key, hash_key); - if (!obj) - abort(); - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); - - START(DATA_TRIE) { - unsigned key = REMOVE[i] + DELTA; - struct trie_object* obj; - obj = (struct trie_object*)tommy_trie_remove(&trie, key); - if (!obj) - abort(); - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); - - START(DATA_TRIE_INPLACE) { - unsigned key = REMOVE[i] + DELTA; - struct trie_inplace_object* obj; - obj = (struct trie_inplace_object*)tommy_trie_inplace_remove(&trie_inplace, key); - if (!obj) - abort(); - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); - - START(DATA_KHASH) { - unsigned key = REMOVE[i] + DELTA; - unsigned hash_key = hash(key); - struct khash_object* obj; - khiter_t k; - k = kh_get(word, khash, hash_key); - if (k == kh_end(khash)) - abort(); - if (dereference) { - obj = kh_value(khash, k); - } - kh_del(word, khash, k); - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); - -#ifdef USE_GOOGLELIBCHASH - START(DATA_GOOGLELIBCHASH) { - unsigned key = REMOVE[i] + DELTA; - HTItem* ptr; - struct google_object* obj; - ptr = HashFind(googlelibhash, key); - if (!ptr) - abort(); - obj = (struct google_object*)ptr->data; - HashDeleteLast(googlelibhash); - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); -#endif - -#ifdef USE_GOOGLEDENSEHASH - START(DATA_GOOGLEDENSEHASH) { - unsigned key = REMOVE[i] + DELTA; - struct google_object* obj; - googledensehash_t::iterator ptr = googledensehash->find(key); - if (ptr == googledensehash->end()) - abort(); - obj = ptr->second; - googledensehash->erase(ptr); - - /* force a resize when we reach 20% load factor */ - googledensehash->resize(0); - - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); -#endif - -#ifdef USE_GOOGLEBTREE - START(DATA_GOOGLEBTREE) { - unsigned key = REMOVE[i] + DELTA; - struct google_object* obj; - googlebtree_t::iterator ptr = googlebtree->find(key); - if (ptr == googlebtree->end()) - abort(); - obj = ptr->second; - googlebtree->erase(ptr); - - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); -#endif - -#ifdef USE_STXBTREE - START(DATA_STXBTREE) { - unsigned key = REMOVE[i] + DELTA; - struct stx_object* obj; - stxbtree_t::iterator ptr = stxbtree->find(key); - if (ptr == stxbtree->end()) - abort(); - obj = ptr->second; - stxbtree->erase(ptr); - - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); -#endif - -#ifdef USE_CPPMAP - START(DATA_CPPMAP) { - unsigned key = REMOVE[i] + DELTA; - struct cpp_object* obj; - cppmap_t::iterator ptr = cppmap->find(key); - if (ptr == cppmap->end()) - abort(); - obj = ptr->second; - cppmap->erase(ptr); - - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); -#endif - -#ifdef USE_CPPUNORDEREDMAP - START(DATA_CPPUNORDEREDMAP) { - unsigned key = REMOVE[i] + DELTA; - struct cpp_object* obj; - cppunorderedmap_t::iterator ptr = cppunorderedmap->find(key); - if (ptr == cppunorderedmap->end()) - abort(); - obj = ptr->second; - cppunorderedmap->erase(ptr); - - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); -#endif - - START(DATA_UTHASH) { - unsigned key = REMOVE[i] + DELTA; - struct uthash_object* obj; - HASH_FIND_INT(uthash, &key, obj); - if (!obj) - abort(); - HASH_DEL(uthash, obj); - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); - - START(DATA_NEDTRIE) { - unsigned key = REMOVE[i] + DELTA; - struct nedtrie_object key_obj; - struct nedtrie_object* obj; - key_obj.value = key; - obj = NEDTRIE_FIND(nedtrie_t, &nedtrie, &key_obj); - if (!obj) - abort(); - NEDTRIE_REMOVE(nedtrie_t, &nedtrie, obj); - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); - -#ifdef USE_JUDY - START(DATA_JUDY) { - Word_t key = REMOVE[i] + DELTA; - struct judy_object* obj; - int r; - if (dereference) { - Pvoid_t PValue; - JLG(PValue, judy, key); - if (!PValue) - abort(); - obj = *(struct judy_object**)PValue; - } - JLD(r, judy, key); - if (r != 1) - abort(); - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); -#endif - -#ifdef USE_JUDYARRAY - START(DATA_JUDYARRAY) { - judyvalue key = REMOVE[i] + DELTA; - struct judyarray_object* obj; - JudySlot* pvalue; - pvalue = judy_slot(judyarray, (uchar*)&key, 0); - if (!pvalue) - abort(); - obj = *(struct judyarray_object**)pvalue; - judy_del(judyarray); - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); -#endif - -#ifdef USE_CUBE - START(DATA_CUBE) { - unsigned key = REMOVE[i] + DELTA; - struct cube_object* obj; - obj = (struct cube_object*)del_key(cube, key); - if (!obj) - abort(); - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); -#endif - -#ifdef USE_CK - START(DATA_CK) { - unsigned key = REMOVE[i] + DELTA; - unsigned hash_key; - struct ck_object obj_key; - struct ck_object* obj; - obj_key.value = key; - hash_key = CK_HS_HASH(&ck, hs_hash, &obj_key); - obj = (struct ck_object*)ck_hs_remove(&ck, hash_key, &obj_key); - if (!obj) - abort(); - if (dereference) { - if (obj->value != key) - abort(); - } - } STOP(); -#endif -} - -tommy_size_t uthash_size(struct uthash_object* obj) -{ - UT_hash_table* table; - if (!obj) - return 0; - table = obj->hh.tbl; - if (!table) - return 0; - return table->num_buckets * (tommy_size_t)sizeof(table->buckets[0]) - + table->num_items * (tommy_size_t)sizeof(UT_hash_handle); -} - -tommy_size_t nedtrie_size(struct nedtrie_t* col) -{ - struct nedtrie_object element; - (void)element; - return col->count * sizeof(element.link); -} - -tommy_size_t khash_size(khash_t(word)* col) -{ - return col->n_buckets * sizeof(void*) /* val */ - + col->n_buckets * sizeof(uint32_t) /* key */ - + (col->n_buckets >> 4) * sizeof(uint32_t); /* flags */ -} - -#ifdef USE_GOOGLEDENSEHASH -tommy_size_t googledensehash_size(googledensehash_t* col) -{ - googledensehash_t::value_type element; - (void)element; - return col->bucket_count() * sizeof(element); -} -#endif - -#ifdef USE_GOOGLEBTREE -tommy_size_t googlebtree_size(googlebtree_t* col) -{ - return col->bytes_used(); -} -#endif - -#ifdef USE_STXBTREE -tommy_size_t stxbtree_size(stxbtree_t* col) -{ - return col->get_stats().leaves * sizeof(struct stxbtree_t::btree_impl::leaf_node) - + col->get_stats().innernodes * sizeof(struct stxbtree_t::btree_impl::inner_node); - return 0; -} -#endif - -tommy_size_t rbt_size(rbtree_t* col, unsigned count) -{ - struct rbt_object element; - (void)col; - (void)element; - return count * sizeof(element.link); -} - -void test_size(void) -{ -#ifdef USE_JUDY - Word_t w; -#endif - - MEM(DATA_TREE, rbt_size(&tree, the_max)); - MEM(DATA_HASHTABLE, tommy_hashtable_memory_usage(&hashtable)); - MEM(DATA_HASHDYN, tommy_hashdyn_memory_usage(&hashdyn)); - MEM(DATA_HASHLIN, tommy_hashlin_memory_usage(&hashlin)); - MEM(DATA_TRIE, tommy_trie_memory_usage(&trie)); - MEM(DATA_TRIE_INPLACE, tommy_trie_inplace_memory_usage(&trie_inplace)); - MEM(DATA_KHASH, khash_size(khash)); -#ifdef USE_GOOGLEDENSEHASH - MEM(DATA_GOOGLEDENSEHASH, googledensehash_size(googledensehash)); -#endif -#ifdef USE_GOOGLEBTREE - MEM(DATA_GOOGLEBTREE, googlebtree_size(googlebtree)); -#endif -#ifdef USE_STXBTREE - MEM(DATA_STXBTREE, stxbtree_size(stxbtree)); -#endif - MEM(DATA_UTHASH, uthash_size(uthash)); - MEM(DATA_NEDTRIE, nedtrie_size(&nedtrie)); -#ifdef USE_JUDY - JLMU(w, judy); - MEM(DATA_JUDY, w); -#endif -#ifdef USE_JUDYARRAY - MEM(DATA_JUDYARRAY,judy_size(judyarray)); -#endif -#ifdef USE_CUBE - MEM(DATA_CUBE, size_cube(cube)); -#endif -} - -void test_operation(unsigned* INSERT, unsigned* SEARCH) -{ - cache_clear(); - - OPERATION(OPERATION_INSERT); - test_insert(INSERT); - - OPERATION(OPERATION_CHANGE); - test_change(SEARCH, INSERT); - - OPERATION(OPERATION_HIT); - test_hit(SEARCH); - - OPERATION(OPERATION_MISS); - test_miss(SEARCH); - - OPERATION(OPERATION_SIZE); - test_size(); - - OPERATION(OPERATION_REMOVE); - test_remove(SEARCH); -} - -void test(unsigned size, unsigned data, int log, int sparse) -{ - double base; - double fact; - - base = 1000; - fact = pow(10, 0.1); - - /* log if batch test or requested */ - the_log = (size == 0 && data == DATA_MAX) || log; - - /* write the header */ - if (the_log) - for(the_order=0;the_order RETRY_MAX) - retry = RETRY_MAX; - if (size != 0) - retry = 1; - - /* clear the log */ - memset(LOG, 0, sizeof(LOG)); - - order_init(the_max, sparse); - - /* run the test */ - for(the_retry=0;the_retry TIME_MAX_NS) { - printf(" (skipped, too slow)\n"); - continue; - } - - if (!the_log) - printf("\n"); - - test_alloc(); - if (the_order == ORDER_FORWARD) - test_operation(FORWARD, FORWARD); - else - test_operation(RAND0, RAND1); - test_free(); - - if (the_log) { - for(i=0;i LAST[the_data][the_order]) { - LAST[the_data][the_order] = v; - } - } - - fprintf(f, "%u\t", v); - } - - fprintf(f, "\n"); - fclose(f); - } - } - - if (size != 0) - break; - - /* new max */ - base *= fact; - the_max = (unsigned)base; - } -} - -void help(void) -{ - printf("Options\n"); - printf("-n NUMBER Run the test for the specified number of objects.\n"); - printf("-m Run the test for the maximum number of objects.\n"); - printf("-d DATA Run the test for the specified data structure.\n"); - printf("-s Use a sparse dataset intead of a compact one.\n"); - printf("-l Logs results into file for graphs creation.\n"); -} - -int main(int argc, char * argv[]) -{ - int i; - int flag_data = DATA_MAX; - int flag_size = 0; - int flag_log = 0; - int flag_sparse = 0; - - nano_init(); - - printf("Tommy benchmark program.\n"); - - for(i=1;i= argc) { - printf("Missing number of objects in %s\n", argv[i]); - exit(EXIT_FAILURE); - } - flag_size = atoi(argv[i+1]); - ++i; - } else if (strcmp(argv[i], "-d") == 0) { - int j; - if (i+1 >= argc) { - printf("Missing data in %s\n", argv[i]); - exit(EXIT_FAILURE); - } - flag_data = DATA_MAX; - for(j=0;j - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {E77E7CFB-E02E-4F25-ADAA-1CF587DB0A04} - benchmark - Win32Proj - - - - Application - MultiByte - true - v110 - - - Application - MultiByte - v110 - - - Application - MultiByte - true - v110 - - - Application - MultiByte - v110 - - - - - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.30319.1 - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - true - $(SolutionDir)$(Platform)\$(Configuration)\ - $(Platform)\$(Configuration)\ - true - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - false - $(SolutionDir)$(Platform)\$(Configuration)\ - $(Platform)\$(Configuration)\ - false - - - - Disabled - benchmark/lib;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebugDLL - - - Level3 - EditAndContinue - - - $(OutDir)tommybench32.exe - true - Console - MachineX86 - - - - - X64 - - - Disabled - benchmark/lib;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;JU_WIN;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebugDLL - - - Level3 - ProgramDatabase - - - $(OutDir)tommybench64.exe - true - Console - MachineX64 - - - - - Full - true - benchmark/lib;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - MultiThreaded - true - NotUsing - Level3 - ProgramDatabase - Sync - All - StreamingSIMDExtensions2 - false - Fast - - - $(OutDir)tommybench32.exe - true - Console - true - true - MachineX86 - - - - - X64 - - - Full - true - benchmark/lib;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - MultiThreaded - true - NotUsing - Level3 - ProgramDatabase - Sync - All - StreamingSIMDExtensions2 - false - Fast - - - $(OutDir)tommybench64.exe - true - Console - true - true - MachineX64 - - - - - - - - - - - - \ No newline at end of file diff --git a/tommyds/benchmark/gr_all.bat b/tommyds/benchmark/gr_all.bat deleted file mode 100644 index c96d2b9..0000000 --- a/tommyds/benchmark/gr_all.bat +++ /dev/null @@ -1,54 +0,0 @@ -rem Set it to a GNUPLOT 4.4 binary -rem In case download it from http://sourceforge.net/projects/gnuplot/files/gnuplot/4.4.2/gp442win32.zip/download -set GNUPLOT=..\contrib\gnuplot\binary\gnuplot.exe - -%GNUPLOT% gr_def_random_hit.gnu -%GNUPLOT% gr_def_random_change.gnu -%GNUPLOT% gr_other_judy_problem.gnu -%GNUPLOT% gr_other_slow_problem.gnu - -DIR=data\core_i5_650_3G2_linux -%GNUPLOT% %DIR%\gr_def.gnu gr_forward_insert.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_forward_hit.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_forward_insert.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_forward_miss.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_forward_change.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_forward_remove.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_forward_size.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_random_hit.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_random_insert.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_random_miss.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_random_change.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_random_remove.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_random_size.gnu - -DIR=data\core_i7_3740_2G7_win -%GNUPLOT% %DIR%\gr_def.gnu gr_forward_insert.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_forward_hit.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_forward_insert.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_forward_miss.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_forward_change.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_forward_remove.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_forward_size.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_random_hit.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_random_insert.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_random_miss.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_random_change.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_random_remove.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_random_size.gnu - -DIR=data\test -%GNUPLOT% %DIR%\gr_def.gnu gr_forward_insert.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_forward_hit.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_forward_insert.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_forward_miss.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_forward_change.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_forward_remove.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_forward_size.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_random_hit.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_random_insert.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_random_miss.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_random_change.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_random_remove.gnu -%GNUPLOT% %DIR%\gr_def.gnu gr_random_size.gnu - diff --git a/tommyds/benchmark/gr_all.sh b/tommyds/benchmark/gr_all.sh deleted file mode 100644 index eb62729..0000000 --- a/tommyds/benchmark/gr_all.sh +++ /dev/null @@ -1,71 +0,0 @@ -echo Ensure to use GNUPLOT 4.4 - -export GDFONTPATH=. -export GNUPLOT_DEFAULT_GDFONT=arial - -gnuplot gr_def_random_hit.gnu -gnuplot gr_def_random_change.gnu -gnuplot gr_other_judy_problem.gnu -gnuplot gr_other_googlelibchash_problem.gnu -gnuplot gr_other_ck_problem.gnu - -DIR=data/core_i5_650_3G2_linux -gnuplot $DIR/gr_def.gnu gr_forward_insert.gnu -gnuplot $DIR/gr_def.gnu gr_forward_hit.gnu -gnuplot $DIR/gr_def.gnu gr_forward_insert.gnu -gnuplot $DIR/gr_def.gnu gr_forward_miss.gnu -gnuplot $DIR/gr_def.gnu gr_forward_change.gnu -gnuplot $DIR/gr_def.gnu gr_forward_remove.gnu -gnuplot $DIR/gr_def.gnu gr_forward_size.gnu -gnuplot $DIR/gr_def.gnu gr_random_hit.gnu -gnuplot $DIR/gr_def.gnu gr_random_insert.gnu -gnuplot $DIR/gr_def.gnu gr_random_miss.gnu -gnuplot $DIR/gr_def.gnu gr_random_change.gnu -gnuplot $DIR/gr_def.gnu gr_random_remove.gnu -gnuplot $DIR/gr_def.gnu gr_random_size.gnu - -DIR=data/core_i7_3740_2G7_linux -gnuplot $DIR/gr_def.gnu gr_forward_insert.gnu -gnuplot $DIR/gr_def.gnu gr_forward_hit.gnu -gnuplot $DIR/gr_def.gnu gr_forward_insert.gnu -gnuplot $DIR/gr_def.gnu gr_forward_miss.gnu -gnuplot $DIR/gr_def.gnu gr_forward_change.gnu -gnuplot $DIR/gr_def.gnu gr_forward_remove.gnu -gnuplot $DIR/gr_def.gnu gr_forward_size.gnu -gnuplot $DIR/gr_def.gnu gr_random_hit.gnu -gnuplot $DIR/gr_def.gnu gr_random_insert.gnu -gnuplot $DIR/gr_def.gnu gr_random_miss.gnu -gnuplot $DIR/gr_def.gnu gr_random_change.gnu -gnuplot $DIR/gr_def.gnu gr_random_remove.gnu -gnuplot $DIR/gr_def.gnu gr_random_size.gnu - -DIR=data/core_i7_3740_2G7_win -gnuplot $DIR/gr_def.gnu gr_forward_insert.gnu -gnuplot $DIR/gr_def.gnu gr_forward_hit.gnu -gnuplot $DIR/gr_def.gnu gr_forward_insert.gnu -gnuplot $DIR/gr_def.gnu gr_forward_miss.gnu -gnuplot $DIR/gr_def.gnu gr_forward_change.gnu -gnuplot $DIR/gr_def.gnu gr_forward_remove.gnu -gnuplot $DIR/gr_def.gnu gr_forward_size.gnu -gnuplot $DIR/gr_def.gnu gr_random_hit.gnu -gnuplot $DIR/gr_def.gnu gr_random_insert.gnu -gnuplot $DIR/gr_def.gnu gr_random_miss.gnu -gnuplot $DIR/gr_def.gnu gr_random_change.gnu -gnuplot $DIR/gr_def.gnu gr_random_remove.gnu -gnuplot $DIR/gr_def.gnu gr_random_size.gnu - -DIR=data/test -gnuplot $DIR/gr_def.gnu gr_forward_insert.gnu -gnuplot $DIR/gr_def.gnu gr_forward_hit.gnu -gnuplot $DIR/gr_def.gnu gr_forward_insert.gnu -gnuplot $DIR/gr_def.gnu gr_forward_miss.gnu -gnuplot $DIR/gr_def.gnu gr_forward_change.gnu -gnuplot $DIR/gr_def.gnu gr_forward_remove.gnu -gnuplot $DIR/gr_def.gnu gr_forward_size.gnu -gnuplot $DIR/gr_def.gnu gr_random_hit.gnu -gnuplot $DIR/gr_def.gnu gr_random_insert.gnu -gnuplot $DIR/gr_def.gnu gr_random_miss.gnu -gnuplot $DIR/gr_def.gnu gr_random_change.gnu -gnuplot $DIR/gr_def.gnu gr_random_remove.gnu -gnuplot $DIR/gr_def.gnu gr_random_size.gnu - diff --git a/tommyds/benchmark/gr_common.gnu b/tommyds/benchmark/gr_common.gnu deleted file mode 100644 index 9a46d83..0000000 --- a/tommyds/benchmark/gr_common.gnu +++ /dev/null @@ -1,39 +0,0 @@ -;set term svg enhanced font 'arial,10' size 800,800 -;bext = ".svg" -set terminal png nocrop enhanced font arial 10 size 800,800 -bext = ".png" -set xtics nomirror rotate by -45 font "arial,8" -set key below -set style data linespoints -set datafile missing "0" -set xlabel "Number of elements in logarithmic scale" -set ylabel "Time for element in nanosecond in logarithmic scale\nLower is better" -set xrange [1000:10000000] -set yrange [6:1000] -set logscale y -set logscale x -set format y "%.0fns" -set format x "%.0s%c" -bdir = "data/" - -# for some colors see: http://www.uni-hamburg.de/Wiss/FB/15/Sustainability/schneider/gnuplot/colors.htm -set style line 1 lc 1 lt 1 # hashtable -set style line 2 lc 2 lt 2 # hashdyn -set style line 3 lc 3 lt 3 # hashlin -set style line 4 lc 4 lt 4 # trie -set style line 5 lc 5 lt 5 # trie-inplace -set style line 6 lc 6 lt 6 # rbtre -set style line 7 lc 7 lt 7 # nedtrie -set style line 8 lc 8 lt 8 # khash -set style line 9 lc 9 lt 8 # uthash -set style line 10 lc 10 lt 10 # judy -set style line 11 lc 11 lt 11 # judyarray -set style line 12 lc 12 lt 12 # googledensehash -set style line 13 lc rgb "#FF69B4" lt 13 # googlebtree -set style line 14 lc 14 lt 14 # stxbtree -set style line 15 lc 15 lt 15 # c++unorderedmap -set style line 16 lc 16 lt 16 # c++map -set style line 17 lc 17 lt 17 # tesseract -set style line 18 lc rgb "#FF69B4" lt 18 # googlelibchash -set style line 19 lc rgb "#1E90FF" lt 19 # concurrencykit - diff --git a/tommyds/benchmark/gr_def_random_change.gnu b/tommyds/benchmark/gr_def_random_change.gnu deleted file mode 100644 index e204c26..0000000 --- a/tommyds/benchmark/gr_def_random_change.gnu +++ /dev/null @@ -1,12 +0,0 @@ -load "gr_common.gnu" - -tdir = "def/" -tsub = "\nCore i5 650 3.20 GHz, 4 MB L3 cache, 2400 Uncore Speed\nLinux, gcc 4.9.2, 32 bit" - -set output bdir.tdir."img_random_change".bext -set title "Random Change (Remove + Insert)".tsub -data = bdir.tdir.'dat_random_change.lst' - -plot data using 1:2 title columnheader(2), \ - for [i=3:20] '' using 1:i title columnheader(i) ls i-1 - diff --git a/tommyds/benchmark/gr_def_random_hit.gnu b/tommyds/benchmark/gr_def_random_hit.gnu deleted file mode 100644 index f6ea0f0..0000000 --- a/tommyds/benchmark/gr_def_random_hit.gnu +++ /dev/null @@ -1,12 +0,0 @@ -load "gr_common.gnu" - -tdir = "def/" -tsub = "\nCore i5 650 3.20 GHz, 4 MB L3 cache, 2400 Uncore Speed\nLinux, gcc 4.9.2, 32 bit" - -set output bdir.tdir."img_random_hit".bext -set title "Random Hit".tsub -data = bdir.tdir.'dat_random_hit.lst' - -plot data using 1:2 title columnheader(2), \ - for [i=3:20] '' using 1:i title columnheader(i) ls i-1 - diff --git a/tommyds/benchmark/gr_forward_change.gnu b/tommyds/benchmark/gr_forward_change.gnu deleted file mode 100644 index 06d35b8..0000000 --- a/tommyds/benchmark/gr_forward_change.gnu +++ /dev/null @@ -1,9 +0,0 @@ -load "gr_common.gnu" - -set output bdir.tdir."img_forward_change".bext -set title "Forward Change (Remove + Insert)".tsub -data = bdir.tdir.'dat_forward_change.lst' - -plot data using 1:2 title columnheader(2), \ - for [i=3:20] '' using 1:i title columnheader(i) ls i-1 - diff --git a/tommyds/benchmark/gr_forward_hit.gnu b/tommyds/benchmark/gr_forward_hit.gnu deleted file mode 100644 index 22d10f4..0000000 --- a/tommyds/benchmark/gr_forward_hit.gnu +++ /dev/null @@ -1,9 +0,0 @@ -load "gr_common.gnu" - -set output bdir.tdir."img_forward_hit".bext -set title "Forward Hit".tsub -data = bdir.tdir.'dat_forward_hit.lst' - -plot data using 1:2 title columnheader(2), \ - for [i=3:20] '' using 1:i title columnheader(i) ls i-1 - diff --git a/tommyds/benchmark/gr_forward_insert.gnu b/tommyds/benchmark/gr_forward_insert.gnu deleted file mode 100644 index 55be118..0000000 --- a/tommyds/benchmark/gr_forward_insert.gnu +++ /dev/null @@ -1,9 +0,0 @@ -load "gr_common.gnu" - -set output bdir.tdir."img_forward_insert".bext -set title "Forward Insert".tsub -data = bdir.tdir.'dat_forward_insert.lst' - -plot data using 1:2 title columnheader(2), \ - for [i=3:20] '' using 1:i title columnheader(i) ls i-1 - diff --git a/tommyds/benchmark/gr_forward_miss.gnu b/tommyds/benchmark/gr_forward_miss.gnu deleted file mode 100644 index 8f48910..0000000 --- a/tommyds/benchmark/gr_forward_miss.gnu +++ /dev/null @@ -1,9 +0,0 @@ -load "gr_common.gnu" - -set output bdir.tdir."img_forward_miss".bext -set title "Forward Miss".tsub -data = bdir.tdir.'dat_forward_miss.lst' - -plot data using 1:2 title columnheader(2), \ - for [i=3:20] '' using 1:i title columnheader(i) ls i-1 - diff --git a/tommyds/benchmark/gr_forward_remove.gnu b/tommyds/benchmark/gr_forward_remove.gnu deleted file mode 100644 index 694bffe..0000000 --- a/tommyds/benchmark/gr_forward_remove.gnu +++ /dev/null @@ -1,9 +0,0 @@ -load "gr_common.gnu" - -set output bdir.tdir."img_forward_remove".bext -set title "Forward Remove".tsub -data = bdir.tdir.'dat_forward_remove.lst' - -plot data using 1:2 title columnheader(2), \ - for [i=3:20] '' using 1:i title columnheader(i) ls i-1 - diff --git a/tommyds/benchmark/gr_forward_size.gnu b/tommyds/benchmark/gr_forward_size.gnu deleted file mode 100644 index 3f1b2e7..0000000 --- a/tommyds/benchmark/gr_forward_size.gnu +++ /dev/null @@ -1,13 +0,0 @@ -load "gr_common.gnu" - -set output bdir.tdir."img_forward_size".bext -set title "Size".tsub -set format y "%.0f" -set ylabel "Size for element in byte\nLower is better" -unset logscale y -set yrange [0:80] -data = bdir.tdir.'dat_forward_size.lst' - -plot data using 1:2 title columnheader(2), \ - for [i=3:20] '' using 1:i title columnheader(i) ls i-1 - diff --git a/tommyds/benchmark/gr_other_ck_problem.gnu b/tommyds/benchmark/gr_other_ck_problem.gnu deleted file mode 100644 index 4612a3e..0000000 --- a/tommyds/benchmark/gr_other_ck_problem.gnu +++ /dev/null @@ -1,14 +0,0 @@ -load "gr_common.gnu" - -set yrange [10:10000] - -tdir = "other/" -tsub = "\nCore i5 650 3.20 GHz, 4 MB L3 cache\nLinux, gcc 4.7.1, 32 bit" - -set output bdir.tdir."ck_problem".bext -set title "Random Change (Remove + Insert)".tsub -data = bdir.tdir.'ck_problem.lst' - -plot data using 1:2 title columnheader(2), \ - for [i=3:20] '' using 1:i title columnheader(i) ls i-1 - diff --git a/tommyds/benchmark/gr_other_googlelibchash_problem.gnu b/tommyds/benchmark/gr_other_googlelibchash_problem.gnu deleted file mode 100644 index 11616c7..0000000 --- a/tommyds/benchmark/gr_other_googlelibchash_problem.gnu +++ /dev/null @@ -1,14 +0,0 @@ -load "gr_common.gnu" - -set yrange [10:10000] - -tdir = "other/" -tsub = "\nCore i5 650 3.20 GHz, 4 MB L3 cache\nLinux, gcc 4.7.1, 32 bit" - -set output bdir.tdir."googlelibchash_problem".bext -set title "Random Change (Remove + Insert)".tsub -data = bdir.tdir.'googlelibchash_problem.lst' - -plot data using 1:2 title columnheader(2), \ - for [i=3:20] '' using 1:i title columnheader(i) ls i-1 - diff --git a/tommyds/benchmark/gr_other_judy_problem.gnu b/tommyds/benchmark/gr_other_judy_problem.gnu deleted file mode 100644 index 2adcaf9..0000000 --- a/tommyds/benchmark/gr_other_judy_problem.gnu +++ /dev/null @@ -1,14 +0,0 @@ -load "gr_common.gnu" - -set yrange [10:10000] - -tdir = "other/" -tsub = "\nXeon E5430 2.66 GHz, 2x6 MB L2 cache, 1333 MT/s FSB\nWindows, Visual C 2008, 32 bit" - -set output bdir.tdir."judy_problem".bext -set title "Forward Change (Remove + Insert)".tsub -data = bdir.tdir.'judy_problem.lst' - -plot data using 1:2 title columnheader(2), \ - for [i=3:20] '' using 1:i title columnheader(i) ls i-1 - diff --git a/tommyds/benchmark/gr_random_change.gnu b/tommyds/benchmark/gr_random_change.gnu deleted file mode 100644 index 391e015..0000000 --- a/tommyds/benchmark/gr_random_change.gnu +++ /dev/null @@ -1,9 +0,0 @@ -load "gr_common.gnu" - -set output bdir.tdir."img_random_change".bext -set title "Random Change (Remove + Insert)".tsub -data = bdir.tdir.'dat_random_change.lst' - -plot data using 1:2 title columnheader(2), \ - for [i=3:20] '' using 1:i title columnheader(i) ls i-1 - diff --git a/tommyds/benchmark/gr_random_hit.gnu b/tommyds/benchmark/gr_random_hit.gnu deleted file mode 100644 index 2b7c40f..0000000 --- a/tommyds/benchmark/gr_random_hit.gnu +++ /dev/null @@ -1,9 +0,0 @@ -load "gr_common.gnu" - -set output bdir.tdir."img_random_hit".bext -set title "Random Hit".tsub -data = bdir.tdir.'dat_random_hit.lst' - -plot data using 1:2 title columnheader(2), \ - for [i=3:20] '' using 1:i title columnheader(i) ls i-1 - diff --git a/tommyds/benchmark/gr_random_insert.gnu b/tommyds/benchmark/gr_random_insert.gnu deleted file mode 100644 index 32b4d4d..0000000 --- a/tommyds/benchmark/gr_random_insert.gnu +++ /dev/null @@ -1,9 +0,0 @@ -load "gr_common.gnu" - -set output bdir.tdir."img_random_insert".bext -set title "Random Insert".tsub -data = bdir.tdir.'dat_random_insert.lst' - -plot data using 1:2 title columnheader(2), \ - for [i=3:20] '' using 1:i title columnheader(i) ls i-1 - diff --git a/tommyds/benchmark/gr_random_miss.gnu b/tommyds/benchmark/gr_random_miss.gnu deleted file mode 100644 index f613322..0000000 --- a/tommyds/benchmark/gr_random_miss.gnu +++ /dev/null @@ -1,9 +0,0 @@ -load "gr_common.gnu" - -set output bdir.tdir."img_random_miss".bext -set title "Random Miss".tsub -data = bdir.tdir.'dat_random_miss.lst' - -plot data using 1:2 title columnheader(2), \ - for [i=3:20] '' using 1:i title columnheader(i) ls i-1 - diff --git a/tommyds/benchmark/gr_random_remove.gnu b/tommyds/benchmark/gr_random_remove.gnu deleted file mode 100644 index 9cc801c..0000000 --- a/tommyds/benchmark/gr_random_remove.gnu +++ /dev/null @@ -1,9 +0,0 @@ -load "gr_common.gnu" - -set output bdir.tdir."img_random_remove".bext -set title "Random Remove".tsub -data = bdir.tdir.'dat_random_remove.lst' - -plot data using 1:2 title columnheader(2), \ - for [i=3:20] '' using 1:i title columnheader(i) ls i-1 - diff --git a/tommyds/benchmark/gr_random_size.gnu b/tommyds/benchmark/gr_random_size.gnu deleted file mode 100644 index 026cd54..0000000 --- a/tommyds/benchmark/gr_random_size.gnu +++ /dev/null @@ -1,13 +0,0 @@ -load "gr_common.gnu" - -set output bdir.tdir."img_random_size".bext -set title "Size".tsub -set format y "%.0f" -set ylabel "Size for element in byte\nLower is better" -unset logscale y -set yrange [0:80] -data = bdir.tdir.'dat_random_size.lst' - -plot data using 1:2 title columnheader(2), \ - for [i=3:20] '' using 1:i title columnheader(i) ls i-1 - diff --git a/tommyds/benchmark/lib/cpp-btree/CMakeLists.txt b/tommyds/benchmark/lib/cpp-btree/CMakeLists.txt deleted file mode 100644 index 3c49ad2..0000000 --- a/tommyds/benchmark/lib/cpp-btree/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright 2013 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -cmake_minimum_required(VERSION 2.6) - -project(cppbtree CXX) - -option(build_tests "Build B-tree tests" OFF) -add_definitions(-std=c++11) -set(CMAKE_CXX_FLAGS "-g -O2") - -# CMake doesn't have a way to pure template library, -# add_library(cppbtree btree.h btree_map.h btree_set.h -# safe_btree.h safe_btree_map.h safe_btree_set.h) -# set_target_properties(cppbtree PROPERTIES LINKER_LANGUAGE CXX) - -if(build_tests) - enable_testing() - include_directories($ENV{GTEST_ROOT}/include) - link_directories($ENV{GTEST_ROOT}) - add_executable(btree_test btree_test.cc btree_test_flags.cc) - add_executable(safe_btree_test safe_btree_test.cc btree_test_flags.cc) - add_executable(btree_bench btree_bench.cc btree_test_flags.cc) - target_link_libraries(btree_test gtest_main gtest gflags) - target_link_libraries(safe_btree_test gtest_main gtest gflags) - target_link_libraries(btree_bench gflags gtest) -endif() diff --git a/tommyds/benchmark/lib/cpp-btree/COPYING b/tommyds/benchmark/lib/cpp-btree/COPYING deleted file mode 100644 index d645695..0000000 --- a/tommyds/benchmark/lib/cpp-btree/COPYING +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/tommyds/benchmark/lib/cpp-btree/README b/tommyds/benchmark/lib/cpp-btree/README deleted file mode 100644 index 0e8674c..0000000 --- a/tommyds/benchmark/lib/cpp-btree/README +++ /dev/null @@ -1,20 +0,0 @@ -This library is a C++ template library and, as such, there is no -library to build and install. Copy the .h files and use them! - -See http://code.google.com/p/cpp-btree/wiki/UsageInstructions for -details. - ----- - -To build and run the provided tests, however, you will need to install -CMake, the Google C++ Test framework, and the Google flags package. - -Download and install CMake from http://www.cmake.org - -Download and build the GoogleTest framework from http://code.google.com/p/googletest - -Download and install gflags from https://code.google.com/p/gflags - -export GTEST_ROOT=/path/for/gtest-x.y - -cmake . -Dbuild_tests=ON diff --git a/tommyds/benchmark/lib/cpp-btree/btree.h b/tommyds/benchmark/lib/cpp-btree/btree.h deleted file mode 100644 index 2f70518..0000000 --- a/tommyds/benchmark/lib/cpp-btree/btree.h +++ /dev/null @@ -1,2396 +0,0 @@ -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// A btree implementation of the STL set and map interfaces. A btree is both -// smaller and faster than STL set/map. The red-black tree implementation of -// STL set/map has an overhead of 3 pointers (left, right and parent) plus the -// node color information for each stored value. So a set consumes 20 -// bytes for each value stored. This btree implementation stores multiple -// values on fixed size nodes (usually 256 bytes) and doesn't store child -// pointers for leaf nodes. The result is that a btree_set may use much -// less memory per stored value. For the random insertion benchmark in -// btree_test.cc, a btree_set with node-size of 256 uses 4.9 bytes per -// stored value. -// -// The packing of multiple values on to each node of a btree has another effect -// besides better space utilization: better cache locality due to fewer cache -// lines being accessed. Better cache locality translates into faster -// operations. -// -// CAVEATS -// -// Insertions and deletions on a btree can cause splitting, merging or -// rebalancing of btree nodes. And even without these operations, insertions -// and deletions on a btree will move values around within a node. In both -// cases, the result is that insertions and deletions can invalidate iterators -// pointing to values other than the one being inserted/deleted. This is -// notably different from STL set/map which takes care to not invalidate -// iterators on insert/erase except, of course, for iterators pointing to the -// value being erased. A partial workaround when erasing is available: -// erase() returns an iterator pointing to the item just after the one that was -// erased (or end() if none exists). See also safe_btree. - -// PERFORMANCE -// -// btree_bench --benchmarks=. 2>&1 | ./benchmarks.awk -// -// Run on pmattis-warp.nyc (4 X 2200 MHz CPUs); 2010/03/04-15:23:06 -// Benchmark STL(ns) B-Tree(ns) @ -// -------------------------------------------------------- -// BM_set_int32_insert 1516 608 +59.89% <256> [40.0, 5.2] -// BM_set_int32_lookup 1160 414 +64.31% <256> [40.0, 5.2] -// BM_set_int32_fulllookup 960 410 +57.29% <256> [40.0, 4.4] -// BM_set_int32_delete 1741 528 +69.67% <256> [40.0, 5.2] -// BM_set_int32_queueaddrem 3078 1046 +66.02% <256> [40.0, 5.5] -// BM_set_int32_mixedaddrem 3600 1384 +61.56% <256> [40.0, 5.3] -// BM_set_int32_fifo 227 113 +50.22% <256> [40.0, 4.4] -// BM_set_int32_fwditer 158 26 +83.54% <256> [40.0, 5.2] -// BM_map_int32_insert 1551 636 +58.99% <256> [48.0, 10.5] -// BM_map_int32_lookup 1200 508 +57.67% <256> [48.0, 10.5] -// BM_map_int32_fulllookup 989 487 +50.76% <256> [48.0, 8.8] -// BM_map_int32_delete 1794 628 +64.99% <256> [48.0, 10.5] -// BM_map_int32_queueaddrem 3189 1266 +60.30% <256> [48.0, 11.6] -// BM_map_int32_mixedaddrem 3822 1623 +57.54% <256> [48.0, 10.9] -// BM_map_int32_fifo 151 134 +11.26% <256> [48.0, 8.8] -// BM_map_int32_fwditer 161 32 +80.12% <256> [48.0, 10.5] -// BM_set_int64_insert 1546 636 +58.86% <256> [40.0, 10.5] -// BM_set_int64_lookup 1200 512 +57.33% <256> [40.0, 10.5] -// BM_set_int64_fulllookup 971 487 +49.85% <256> [40.0, 8.8] -// BM_set_int64_delete 1745 616 +64.70% <256> [40.0, 10.5] -// BM_set_int64_queueaddrem 3163 1195 +62.22% <256> [40.0, 11.6] -// BM_set_int64_mixedaddrem 3760 1564 +58.40% <256> [40.0, 10.9] -// BM_set_int64_fifo 146 103 +29.45% <256> [40.0, 8.8] -// BM_set_int64_fwditer 162 31 +80.86% <256> [40.0, 10.5] -// BM_map_int64_insert 1551 720 +53.58% <256> [48.0, 20.7] -// BM_map_int64_lookup 1214 612 +49.59% <256> [48.0, 20.7] -// BM_map_int64_fulllookup 994 592 +40.44% <256> [48.0, 17.2] -// BM_map_int64_delete 1778 764 +57.03% <256> [48.0, 20.7] -// BM_map_int64_queueaddrem 3189 1547 +51.49% <256> [48.0, 20.9] -// BM_map_int64_mixedaddrem 3779 1887 +50.07% <256> [48.0, 21.6] -// BM_map_int64_fifo 147 145 +1.36% <256> [48.0, 17.2] -// BM_map_int64_fwditer 162 41 +74.69% <256> [48.0, 20.7] -// BM_set_string_insert 1989 1966 +1.16% <256> [64.0, 44.5] -// BM_set_string_lookup 1709 1600 +6.38% <256> [64.0, 44.5] -// BM_set_string_fulllookup 1573 1529 +2.80% <256> [64.0, 35.4] -// BM_set_string_delete 2520 1920 +23.81% <256> [64.0, 44.5] -// BM_set_string_queueaddrem 4706 4309 +8.44% <256> [64.0, 48.3] -// BM_set_string_mixedaddrem 5080 4654 +8.39% <256> [64.0, 46.7] -// BM_set_string_fifo 318 512 -61.01% <256> [64.0, 35.4] -// BM_set_string_fwditer 182 93 +48.90% <256> [64.0, 44.5] -// BM_map_string_insert 2600 2227 +14.35% <256> [72.0, 55.8] -// BM_map_string_lookup 2068 1730 +16.34% <256> [72.0, 55.8] -// BM_map_string_fulllookup 1859 1618 +12.96% <256> [72.0, 44.0] -// BM_map_string_delete 3168 2080 +34.34% <256> [72.0, 55.8] -// BM_map_string_queueaddrem 5840 4701 +19.50% <256> [72.0, 59.4] -// BM_map_string_mixedaddrem 6400 5200 +18.75% <256> [72.0, 57.8] -// BM_map_string_fifo 398 596 -49.75% <256> [72.0, 44.0] -// BM_map_string_fwditer 243 113 +53.50% <256> [72.0, 55.8] - -#ifndef UTIL_BTREE_BTREE_H__ -#define UTIL_BTREE_BTREE_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef NDEBUG -#define NDEBUG 1 -#endif - -namespace btree { - -// Inside a btree method, if we just call swap(), it will choose the -// btree::swap method, which we don't want. And we can't say ::swap -// because then MSVC won't pickup any std::swap() implementations. We -// can't just use std::swap() directly because then we don't get the -// specialization for types outside the std namespace. So the solution -// is to have a special swap helper function whose name doesn't -// collide with other swap functions defined by the btree classes. -template -inline void btree_swap_helper(T &a, T &b) { - using std::swap; - swap(a, b); -} - -// A template helper used to select A or B based on a condition. -template -struct if_{ - typedef A type; -}; - -template -struct if_ { - typedef B type; -}; - -// Types small_ and big_ are promise that sizeof(small_) < sizeof(big_) -typedef char small_; - -struct big_ { - char dummy[2]; -}; - -// A compile-time assertion. -template -struct CompileAssert { -}; - -#define COMPILE_ASSERT(expr, msg) \ - typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] - -// A helper type used to indicate that a key-compare-to functor has been -// provided. A user can specify a key-compare-to functor by doing: -// -// struct MyStringComparer -// : public util::btree::btree_key_compare_to_tag { -// int operator()(const string &a, const string &b) const { -// return a.compare(b); -// } -// }; -// -// Note that the return type is an int and not a bool. There is a -// COMPILE_ASSERT which enforces this return type. -struct btree_key_compare_to_tag { -}; - -// A helper class that indicates if the Compare parameter is derived from -// btree_key_compare_to_tag. -template -struct btree_is_key_compare_to - : public std::is_convertible { -}; - -// A helper class to convert a boolean comparison into a three-way -// "compare-to" comparison that returns a negative value to indicate -// less-than, zero to indicate equality and a positive value to -// indicate greater-than. This helper class is specialized for -// less and greater. The btree_key_compare_to_adapter -// class is provided so that btree users automatically get the more -// efficient compare-to code when using common google string types -// with common comparison functors. -template -struct btree_key_compare_to_adapter : Compare { - btree_key_compare_to_adapter() { } - btree_key_compare_to_adapter(const Compare &c) : Compare(c) { } - btree_key_compare_to_adapter(const btree_key_compare_to_adapter &c) - : Compare(c) { - } -}; - -template <> -struct btree_key_compare_to_adapter > - : public btree_key_compare_to_tag { - btree_key_compare_to_adapter() {} - btree_key_compare_to_adapter(const std::less&) {} - btree_key_compare_to_adapter( - const btree_key_compare_to_adapter >&) {} - int operator()(const std::string &a, const std::string &b) const { - return a.compare(b); - } -}; - -template <> -struct btree_key_compare_to_adapter > - : public btree_key_compare_to_tag { - btree_key_compare_to_adapter() {} - btree_key_compare_to_adapter(const std::greater&) {} - btree_key_compare_to_adapter( - const btree_key_compare_to_adapter >&) {} - int operator()(const std::string &a, const std::string &b) const { - return b.compare(a); - } -}; - -// A helper class that allows a compare-to functor to behave like a plain -// compare functor. This specialization is used when we do not have a -// compare-to functor. -template -struct btree_key_comparer { - btree_key_comparer() {} - btree_key_comparer(Compare c) : comp(c) {} - static bool bool_compare(const Compare &comp, const Key &x, const Key &y) { - return comp(x, y); - } - bool operator()(const Key &x, const Key &y) const { - return bool_compare(comp, x, y); - } - Compare comp; -}; - -// A specialization of btree_key_comparer when a compare-to functor is -// present. We need a plain (boolean) comparison in some parts of the btree -// code, such as insert-with-hint. -template -struct btree_key_comparer { - btree_key_comparer() {} - btree_key_comparer(Compare c) : comp(c) {} - static bool bool_compare(const Compare &comp, const Key &x, const Key &y) { - return comp(x, y) < 0; - } - bool operator()(const Key &x, const Key &y) const { - return bool_compare(comp, x, y); - } - Compare comp; -}; - -// A helper function to compare to keys using the specified compare -// functor. This dispatches to the appropriate btree_key_comparer comparison, -// depending on whether we have a compare-to functor or not (which depends on -// whether Compare is derived from btree_key_compare_to_tag). -template -static bool btree_compare_keys( - const Compare &comp, const Key &x, const Key &y) { - typedef btree_key_comparer::value> key_comparer; - return key_comparer::bool_compare(comp, x, y); -} - -template -struct btree_common_params { - // If Compare is derived from btree_key_compare_to_tag then use it as the - // key_compare type. Otherwise, use btree_key_compare_to_adapter<> which will - // fall-back to Compare if we don't have an appropriate specialization. - typedef typename if_< - btree_is_key_compare_to::value, - Compare, btree_key_compare_to_adapter >::type key_compare; - // A type which indicates if we have a key-compare-to functor or a plain old - // key-compare functor. - typedef btree_is_key_compare_to is_key_compare_to; - - typedef Alloc allocator_type; - typedef Key key_type; - typedef ssize_t size_type; - typedef ptrdiff_t difference_type; - - enum { - kTargetNodeSize = TargetNodeSize, - - // Available space for values. This is largest for leaf nodes, - // which has overhead no fewer than two pointers. - kNodeValueSpace = TargetNodeSize - 2 * sizeof(void*), - }; - - // This is an integral type large enough to hold as many - // ValueSize-values as will fit a node of TargetNodeSize bytes. - typedef typename if_< - (kNodeValueSpace / ValueSize) >= 256, - uint16_t, - uint8_t>::type node_count_type; -}; - -// A parameters structure for holding the type parameters for a btree_map. -template -struct btree_map_params - : public btree_common_params { - typedef Data data_type; - typedef Data mapped_type; - typedef std::pair value_type; - typedef std::pair mutable_value_type; - typedef value_type* pointer; - typedef const value_type* const_pointer; - typedef value_type& reference; - typedef const value_type& const_reference; - - enum { - kValueSize = sizeof(Key) + sizeof(data_type), - }; - - static const Key& key(const value_type &x) { return x.first; } - static const Key& key(const mutable_value_type &x) { return x.first; } - static void swap(mutable_value_type *a, mutable_value_type *b) { - btree_swap_helper(a->first, b->first); - btree_swap_helper(a->second, b->second); - } -}; - -// A parameters structure for holding the type parameters for a btree_set. -template -struct btree_set_params - : public btree_common_params { - typedef std::false_type data_type; - typedef std::false_type mapped_type; - typedef Key value_type; - typedef value_type mutable_value_type; - typedef value_type* pointer; - typedef const value_type* const_pointer; - typedef value_type& reference; - typedef const value_type& const_reference; - - enum { - kValueSize = sizeof(Key), - }; - - static const Key& key(const value_type &x) { return x; } - static void swap(mutable_value_type *a, mutable_value_type *b) { - btree_swap_helper(*a, *b); - } -}; - -// An adapter class that converts a lower-bound compare into an upper-bound -// compare. -template -struct btree_upper_bound_adapter : public Compare { - btree_upper_bound_adapter(Compare c) : Compare(c) {} - bool operator()(const Key &a, const Key &b) const { - return !static_cast(*this)(b, a); - } -}; - -template -struct btree_upper_bound_compare_to_adapter : public CompareTo { - btree_upper_bound_compare_to_adapter(CompareTo c) : CompareTo(c) {} - int operator()(const Key &a, const Key &b) const { - return static_cast(*this)(b, a); - } -}; - -// Dispatch helper class for using linear search with plain compare. -template -struct btree_linear_search_plain_compare { - static int lower_bound(const K &k, const N &n, Compare comp) { - return n.linear_search_plain_compare(k, 0, n.count(), comp); - } - static int upper_bound(const K &k, const N &n, Compare comp) { - typedef btree_upper_bound_adapter upper_compare; - return n.linear_search_plain_compare(k, 0, n.count(), upper_compare(comp)); - } -}; - -// Dispatch helper class for using linear search with compare-to -template -struct btree_linear_search_compare_to { - static int lower_bound(const K &k, const N &n, CompareTo comp) { - return n.linear_search_compare_to(k, 0, n.count(), comp); - } - static int upper_bound(const K &k, const N &n, CompareTo comp) { - typedef btree_upper_bound_adapter > upper_compare; - return n.linear_search_plain_compare(k, 0, n.count(), upper_compare(comp)); - } -}; - -// Dispatch helper class for using binary search with plain compare. -template -struct btree_binary_search_plain_compare { - static int lower_bound(const K &k, const N &n, Compare comp) { - return n.binary_search_plain_compare(k, 0, n.count(), comp); - } - static int upper_bound(const K &k, const N &n, Compare comp) { - typedef btree_upper_bound_adapter upper_compare; - return n.binary_search_plain_compare(k, 0, n.count(), upper_compare(comp)); - } -}; - -// Dispatch helper class for using binary search with compare-to. -template -struct btree_binary_search_compare_to { - static int lower_bound(const K &k, const N &n, CompareTo comp) { - return n.binary_search_compare_to(k, 0, n.count(), CompareTo()); - } - static int upper_bound(const K &k, const N &n, CompareTo comp) { - typedef btree_upper_bound_adapter > upper_compare; - return n.linear_search_plain_compare(k, 0, n.count(), upper_compare(comp)); - } -}; - -// A node in the btree holding. The same node type is used for both internal -// and leaf nodes in the btree, though the nodes are allocated in such a way -// that the children array is only valid in internal nodes. -template -class btree_node { - public: - typedef Params params_type; - typedef btree_node self_type; - typedef typename Params::key_type key_type; - typedef typename Params::data_type data_type; - typedef typename Params::value_type value_type; - typedef typename Params::mutable_value_type mutable_value_type; - typedef typename Params::pointer pointer; - typedef typename Params::const_pointer const_pointer; - typedef typename Params::reference reference; - typedef typename Params::const_reference const_reference; - typedef typename Params::key_compare key_compare; - typedef typename Params::size_type size_type; - typedef typename Params::difference_type difference_type; - // Typedefs for the various types of node searches. - typedef btree_linear_search_plain_compare< - key_type, self_type, key_compare> linear_search_plain_compare_type; - typedef btree_linear_search_compare_to< - key_type, self_type, key_compare> linear_search_compare_to_type; - typedef btree_binary_search_plain_compare< - key_type, self_type, key_compare> binary_search_plain_compare_type; - typedef btree_binary_search_compare_to< - key_type, self_type, key_compare> binary_search_compare_to_type; - // If we have a valid key-compare-to type, use linear_search_compare_to, - // otherwise use linear_search_plain_compare. - typedef typename if_< - Params::is_key_compare_to::value, - linear_search_compare_to_type, - linear_search_plain_compare_type>::type linear_search_type; - // If we have a valid key-compare-to type, use binary_search_compare_to, - // otherwise use binary_search_plain_compare. - typedef typename if_< - Params::is_key_compare_to::value, - binary_search_compare_to_type, - binary_search_plain_compare_type>::type binary_search_type; - // If the key is an integral or floating point type, use linear search which - // is faster than binary search for such types. Might be wise to also - // configure linear search based on node-size. - typedef typename if_< - std::is_integral::value || - std::is_floating_point::value, - linear_search_type, binary_search_type>::type search_type; - - struct base_fields { - typedef typename Params::node_count_type field_type; - - // A boolean indicating whether the node is a leaf or not. - bool leaf; - // The position of the node in the node's parent. - field_type position; - // The maximum number of values the node can hold. - field_type max_count; - // The count of the number of values in the node. - field_type count; - // A pointer to the node's parent. - btree_node *parent; - }; - - enum { - kValueSize = params_type::kValueSize, - kTargetNodeSize = params_type::kTargetNodeSize, - - // Compute how many values we can fit onto a leaf node. - kNodeTargetValues = (kTargetNodeSize - sizeof(base_fields)) / kValueSize, - // We need a minimum of 3 values per internal node in order to perform - // splitting (1 value for the two nodes involved in the split and 1 value - // propagated to the parent as the delimiter for the split). - kNodeValues = kNodeTargetValues >= 3 ? kNodeTargetValues : 3, - - kExactMatch = 1 << 30, - kMatchMask = kExactMatch - 1, - }; - - struct leaf_fields : public base_fields { - // The array of values. Only the first count of these values have been - // constructed and are valid. - mutable_value_type values[kNodeValues]; - }; - - struct internal_fields : public leaf_fields { - // The array of child pointers. The keys in children_[i] are all less than - // key(i). The keys in children_[i + 1] are all greater than key(i). There - // are always count + 1 children. - btree_node *children[kNodeValues + 1]; - }; - - struct root_fields : public internal_fields { - btree_node *rightmost; - size_type size; - }; - - public: - // Getter/setter for whether this is a leaf node or not. This value doesn't - // change after the node is created. - bool leaf() const { return fields_.leaf; } - - // Getter for the position of this node in its parent. - int position() const { return fields_.position; } - void set_position(int v) { fields_.position = v; } - - // Getter/setter for the number of values stored in this node. - int count() const { return fields_.count; } - void set_count(int v) { fields_.count = v; } - int max_count() const { return fields_.max_count; } - - // Getter for the parent of this node. - btree_node* parent() const { return fields_.parent; } - // Getter for whether the node is the root of the tree. The parent of the - // root of the tree is the leftmost node in the tree which is guaranteed to - // be a leaf. - bool is_root() const { return parent()->leaf(); } - void make_root() { - assert(parent()->is_root()); - fields_.parent = fields_.parent->parent(); - } - - // Getter for the rightmost root node field. Only valid on the root node. - btree_node* rightmost() const { return fields_.rightmost; } - btree_node** mutable_rightmost() { return &fields_.rightmost; } - - // Getter for the size root node field. Only valid on the root node. - size_type size() const { return fields_.size; } - size_type* mutable_size() { return &fields_.size; } - - // Getters for the key/value at position i in the node. - const key_type& key(int i) const { - return params_type::key(fields_.values[i]); - } - reference value(int i) { - return reinterpret_cast(fields_.values[i]); - } - const_reference value(int i) const { - return reinterpret_cast(fields_.values[i]); - } - mutable_value_type* mutable_value(int i) { - return &fields_.values[i]; - } - - // Swap value i in this node with value j in node x. - void value_swap(int i, btree_node *x, int j) { - params_type::swap(mutable_value(i), x->mutable_value(j)); - } - - // Getters/setter for the child at position i in the node. - btree_node* child(int i) const { return fields_.children[i]; } - btree_node** mutable_child(int i) { return &fields_.children[i]; } - void set_child(int i, btree_node *c) { - *mutable_child(i) = c; - c->fields_.parent = this; - c->fields_.position = i; - } - - // Returns the position of the first value whose key is not less than k. - template - int lower_bound(const key_type &k, const Compare &comp) const { - return search_type::lower_bound(k, *this, comp); - } - // Returns the position of the first value whose key is greater than k. - template - int upper_bound(const key_type &k, const Compare &comp) const { - return search_type::upper_bound(k, *this, comp); - } - - // Returns the position of the first value whose key is not less than k using - // linear search performed using plain compare. - template - int linear_search_plain_compare( - const key_type &k, int s, int e, const Compare &comp) const { - while (s < e) { - if (!btree_compare_keys(comp, key(s), k)) { - break; - } - ++s; - } - return s; - } - - // Returns the position of the first value whose key is not less than k using - // linear search performed using compare-to. - template - int linear_search_compare_to( - const key_type &k, int s, int e, const Compare &comp) const { - while (s < e) { - int c = comp(key(s), k); - if (c == 0) { - return s | kExactMatch; - } else if (c > 0) { - break; - } - ++s; - } - return s; - } - - // Returns the position of the first value whose key is not less than k using - // binary search performed using plain compare. - template - int binary_search_plain_compare( - const key_type &k, int s, int e, const Compare &comp) const { - while (s != e) { - int mid = (s + e) / 2; - if (btree_compare_keys(comp, key(mid), k)) { - s = mid + 1; - } else { - e = mid; - } - } - return s; - } - - // Returns the position of the first value whose key is not less than k using - // binary search performed using compare-to. - template - int binary_search_compare_to( - const key_type &k, int s, int e, const CompareTo &comp) const { - while (s != e) { - int mid = (s + e) / 2; - int c = comp(key(mid), k); - if (c < 0) { - s = mid + 1; - } else if (c > 0) { - e = mid; - } else { - // Need to return the first value whose key is not less than k, which - // requires continuing the binary search. Note that we are guaranteed - // that the result is an exact match because if "key(mid-1) < k" the - // call to binary_search_compare_to() will return "mid". - s = binary_search_compare_to(k, s, mid, comp); - return s | kExactMatch; - } - } - return s; - } - - // Inserts the value x at position i, shifting all existing values and - // children at positions >= i to the right by 1. - void insert_value(int i, const value_type &x); - - // Removes the value at position i, shifting all existing values and children - // at positions > i to the left by 1. - void remove_value(int i); - - // Rebalances a node with its right sibling. - void rebalance_right_to_left(btree_node *sibling, int to_move); - void rebalance_left_to_right(btree_node *sibling, int to_move); - - // Splits a node, moving a portion of the node's values to its right sibling. - void split(btree_node *sibling, int insert_position); - - // Merges a node with its right sibling, moving all of the values and the - // delimiting key in the parent node onto itself. - void merge(btree_node *sibling); - - // Swap the contents of "this" and "src". - void swap(btree_node *src); - - // Node allocation/deletion routines. - static btree_node* init_leaf( - leaf_fields *f, btree_node *parent, int max_count) { - btree_node *n = reinterpret_cast(f); - f->leaf = 1; - f->position = 0; - f->max_count = max_count; - f->count = 0; - f->parent = parent; - if (!NDEBUG) { - memset(&f->values, 0, max_count * sizeof(value_type)); - } - return n; - } - static btree_node* init_internal(internal_fields *f, btree_node *parent) { - btree_node *n = init_leaf(f, parent, kNodeValues); - f->leaf = 0; - if (!NDEBUG) { - memset(f->children, 0, sizeof(f->children)); - } - return n; - } - static btree_node* init_root(root_fields *f, btree_node *parent) { - btree_node *n = init_internal(f, parent); - f->rightmost = parent; - f->size = parent->count(); - return n; - } - void destroy() { - for (int i = 0; i < count(); ++i) { - value_destroy(i); - } - } - - private: - void value_init(int i) { - new (&fields_.values[i]) mutable_value_type; - } - void value_init(int i, const value_type &x) { - new (&fields_.values[i]) mutable_value_type(x); - } - void value_destroy(int i) { - fields_.values[i].~mutable_value_type(); - } - - private: - root_fields fields_; - - private: - btree_node(const btree_node&); - void operator=(const btree_node&); -}; - -template -struct btree_iterator { - typedef typename Node::key_type key_type; - typedef typename Node::size_type size_type; - typedef typename Node::difference_type difference_type; - typedef typename Node::params_type params_type; - - typedef Node node_type; - typedef typename std::remove_const::type normal_node; - typedef const Node const_node; - typedef typename params_type::value_type value_type; - typedef typename params_type::pointer normal_pointer; - typedef typename params_type::reference normal_reference; - typedef typename params_type::const_pointer const_pointer; - typedef typename params_type::const_reference const_reference; - - typedef Pointer pointer; - typedef Reference reference; - typedef std::bidirectional_iterator_tag iterator_category; - - typedef btree_iterator< - normal_node, normal_reference, normal_pointer> iterator; - typedef btree_iterator< - const_node, const_reference, const_pointer> const_iterator; - typedef btree_iterator self_type; - - btree_iterator() - : node(NULL), - position(-1) { - } - btree_iterator(Node *n, int p) - : node(n), - position(p) { - } - btree_iterator(const iterator &x) - : node(x.node), - position(x.position) { - } - - // Increment/decrement the iterator. - void increment() { - if (node->leaf() && ++position < node->count()) { - return; - } - increment_slow(); - } - void increment_by(int count); - void increment_slow(); - - void decrement() { - if (node->leaf() && --position >= 0) { - return; - } - decrement_slow(); - } - void decrement_slow(); - - bool operator==(const const_iterator &x) const { - return node == x.node && position == x.position; - } - bool operator!=(const const_iterator &x) const { - return node != x.node || position != x.position; - } - - // Accessors for the key/value the iterator is pointing at. - const key_type& key() const { - return node->key(position); - } - reference operator*() const { - return node->value(position); - } - pointer operator->() const { - return &node->value(position); - } - - self_type& operator++() { - increment(); - return *this; - } - self_type& operator--() { - decrement(); - return *this; - } - self_type operator++(int) { - self_type tmp = *this; - ++*this; - return tmp; - } - self_type operator--(int) { - self_type tmp = *this; - --*this; - return tmp; - } - - // The node in the tree the iterator is pointing at. - Node *node; - // The position within the node of the tree the iterator is pointing at. - int position; -}; - -// Dispatch helper class for using btree::internal_locate with plain compare. -struct btree_internal_locate_plain_compare { - template - static std::pair dispatch(const K &k, const T &t, Iter iter) { - return t.internal_locate_plain_compare(k, iter); - } -}; - -// Dispatch helper class for using btree::internal_locate with compare-to. -struct btree_internal_locate_compare_to { - template - static std::pair dispatch(const K &k, const T &t, Iter iter) { - return t.internal_locate_compare_to(k, iter); - } -}; - -template -class btree : public Params::key_compare { - typedef btree self_type; - typedef btree_node node_type; - typedef typename node_type::base_fields base_fields; - typedef typename node_type::leaf_fields leaf_fields; - typedef typename node_type::internal_fields internal_fields; - typedef typename node_type::root_fields root_fields; - typedef typename Params::is_key_compare_to is_key_compare_to; - - friend class btree_internal_locate_plain_compare; - friend class btree_internal_locate_compare_to; - typedef typename if_< - is_key_compare_to::value, - btree_internal_locate_compare_to, - btree_internal_locate_plain_compare>::type internal_locate_type; - - enum { - kNodeValues = node_type::kNodeValues, - kMinNodeValues = kNodeValues / 2, - kValueSize = node_type::kValueSize, - kExactMatch = node_type::kExactMatch, - kMatchMask = node_type::kMatchMask, - }; - - // A helper class to get the empty base class optimization for 0-size - // allocators. Base is internal_allocator_type. - // (e.g. empty_base_handle). If Base is - // 0-size, the compiler doesn't have to reserve any space for it and - // sizeof(empty_base_handle) will simply be sizeof(Data). Google [empty base - // class optimization] for more details. - template - struct empty_base_handle : public Base { - empty_base_handle(const Base &b, const Data &d) - : Base(b), - data(d) { - } - Data data; - }; - - struct node_stats { - node_stats(ssize_t l, ssize_t i) - : leaf_nodes(l), - internal_nodes(i) { - } - - node_stats& operator+=(const node_stats &x) { - leaf_nodes += x.leaf_nodes; - internal_nodes += x.internal_nodes; - return *this; - } - - ssize_t leaf_nodes; - ssize_t internal_nodes; - }; - - public: - typedef Params params_type; - typedef typename Params::key_type key_type; - typedef typename Params::data_type data_type; - typedef typename Params::mapped_type mapped_type; - typedef typename Params::value_type value_type; - typedef typename Params::key_compare key_compare; - typedef typename Params::pointer pointer; - typedef typename Params::const_pointer const_pointer; - typedef typename Params::reference reference; - typedef typename Params::const_reference const_reference; - typedef typename Params::size_type size_type; - typedef typename Params::difference_type difference_type; - typedef btree_iterator iterator; - typedef typename iterator::const_iterator const_iterator; - typedef std::reverse_iterator const_reverse_iterator; - typedef std::reverse_iterator reverse_iterator; - - typedef typename Params::allocator_type allocator_type; - typedef typename allocator_type::template rebind::other - internal_allocator_type; - - public: - // Default constructor. - btree(const key_compare &comp, const allocator_type &alloc); - - // Copy constructor. - btree(const self_type &x); - - // Destructor. - ~btree() { - clear(); - } - - // Iterator routines. - iterator begin() { - return iterator(leftmost(), 0); - } - const_iterator begin() const { - return const_iterator(leftmost(), 0); - } - iterator end() { - return iterator(rightmost(), rightmost() ? rightmost()->count() : 0); - } - const_iterator end() const { - return const_iterator(rightmost(), rightmost() ? rightmost()->count() : 0); - } - reverse_iterator rbegin() { - return reverse_iterator(end()); - } - const_reverse_iterator rbegin() const { - return const_reverse_iterator(end()); - } - reverse_iterator rend() { - return reverse_iterator(begin()); - } - const_reverse_iterator rend() const { - return const_reverse_iterator(begin()); - } - - // Finds the first element whose key is not less than key. - iterator lower_bound(const key_type &key) { - return internal_end( - internal_lower_bound(key, iterator(root(), 0))); - } - const_iterator lower_bound(const key_type &key) const { - return internal_end( - internal_lower_bound(key, const_iterator(root(), 0))); - } - - // Finds the first element whose key is greater than key. - iterator upper_bound(const key_type &key) { - return internal_end( - internal_upper_bound(key, iterator(root(), 0))); - } - const_iterator upper_bound(const key_type &key) const { - return internal_end( - internal_upper_bound(key, const_iterator(root(), 0))); - } - - // Finds the range of values which compare equal to key. The first member of - // the returned pair is equal to lower_bound(key). The second member pair of - // the pair is equal to upper_bound(key). - std::pair equal_range(const key_type &key) { - return std::make_pair(lower_bound(key), upper_bound(key)); - } - std::pair equal_range(const key_type &key) const { - return std::make_pair(lower_bound(key), upper_bound(key)); - } - - // Inserts a value into the btree only if it does not already exist. The - // boolean return value indicates whether insertion succeeded or failed. The - // ValuePointer type is used to avoid instatiating the value unless the key - // is being inserted. Value is not dereferenced if the key already exists in - // the btree. See btree_map::operator[]. - template - std::pair insert_unique(const key_type &key, ValuePointer value); - - // Inserts a value into the btree only if it does not already exist. The - // boolean return value indicates whether insertion succeeded or failed. - std::pair insert_unique(const value_type &v) { - return insert_unique(params_type::key(v), &v); - } - - // Insert with hint. Check to see if the value should be placed immediately - // before position in the tree. If it does, then the insertion will take - // amortized constant time. If not, the insertion will take amortized - // logarithmic time as if a call to insert_unique(v) were made. - iterator insert_unique(iterator position, const value_type &v); - - // Insert a range of values into the btree. - template - void insert_unique(InputIterator b, InputIterator e); - - // Inserts a value into the btree. The ValuePointer type is used to avoid - // instatiating the value unless the key is being inserted. Value is not - // dereferenced if the key already exists in the btree. See - // btree_map::operator[]. - template - iterator insert_multi(const key_type &key, ValuePointer value); - - // Inserts a value into the btree. - iterator insert_multi(const value_type &v) { - return insert_multi(params_type::key(v), &v); - } - - // Insert with hint. Check to see if the value should be placed immediately - // before position in the tree. If it does, then the insertion will take - // amortized constant time. If not, the insertion will take amortized - // logarithmic time as if a call to insert_multi(v) were made. - iterator insert_multi(iterator position, const value_type &v); - - // Insert a range of values into the btree. - template - void insert_multi(InputIterator b, InputIterator e); - - void assign(const self_type &x); - - // Erase the specified iterator from the btree. The iterator must be valid - // (i.e. not equal to end()). Return an iterator pointing to the node after - // the one that was erased (or end() if none exists). - iterator erase(iterator iter); - - // Erases range. Returns the number of keys erased. - int erase(iterator begin, iterator end); - - // Erases the specified key from the btree. Returns 1 if an element was - // erased and 0 otherwise. - int erase_unique(const key_type &key); - - // Erases all of the entries matching the specified key from the - // btree. Returns the number of elements erased. - int erase_multi(const key_type &key); - - // Finds the iterator corresponding to a key or returns end() if the key is - // not present. - iterator find_unique(const key_type &key) { - return internal_end( - internal_find_unique(key, iterator(root(), 0))); - } - const_iterator find_unique(const key_type &key) const { - return internal_end( - internal_find_unique(key, const_iterator(root(), 0))); - } - iterator find_multi(const key_type &key) { - return internal_end( - internal_find_multi(key, iterator(root(), 0))); - } - const_iterator find_multi(const key_type &key) const { - return internal_end( - internal_find_multi(key, const_iterator(root(), 0))); - } - - // Returns a count of the number of times the key appears in the btree. - size_type count_unique(const key_type &key) const { - const_iterator begin = internal_find_unique( - key, const_iterator(root(), 0)); - if (!begin.node) { - // The key doesn't exist in the tree. - return 0; - } - return 1; - } - // Returns a count of the number of times the key appears in the btree. - size_type count_multi(const key_type &key) const { - return distance(lower_bound(key), upper_bound(key)); - } - - // Clear the btree, deleting all of the values it contains. - void clear(); - - // Swap the contents of *this and x. - void swap(self_type &x); - - // Assign the contents of x to *this. - self_type& operator=(const self_type &x) { - if (&x == this) { - // Don't copy onto ourselves. - return *this; - } - assign(x); - return *this; - } - - key_compare* mutable_key_comp() { - return this; - } - const key_compare& key_comp() const { - return *this; - } - bool compare_keys(const key_type &x, const key_type &y) const { - return btree_compare_keys(key_comp(), x, y); - } - - // Dump the btree to the specified ostream. Requires that operator<< is - // defined for Key and Value. - void dump(std::ostream &os) const { - if (root() != NULL) { - internal_dump(os, root(), 0); - } - } - - // Verifies the structure of the btree. - void verify() const; - - // Size routines. Note that empty() is slightly faster than doing size()==0. - size_type size() const { - if (empty()) return 0; - if (root()->leaf()) return root()->count(); - return root()->size(); - } - size_type max_size() const { return std::numeric_limits::max(); } - bool empty() const { return root() == NULL; } - - // The height of the btree. An empty tree will have height 0. - size_type height() const { - size_type h = 0; - if (root()) { - // Count the length of the chain from the leftmost node up to the - // root. We actually count from the root back around to the level below - // the root, but the calculation is the same because of the circularity - // of that traversal. - const node_type *n = root(); - do { - ++h; - n = n->parent(); - } while (n != root()); - } - return h; - } - - // The number of internal, leaf and total nodes used by the btree. - size_type leaf_nodes() const { - return internal_stats(root()).leaf_nodes; - } - size_type internal_nodes() const { - return internal_stats(root()).internal_nodes; - } - size_type nodes() const { - node_stats stats = internal_stats(root()); - return stats.leaf_nodes + stats.internal_nodes; - } - - // The total number of bytes used by the btree. - size_type bytes_used() const { - node_stats stats = internal_stats(root()); - if (stats.leaf_nodes == 1 && stats.internal_nodes == 0) { - return sizeof(*this) + - sizeof(base_fields) + root()->max_count() * sizeof(value_type); - } else { - return sizeof(*this) + - sizeof(root_fields) - sizeof(internal_fields) + - stats.leaf_nodes * sizeof(leaf_fields) + - stats.internal_nodes * sizeof(internal_fields); - } - } - - // The average number of bytes used per value stored in the btree. - static double average_bytes_per_value() { - // Returns the number of bytes per value on a leaf node that is 75% - // full. Experimentally, this matches up nicely with the computed number of - // bytes per value in trees that had their values inserted in random order. - return sizeof(leaf_fields) / (kNodeValues * 0.75); - } - - // The fullness of the btree. Computed as the number of elements in the btree - // divided by the maximum number of elements a tree with the current number - // of nodes could hold. A value of 1 indicates perfect space - // utilization. Smaller values indicate space wastage. - double fullness() const { - return double(size()) / (nodes() * kNodeValues); - } - // The overhead of the btree structure in bytes per node. Computed as the - // total number of bytes used by the btree minus the number of bytes used for - // storing elements divided by the number of elements. - double overhead() const { - if (empty()) { - return 0.0; - } - return (bytes_used() - size() * kValueSize) / double(size()); - } - - private: - // Internal accessor routines. - node_type* root() { return root_.data; } - const node_type* root() const { return root_.data; } - node_type** mutable_root() { return &root_.data; } - - // The rightmost node is stored in the root node. - node_type* rightmost() { - return (!root() || root()->leaf()) ? root() : root()->rightmost(); - } - const node_type* rightmost() const { - return (!root() || root()->leaf()) ? root() : root()->rightmost(); - } - node_type** mutable_rightmost() { return root()->mutable_rightmost(); } - - // The leftmost node is stored as the parent of the root node. - node_type* leftmost() { return root() ? root()->parent() : NULL; } - const node_type* leftmost() const { return root() ? root()->parent() : NULL; } - - // The size of the tree is stored in the root node. - size_type* mutable_size() { return root()->mutable_size(); } - - // Allocator routines. - internal_allocator_type* mutable_internal_allocator() { - return static_cast(&root_); - } - const internal_allocator_type& internal_allocator() const { - return *static_cast(&root_); - } - - // Node creation/deletion routines. - node_type* new_internal_node(node_type *parent) { - internal_fields *p = reinterpret_cast( - mutable_internal_allocator()->allocate(sizeof(internal_fields))); - return node_type::init_internal(p, parent); - } - node_type* new_internal_root_node() { - root_fields *p = reinterpret_cast( - mutable_internal_allocator()->allocate(sizeof(root_fields))); - return node_type::init_root(p, root()->parent()); - } - node_type* new_leaf_node(node_type *parent) { - leaf_fields *p = reinterpret_cast( - mutable_internal_allocator()->allocate(sizeof(leaf_fields))); - return node_type::init_leaf(p, parent, kNodeValues); - } - node_type* new_leaf_root_node(int max_count) { - leaf_fields *p = reinterpret_cast( - mutable_internal_allocator()->allocate( - sizeof(base_fields) + max_count * sizeof(value_type))); - return node_type::init_leaf(p, reinterpret_cast(p), max_count); - } - void delete_internal_node(node_type *node) { - node->destroy(); - assert(node != root()); - mutable_internal_allocator()->deallocate( - reinterpret_cast(node), sizeof(internal_fields)); - } - void delete_internal_root_node() { - root()->destroy(); - mutable_internal_allocator()->deallocate( - reinterpret_cast(root()), sizeof(root_fields)); - } - void delete_leaf_node(node_type *node) { - node->destroy(); - mutable_internal_allocator()->deallocate( - reinterpret_cast(node), - sizeof(base_fields) + node->max_count() * sizeof(value_type)); - } - - // Rebalances or splits the node iter points to. - void rebalance_or_split(iterator *iter); - - // Merges the values of left, right and the delimiting key on their parent - // onto left, removing the delimiting key and deleting right. - void merge_nodes(node_type *left, node_type *right); - - // Tries to merge node with its left or right sibling, and failing that, - // rebalance with its left or right sibling. Returns true if a merge - // occurred, at which point it is no longer valid to access node. Returns - // false if no merging took place. - bool try_merge_or_rebalance(iterator *iter); - - // Tries to shrink the height of the tree by 1. - void try_shrink(); - - iterator internal_end(iterator iter) { - return iter.node ? iter : end(); - } - const_iterator internal_end(const_iterator iter) const { - return iter.node ? iter : end(); - } - - // Inserts a value into the btree immediately before iter. Requires that - // key(v) <= iter.key() and (--iter).key() <= key(v). - iterator internal_insert(iterator iter, const value_type &v); - - // Returns an iterator pointing to the first value >= the value "iter" is - // pointing at. Note that "iter" might be pointing to an invalid location as - // iter.position == iter.node->count(). This routine simply moves iter up in - // the tree to a valid location. - template - static IterType internal_last(IterType iter); - - // Returns an iterator pointing to the leaf position at which key would - // reside in the tree. We provide 2 versions of internal_locate. The first - // version (internal_locate_plain_compare) always returns 0 for the second - // field of the pair. The second version (internal_locate_compare_to) is for - // the key-compare-to specialization and returns either kExactMatch (if the - // key was found in the tree) or -kExactMatch (if it wasn't) in the second - // field of the pair. The compare_to specialization allows the caller to - // avoid a subsequent comparison to determine if an exact match was made, - // speeding up string keys. - template - std::pair internal_locate( - const key_type &key, IterType iter) const; - template - std::pair internal_locate_plain_compare( - const key_type &key, IterType iter) const; - template - std::pair internal_locate_compare_to( - const key_type &key, IterType iter) const; - - // Internal routine which implements lower_bound(). - template - IterType internal_lower_bound( - const key_type &key, IterType iter) const; - - // Internal routine which implements upper_bound(). - template - IterType internal_upper_bound( - const key_type &key, IterType iter) const; - - // Internal routine which implements find_unique(). - template - IterType internal_find_unique( - const key_type &key, IterType iter) const; - - // Internal routine which implements find_multi(). - template - IterType internal_find_multi( - const key_type &key, IterType iter) const; - - // Deletes a node and all of its children. - void internal_clear(node_type *node); - - // Dumps a node and all of its children to the specified ostream. - void internal_dump(std::ostream &os, const node_type *node, int level) const; - - // Verifies the tree structure of node. - int internal_verify(const node_type *node, - const key_type *lo, const key_type *hi) const; - - node_stats internal_stats(const node_type *node) const { - if (!node) { - return node_stats(0, 0); - } - if (node->leaf()) { - return node_stats(1, 0); - } - node_stats res(0, 1); - for (int i = 0; i <= node->count(); ++i) { - res += internal_stats(node->child(i)); - } - return res; - } - - private: - empty_base_handle root_; - - private: - // A never instantiated helper function that returns big_ if we have a - // key-compare-to functor or if R is bool and small_ otherwise. - template - static typename if_< - if_, - std::is_same >::type::value, - big_, small_>::type key_compare_checker(R); - - // A never instantiated helper function that returns the key comparison - // functor. - static key_compare key_compare_helper(); - - // Verify that key_compare returns a bool. This is similar to the way - // is_convertible in base/type_traits.h works. Note that key_compare_checker - // is never actually invoked. The compiler will select which - // key_compare_checker() to instantiate and then figure out the size of the - // return type of key_compare_checker() at compile time which we then check - // against the sizeof of big_. -#if !defined(_MSC_VER) /* Allow compilation in MSVC 2010 */ - COMPILE_ASSERT( - sizeof(key_compare_checker(key_compare_helper()(key_type(), key_type()))) == - sizeof(big_), - key_comparison_function_must_return_bool); -#endif - - // Note: We insist on kTargetValues, which is computed from - // Params::kTargetNodeSize, must fit the base_fields::field_type. - COMPILE_ASSERT(kNodeValues < - (1 << (8 * sizeof(typename base_fields::field_type))), - target_node_size_too_large); - - // Test the assumption made in setting kNodeValueSpace. - COMPILE_ASSERT(sizeof(base_fields) >= 2 * sizeof(void*), - node_space_assumption_incorrect); -}; - -//// -// btree_node methods -template -inline void btree_node

::insert_value(int i, const value_type &x) { - assert(i <= count()); - value_init(count(), x); - for (int j = count(); j > i; --j) { - value_swap(j, this, j - 1); - } - set_count(count() + 1); - - if (!leaf()) { - ++i; - for (int j = count(); j > i; --j) { - *mutable_child(j) = child(j - 1); - child(j)->set_position(j); - } - *mutable_child(i) = NULL; - } -} - -template -inline void btree_node

::remove_value(int i) { - if (!leaf()) { - assert(child(i + 1)->count() == 0); - for (int j = i + 1; j < count(); ++j) { - *mutable_child(j) = child(j + 1); - child(j)->set_position(j); - } - *mutable_child(count()) = NULL; - } - - set_count(count() - 1); - for (; i < count(); ++i) { - value_swap(i, this, i + 1); - } - value_destroy(i); -} - -template -void btree_node

::rebalance_right_to_left(btree_node *src, int to_move) { - assert(parent() == src->parent()); - assert(position() + 1 == src->position()); - assert(src->count() >= count()); - assert(to_move >= 1); - assert(to_move <= src->count()); - - // Make room in the left node for the new values. - for (int i = 0; i < to_move; ++i) { - value_init(i + count()); - } - - // Move the delimiting value to the left node and the new delimiting value - // from the right node. - value_swap(count(), parent(), position()); - parent()->value_swap(position(), src, to_move - 1); - - // Move the values from the right to the left node. - for (int i = 1; i < to_move; ++i) { - value_swap(count() + i, src, i - 1); - } - // Shift the values in the right node to their correct position. - for (int i = to_move; i < src->count(); ++i) { - src->value_swap(i - to_move, src, i); - } - for (int i = 1; i <= to_move; ++i) { - src->value_destroy(src->count() - i); - } - - if (!leaf()) { - // Move the child pointers from the right to the left node. - for (int i = 0; i < to_move; ++i) { - set_child(1 + count() + i, src->child(i)); - } - for (int i = 0; i <= src->count() - to_move; ++i) { - assert(i + to_move <= src->max_count()); - src->set_child(i, src->child(i + to_move)); - *src->mutable_child(i + to_move) = NULL; - } - } - - // Fixup the counts on the src and dest nodes. - set_count(count() + to_move); - src->set_count(src->count() - to_move); -} - -template -void btree_node

::rebalance_left_to_right(btree_node *dest, int to_move) { - assert(parent() == dest->parent()); - assert(position() + 1 == dest->position()); - assert(count() >= dest->count()); - assert(to_move >= 1); - assert(to_move <= count()); - - // Make room in the right node for the new values. - for (int i = 0; i < to_move; ++i) { - dest->value_init(i + dest->count()); - } - for (int i = dest->count() - 1; i >= 0; --i) { - dest->value_swap(i, dest, i + to_move); - } - - // Move the delimiting value to the right node and the new delimiting value - // from the left node. - dest->value_swap(to_move - 1, parent(), position()); - parent()->value_swap(position(), this, count() - to_move); - value_destroy(count() - to_move); - - // Move the values from the left to the right node. - for (int i = 1; i < to_move; ++i) { - value_swap(count() - to_move + i, dest, i - 1); - value_destroy(count() - to_move + i); - } - - if (!leaf()) { - // Move the child pointers from the left to the right node. - for (int i = dest->count(); i >= 0; --i) { - dest->set_child(i + to_move, dest->child(i)); - *dest->mutable_child(i) = NULL; - } - for (int i = 1; i <= to_move; ++i) { - dest->set_child(i - 1, child(count() - to_move + i)); - *mutable_child(count() - to_move + i) = NULL; - } - } - - // Fixup the counts on the src and dest nodes. - set_count(count() - to_move); - dest->set_count(dest->count() + to_move); -} - -template -void btree_node

::split(btree_node *dest, int insert_position) { - assert(dest->count() == 0); - - // We bias the split based on the position being inserted. If we're - // inserting at the beginning of the left node then bias the split to put - // more values on the right node. If we're inserting at the end of the - // right node then bias the split to put more values on the left node. - if (insert_position == 0) { - dest->set_count(count() - 1); - } else if (insert_position == max_count()) { - dest->set_count(0); - } else { - dest->set_count(count() / 2); - } - set_count(count() - dest->count()); - assert(count() >= 1); - - // Move values from the left sibling to the right sibling. - for (int i = 0; i < dest->count(); ++i) { - dest->value_init(i); - value_swap(count() + i, dest, i); - value_destroy(count() + i); - } - - // The split key is the largest value in the left sibling. - set_count(count() - 1); - parent()->insert_value(position(), value_type()); - value_swap(count(), parent(), position()); - value_destroy(count()); - parent()->set_child(position() + 1, dest); - - if (!leaf()) { - for (int i = 0; i <= dest->count(); ++i) { - assert(child(count() + i + 1) != NULL); - dest->set_child(i, child(count() + i + 1)); - *mutable_child(count() + i + 1) = NULL; - } - } -} - -template -void btree_node

::merge(btree_node *src) { - assert(parent() == src->parent()); - assert(position() + 1 == src->position()); - - // Move the delimiting value to the left node. - value_init(count()); - value_swap(count(), parent(), position()); - - // Move the values from the right to the left node. - for (int i = 0; i < src->count(); ++i) { - value_init(1 + count() + i); - value_swap(1 + count() + i, src, i); - src->value_destroy(i); - } - - if (!leaf()) { - // Move the child pointers from the right to the left node. - for (int i = 0; i <= src->count(); ++i) { - set_child(1 + count() + i, src->child(i)); - *src->mutable_child(i) = NULL; - } - } - - // Fixup the counts on the src and dest nodes. - set_count(1 + count() + src->count()); - src->set_count(0); - - // Remove the value on the parent node. - parent()->remove_value(position()); -} - -template -void btree_node

::swap(btree_node *x) { - assert(leaf() == x->leaf()); - - // Swap the values. - for (int i = count(); i < x->count(); ++i) { - value_init(i); - } - for (int i = x->count(); i < count(); ++i) { - x->value_init(i); - } - int n = std::max(count(), x->count()); - for (int i = 0; i < n; ++i) { - value_swap(i, x, i); - } - for (int i = count(); i < x->count(); ++i) { - x->value_destroy(i); - } - for (int i = x->count(); i < count(); ++i) { - value_destroy(i); - } - - if (!leaf()) { - // Swap the child pointers. - for (int i = 0; i <= n; ++i) { - btree_swap_helper(*mutable_child(i), *x->mutable_child(i)); - } - for (int i = 0; i <= count(); ++i) { - x->child(i)->fields_.parent = x; - } - for (int i = 0; i <= x->count(); ++i) { - child(i)->fields_.parent = this; - } - } - - // Swap the counts. - btree_swap_helper(fields_.count, x->fields_.count); -} - -//// -// btree_iterator methods -template -void btree_iterator::increment_slow() { - if (node->leaf()) { - assert(position >= node->count()); - self_type save(*this); - while (position == node->count() && !node->is_root()) { - assert(node->parent()->child(node->position()) == node); - position = node->position(); - node = node->parent(); - } - if (position == node->count()) { - *this = save; - } - } else { - assert(position < node->count()); - node = node->child(position + 1); - while (!node->leaf()) { - node = node->child(0); - } - position = 0; - } -} - -template -void btree_iterator::increment_by(int count) { - while (count > 0) { - if (node->leaf()) { - int rest = node->count() - position; - position += std::min(rest, count); - count = count - rest; - if (position < node->count()) { - return; - } - } else { - --count; - } - increment_slow(); - } -} - -template -void btree_iterator::decrement_slow() { - if (node->leaf()) { - assert(position <= -1); - self_type save(*this); - while (position < 0 && !node->is_root()) { - assert(node->parent()->child(node->position()) == node); - position = node->position() - 1; - node = node->parent(); - } - if (position < 0) { - *this = save; - } - } else { - assert(position >= 0); - node = node->child(position); - while (!node->leaf()) { - node = node->child(node->count()); - } - position = node->count() - 1; - } -} - -//// -// btree methods -template -btree

::btree(const key_compare &comp, const allocator_type &alloc) - : key_compare(comp), - root_(alloc, NULL) { -} - -template -btree

::btree(const self_type &x) - : key_compare(x.key_comp()), - root_(x.internal_allocator(), NULL) { - assign(x); -} - -template template -std::pair::iterator, bool> -btree

::insert_unique(const key_type &key, ValuePointer value) { - if (empty()) { - *mutable_root() = new_leaf_root_node(1); - } - - std::pair res = internal_locate(key, iterator(root(), 0)); - iterator &iter = res.first; - if (res.second == kExactMatch) { - // The key already exists in the tree, do nothing. - return std::make_pair(internal_last(iter), false); - } else if (!res.second) { - iterator last = internal_last(iter); - if (last.node && !compare_keys(key, last.key())) { - // The key already exists in the tree, do nothing. - return std::make_pair(last, false); - } - } - - return std::make_pair(internal_insert(iter, *value), true); -} - -template -inline typename btree

::iterator -btree

::insert_unique(iterator position, const value_type &v) { - if (!empty()) { - const key_type &key = params_type::key(v); - if (position == end() || compare_keys(key, position.key())) { - iterator prev = position; - if (position == begin() || compare_keys((--prev).key(), key)) { - // prev.key() < key < position.key() - return internal_insert(position, v); - } - } else if (compare_keys(position.key(), key)) { - iterator next = position; - ++next; - if (next == end() || compare_keys(key, next.key())) { - // position.key() < key < next.key() - return internal_insert(next, v); - } - } else { - // position.key() == key - return position; - } - } - return insert_unique(v).first; -} - -template template -void btree

::insert_unique(InputIterator b, InputIterator e) { - for (; b != e; ++b) { - insert_unique(end(), *b); - } -} - -template template -typename btree

::iterator -btree

::insert_multi(const key_type &key, ValuePointer value) { - if (empty()) { - *mutable_root() = new_leaf_root_node(1); - } - - iterator iter = internal_upper_bound(key, iterator(root(), 0)); - if (!iter.node) { - iter = end(); - } - return internal_insert(iter, *value); -} - -template -typename btree

::iterator -btree

::insert_multi(iterator position, const value_type &v) { - if (!empty()) { - const key_type &key = params_type::key(v); - if (position == end() || !compare_keys(position.key(), key)) { - iterator prev = position; - if (position == begin() || !compare_keys(key, (--prev).key())) { - // prev.key() <= key <= position.key() - return internal_insert(position, v); - } - } else { - iterator next = position; - ++next; - if (next == end() || !compare_keys(next.key(), key)) { - // position.key() < key <= next.key() - return internal_insert(next, v); - } - } - } - return insert_multi(v); -} - -template template -void btree

::insert_multi(InputIterator b, InputIterator e) { - for (; b != e; ++b) { - insert_multi(end(), *b); - } -} - -template -void btree

::assign(const self_type &x) { - clear(); - - *mutable_key_comp() = x.key_comp(); - *mutable_internal_allocator() = x.internal_allocator(); - - // Assignment can avoid key comparisons because we know the order of the - // values is the same order we'll store them in. - for (const_iterator iter = x.begin(); iter != x.end(); ++iter) { - if (empty()) { - insert_multi(*iter); - } else { - // If the btree is not empty, we can just insert the new value at the end - // of the tree! - internal_insert(end(), *iter); - } - } -} - -template -typename btree

::iterator btree

::erase(iterator iter) { - bool internal_delete = false; - if (!iter.node->leaf()) { - // Deletion of a value on an internal node. Swap the key with the largest - // value of our left child. This is easy, we just decrement iter. - iterator tmp_iter(iter--); - assert(iter.node->leaf()); - assert(!compare_keys(tmp_iter.key(), iter.key())); - iter.node->value_swap(iter.position, tmp_iter.node, tmp_iter.position); - internal_delete = true; - --*mutable_size(); - } else if (!root()->leaf()) { - --*mutable_size(); - } - - // Delete the key from the leaf. - iter.node->remove_value(iter.position); - - // We want to return the next value after the one we just erased. If we - // erased from an internal node (internal_delete == true), then the next - // value is ++(++iter). If we erased from a leaf node (internal_delete == - // false) then the next value is ++iter. Note that ++iter may point to an - // internal node and the value in the internal node may move to a leaf node - // (iter.node) when rebalancing is performed at the leaf level. - - // Merge/rebalance as we walk back up the tree. - iterator res(iter); - for (;;) { - if (iter.node == root()) { - try_shrink(); - if (empty()) { - return end(); - } - break; - } - if (iter.node->count() >= kMinNodeValues) { - break; - } - bool merged = try_merge_or_rebalance(&iter); - if (iter.node->leaf()) { - res = iter; - } - if (!merged) { - break; - } - iter.node = iter.node->parent(); - } - - // Adjust our return value. If we're pointing at the end of a node, advance - // the iterator. - if (res.position == res.node->count()) { - res.position = res.node->count() - 1; - ++res; - } - // If we erased from an internal node, advance the iterator. - if (internal_delete) { - ++res; - } - return res; -} - -template -int btree

::erase(iterator begin, iterator end) { - int count = distance(begin, end); - for (int i = 0; i < count; i++) { - begin = erase(begin); - } - return count; -} - -template -int btree

::erase_unique(const key_type &key) { - iterator iter = internal_find_unique(key, iterator(root(), 0)); - if (!iter.node) { - // The key doesn't exist in the tree, return nothing done. - return 0; - } - erase(iter); - return 1; -} - -template -int btree

::erase_multi(const key_type &key) { - iterator begin = internal_lower_bound(key, iterator(root(), 0)); - if (!begin.node) { - // The key doesn't exist in the tree, return nothing done. - return 0; - } - // Delete all of the keys between begin and upper_bound(key). - iterator end = internal_end( - internal_upper_bound(key, iterator(root(), 0))); - return erase(begin, end); -} - -template -void btree

::clear() { - if (root() != NULL) { - internal_clear(root()); - } - *mutable_root() = NULL; -} - -template -void btree

::swap(self_type &x) { - std::swap(static_cast(*this), static_cast(x)); - std::swap(root_, x.root_); -} - -template -void btree

::verify() const { - if (root() != NULL) { - assert(size() == internal_verify(root(), NULL, NULL)); - assert(leftmost() == (++const_iterator(root(), -1)).node); - assert(rightmost() == (--const_iterator(root(), root()->count())).node); - assert(leftmost()->leaf()); - assert(rightmost()->leaf()); - } else { - assert(size() == 0); - assert(leftmost() == NULL); - assert(rightmost() == NULL); - } -} - -template -void btree

::rebalance_or_split(iterator *iter) { - node_type *&node = iter->node; - int &insert_position = iter->position; - assert(node->count() == node->max_count()); - - // First try to make room on the node by rebalancing. - node_type *parent = node->parent(); - if (node != root()) { - if (node->position() > 0) { - // Try rebalancing with our left sibling. - node_type *left = parent->child(node->position() - 1); - if (left->count() < left->max_count()) { - // We bias rebalancing based on the position being inserted. If we're - // inserting at the end of the right node then we bias rebalancing to - // fill up the left node. - int to_move = (left->max_count() - left->count()) / - (1 + (insert_position < left->max_count())); - to_move = std::max(1, to_move); - - if (((insert_position - to_move) >= 0) || - ((left->count() + to_move) < left->max_count())) { - left->rebalance_right_to_left(node, to_move); - - assert(node->max_count() - node->count() == to_move); - insert_position = insert_position - to_move; - if (insert_position < 0) { - insert_position = insert_position + left->count() + 1; - node = left; - } - - assert(node->count() < node->max_count()); - return; - } - } - } - - if (node->position() < parent->count()) { - // Try rebalancing with our right sibling. - node_type *right = parent->child(node->position() + 1); - if (right->count() < right->max_count()) { - // We bias rebalancing based on the position being inserted. If we're - // inserting at the beginning of the left node then we bias rebalancing - // to fill up the right node. - int to_move = (right->max_count() - right->count()) / - (1 + (insert_position > 0)); - to_move = std::max(1, to_move); - - if ((insert_position <= (node->count() - to_move)) || - ((right->count() + to_move) < right->max_count())) { - node->rebalance_left_to_right(right, to_move); - - if (insert_position > node->count()) { - insert_position = insert_position - node->count() - 1; - node = right; - } - - assert(node->count() < node->max_count()); - return; - } - } - } - - // Rebalancing failed, make sure there is room on the parent node for a new - // value. - if (parent->count() == parent->max_count()) { - iterator parent_iter(node->parent(), node->position()); - rebalance_or_split(&parent_iter); - } - } else { - // Rebalancing not possible because this is the root node. - if (root()->leaf()) { - // The root node is currently a leaf node: create a new root node and set - // the current root node as the child of the new root. - parent = new_internal_root_node(); - parent->set_child(0, root()); - *mutable_root() = parent; - assert(*mutable_rightmost() == parent->child(0)); - } else { - // The root node is an internal node. We do not want to create a new root - // node because the root node is special and holds the size of the tree - // and a pointer to the rightmost node. So we create a new internal node - // and move all of the items on the current root into the new node. - parent = new_internal_node(parent); - parent->set_child(0, parent); - parent->swap(root()); - node = parent; - } - } - - // Split the node. - node_type *split_node; - if (node->leaf()) { - split_node = new_leaf_node(parent); - node->split(split_node, insert_position); - if (rightmost() == node) { - *mutable_rightmost() = split_node; - } - } else { - split_node = new_internal_node(parent); - node->split(split_node, insert_position); - } - - if (insert_position > node->count()) { - insert_position = insert_position - node->count() - 1; - node = split_node; - } -} - -template -void btree

::merge_nodes(node_type *left, node_type *right) { - left->merge(right); - if (right->leaf()) { - if (rightmost() == right) { - *mutable_rightmost() = left; - } - delete_leaf_node(right); - } else { - delete_internal_node(right); - } -} - -template -bool btree

::try_merge_or_rebalance(iterator *iter) { - node_type *parent = iter->node->parent(); - if (iter->node->position() > 0) { - // Try merging with our left sibling. - node_type *left = parent->child(iter->node->position() - 1); - if ((1 + left->count() + iter->node->count()) <= left->max_count()) { - iter->position += 1 + left->count(); - merge_nodes(left, iter->node); - iter->node = left; - return true; - } - } - if (iter->node->position() < parent->count()) { - // Try merging with our right sibling. - node_type *right = parent->child(iter->node->position() + 1); - if ((1 + iter->node->count() + right->count()) <= right->max_count()) { - merge_nodes(iter->node, right); - return true; - } - // Try rebalancing with our right sibling. We don't perform rebalancing if - // we deleted the first element from iter->node and the node is not - // empty. This is a small optimization for the common pattern of deleting - // from the front of the tree. - if ((right->count() > kMinNodeValues) && - ((iter->node->count() == 0) || - (iter->position > 0))) { - int to_move = (right->count() - iter->node->count()) / 2; - to_move = std::min(to_move, right->count() - 1); - iter->node->rebalance_right_to_left(right, to_move); - return false; - } - } - if (iter->node->position() > 0) { - // Try rebalancing with our left sibling. We don't perform rebalancing if - // we deleted the last element from iter->node and the node is not - // empty. This is a small optimization for the common pattern of deleting - // from the back of the tree. - node_type *left = parent->child(iter->node->position() - 1); - if ((left->count() > kMinNodeValues) && - ((iter->node->count() == 0) || - (iter->position < iter->node->count()))) { - int to_move = (left->count() - iter->node->count()) / 2; - to_move = std::min(to_move, left->count() - 1); - left->rebalance_left_to_right(iter->node, to_move); - iter->position += to_move; - return false; - } - } - return false; -} - -template -void btree

::try_shrink() { - if (root()->count() > 0) { - return; - } - // Deleted the last item on the root node, shrink the height of the tree. - if (root()->leaf()) { - assert(size() == 0); - delete_leaf_node(root()); - *mutable_root() = NULL; - } else { - node_type *child = root()->child(0); - if (child->leaf()) { - // The child is a leaf node so simply make it the root node in the tree. - child->make_root(); - delete_internal_root_node(); - *mutable_root() = child; - } else { - // The child is an internal node. We want to keep the existing root node - // so we move all of the values from the child node into the existing - // (empty) root node. - child->swap(root()); - delete_internal_node(child); - } - } -} - -template template -inline IterType btree

::internal_last(IterType iter) { - while (iter.node && iter.position == iter.node->count()) { - iter.position = iter.node->position(); - iter.node = iter.node->parent(); - if (iter.node->leaf()) { - iter.node = NULL; - } - } - return iter; -} - -template -inline typename btree

::iterator -btree

::internal_insert(iterator iter, const value_type &v) { - if (!iter.node->leaf()) { - // We can't insert on an internal node. Instead, we'll insert after the - // previous value which is guaranteed to be on a leaf node. - --iter; - ++iter.position; - } - if (iter.node->count() == iter.node->max_count()) { - // Make room in the leaf for the new item. - if (iter.node->max_count() < kNodeValues) { - // Insertion into the root where the root is smaller that the full node - // size. Simply grow the size of the root node. - assert(iter.node == root()); - iter.node = new_leaf_root_node( - std::min(kNodeValues, 2 * iter.node->max_count())); - iter.node->swap(root()); - delete_leaf_node(root()); - *mutable_root() = iter.node; - } else { - rebalance_or_split(&iter); - ++*mutable_size(); - } - } else if (!root()->leaf()) { - ++*mutable_size(); - } - iter.node->insert_value(iter.position, v); - return iter; -} - -template template -inline std::pair btree

::internal_locate( - const key_type &key, IterType iter) const { - return internal_locate_type::dispatch(key, *this, iter); -} - -template template -inline std::pair btree

::internal_locate_plain_compare( - const key_type &key, IterType iter) const { - for (;;) { - iter.position = iter.node->lower_bound(key, key_comp()); - if (iter.node->leaf()) { - break; - } - iter.node = iter.node->child(iter.position); - } - return std::make_pair(iter, 0); -} - -template template -inline std::pair btree

::internal_locate_compare_to( - const key_type &key, IterType iter) const { - for (;;) { - int res = iter.node->lower_bound(key, key_comp()); - iter.position = res & kMatchMask; - if (res & kExactMatch) { - return std::make_pair(iter, static_cast(kExactMatch)); - } - if (iter.node->leaf()) { - break; - } - iter.node = iter.node->child(iter.position); - } - return std::make_pair(iter, -kExactMatch); -} - -template template -IterType btree

::internal_lower_bound( - const key_type &key, IterType iter) const { - if (iter.node) { - for (;;) { - iter.position = - iter.node->lower_bound(key, key_comp()) & kMatchMask; - if (iter.node->leaf()) { - break; - } - iter.node = iter.node->child(iter.position); - } - iter = internal_last(iter); - } - return iter; -} - -template template -IterType btree

::internal_upper_bound( - const key_type &key, IterType iter) const { - if (iter.node) { - for (;;) { - iter.position = iter.node->upper_bound(key, key_comp()); - if (iter.node->leaf()) { - break; - } - iter.node = iter.node->child(iter.position); - } - iter = internal_last(iter); - } - return iter; -} - -template template -IterType btree

::internal_find_unique( - const key_type &key, IterType iter) const { - if (iter.node) { - std::pair res = internal_locate(key, iter); - if (res.second == kExactMatch) { - return res.first; - } - if (!res.second) { - iter = internal_last(res.first); - if (iter.node && !compare_keys(key, iter.key())) { - return iter; - } - } - } - return IterType(NULL, 0); -} - -template template -IterType btree

::internal_find_multi( - const key_type &key, IterType iter) const { - if (iter.node) { - iter = internal_lower_bound(key, iter); - if (iter.node) { - iter = internal_last(iter); - if (iter.node && !compare_keys(key, iter.key())) { - return iter; - } - } - } - return IterType(NULL, 0); -} - -template -void btree

::internal_clear(node_type *node) { - if (!node->leaf()) { - for (int i = 0; i <= node->count(); ++i) { - internal_clear(node->child(i)); - } - if (node == root()) { - delete_internal_root_node(); - } else { - delete_internal_node(node); - } - } else { - delete_leaf_node(node); - } -} - -template -void btree

::internal_dump( - std::ostream &os, const node_type *node, int level) const { - for (int i = 0; i < node->count(); ++i) { - if (!node->leaf()) { - internal_dump(os, node->child(i), level + 1); - } - for (int j = 0; j < level; ++j) { - os << " "; - } - os << node->key(i) << " [" << level << "]\n"; - } - if (!node->leaf()) { - internal_dump(os, node->child(node->count()), level + 1); - } -} - -template -int btree

::internal_verify( - const node_type *node, const key_type *lo, const key_type *hi) const { - assert(node->count() > 0); - assert(node->count() <= node->max_count()); - if (lo) { - assert(!compare_keys(node->key(0), *lo)); - } - if (hi) { - assert(!compare_keys(*hi, node->key(node->count() - 1))); - } - for (int i = 1; i < node->count(); ++i) { - assert(!compare_keys(node->key(i), node->key(i - 1))); - } - int count = node->count(); - if (!node->leaf()) { - for (int i = 0; i <= node->count(); ++i) { - assert(node->child(i) != NULL); - assert(node->child(i)->parent() == node); - assert(node->child(i)->position() == i); - count += internal_verify( - node->child(i), - (i == 0) ? lo : &node->key(i - 1), - (i == node->count()) ? hi : &node->key(i)); - } - } - return count; -} - -} // namespace btree - -#endif // UTIL_BTREE_BTREE_H__ diff --git a/tommyds/benchmark/lib/cpp-btree/btree_bench.cc b/tommyds/benchmark/lib/cpp-btree/btree_bench.cc deleted file mode 100644 index d5495c5..0000000 --- a/tommyds/benchmark/lib/cpp-btree/btree_bench.cc +++ /dev/null @@ -1,594 +0,0 @@ -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gflags/gflags.h" -#include "btree_map.h" -#include "btree_set.h" -#include "btree_test.h" - -DEFINE_int32(test_random_seed, 123456789, "Seed for srand()"); -DEFINE_int32(benchmark_max_iters, 10000000, "Maximum test iterations"); -DEFINE_int32(benchmark_min_iters, 100, "Minimum test iterations"); -DEFINE_int32(benchmark_target_seconds, 1, - "Attempt to benchmark for this many seconds"); - -using std::allocator; -using std::less; -using std::map; -using std::max; -using std::min; -using std::multimap; -using std::multiset; -using std::set; -using std::string; -using std::vector; - -namespace btree { -namespace { - -struct RandGen { - typedef ptrdiff_t result_type; - RandGen(result_type seed) { - srand(seed); - } - result_type operator()(result_type l) { - return rand() % l; - } -}; - -struct BenchmarkRun { - BenchmarkRun(const char *name, void (*func)(int)); - void Run(); - void Stop(); - void Start(); - void Reset(); - - BenchmarkRun *next_benchmark; - const char *benchmark_name; - void (*benchmark_func)(int); - int64_t accum_micros; - int64_t last_started; -}; - -BenchmarkRun *first_benchmark; -BenchmarkRun *current_benchmark; - -int64_t get_micros () { - timeval tv; - gettimeofday(&tv, NULL); - return tv.tv_sec * 1000000 + tv.tv_usec; -} - -BenchmarkRun::BenchmarkRun(const char *name, void (*func)(int)) - : next_benchmark(first_benchmark), - benchmark_name(name), - benchmark_func(func), - accum_micros(0), - last_started(0) { - first_benchmark = this; -} - -#define BTREE_BENCHMARK(name) \ - BTREE_BENCHMARK2(#name, name, __COUNTER__) -#define BTREE_BENCHMARK2(name, func, counter) \ - BTREE_BENCHMARK3(name, func, counter) -#define BTREE_BENCHMARK3(name, func, counter) \ - BenchmarkRun bench ## counter (name, func) - -void StopBenchmarkTiming() { - current_benchmark->Stop(); -} - -void StartBenchmarkTiming() { - current_benchmark->Start(); -} - -void RunBenchmarks() { - for (BenchmarkRun *bench = first_benchmark; bench; - bench = bench->next_benchmark) { - bench->Run(); - } -} - -void BenchmarkRun::Start() { - assert(!last_started); - last_started = get_micros(); -} - -void BenchmarkRun::Stop() { - if (last_started == 0) { - return; - } - accum_micros += get_micros() - last_started; - last_started = 0; -} - -void BenchmarkRun::Reset() { - last_started = 0; - accum_micros = 0; -} - -void BenchmarkRun::Run() { - assert(current_benchmark == NULL); - current_benchmark = this; - int iters = FLAGS_benchmark_min_iters; - for (;;) { - Reset(); - Start(); - benchmark_func(iters); - Stop(); - if (accum_micros > FLAGS_benchmark_target_seconds * 1000000 || - iters >= FLAGS_benchmark_max_iters) { - break; - } else if (accum_micros == 0) { - iters *= 100; - } else { - int64_t target_micros = FLAGS_benchmark_target_seconds * 1000000; - iters = target_micros * iters / accum_micros; - } - iters = min(iters, FLAGS_benchmark_max_iters); - } - fprintf(stdout, "%s\t%qu\t%qu\n", - benchmark_name, - accum_micros * 1000 / iters, - iters); - current_benchmark = NULL; -} - -// Used to avoid compiler optimizations for these benchmarks. -template -void sink(const T& t0) { - volatile T t = t0; -} - -// Benchmark insertion of values into a container. -template -void BM_Insert(int n) { - typedef typename std::remove_const::type V; - typename KeyOfValue::type key_of_value; - - // Disable timing while we perform some initialization. - StopBenchmarkTiming(); - - T container; - vector values = GenerateValues(FLAGS_benchmark_values); - for (int i = 0; i < values.size(); i++) { - container.insert(values[i]); - } - - for (int i = 0; i < n; ) { - // Remove and re-insert 10% of the keys - int m = min(n - i, FLAGS_benchmark_values / 10); - - for (int j = i; j < i + m; j++) { - int x = j % FLAGS_benchmark_values; - container.erase(key_of_value(values[x])); - } - - StartBenchmarkTiming(); - - for (int j = i; j < i + m; j++) { - int x = j % FLAGS_benchmark_values; - container.insert(values[x]); - } - - StopBenchmarkTiming(); - - i += m; - } -} - -// Benchmark lookup of values in a container. -template -void BM_Lookup(int n) { - typedef typename std::remove_const::type V; - typename KeyOfValue::type key_of_value; - - // Disable timing while we perform some initialization. - StopBenchmarkTiming(); - - T container; - vector values = GenerateValues(FLAGS_benchmark_values); - - for (int i = 0; i < values.size(); i++) { - container.insert(values[i]); - } - - V r = V(); - - StartBenchmarkTiming(); - - for (int i = 0; i < n; i++) { - int m = i % values.size(); - r = *container.find(key_of_value(values[m])); - } - - StopBenchmarkTiming(); - - sink(r); // Keep compiler from optimizing away r. -} - -// Benchmark lookup of values in a full container, meaning that values -// are inserted in-order to take advantage of biased insertion, which -// yields a full tree. -template -void BM_FullLookup(int n) { - typedef typename std::remove_const::type V; - typename KeyOfValue::type key_of_value; - - // Disable timing while we perform some initialization. - StopBenchmarkTiming(); - - T container; - vector values = GenerateValues(FLAGS_benchmark_values); - vector sorted(values); - sort(sorted.begin(), sorted.end()); - - for (int i = 0; i < sorted.size(); i++) { - container.insert(sorted[i]); - } - - V r = V(); - - StartBenchmarkTiming(); - - for (int i = 0; i < n; i++) { - int m = i % values.size(); - r = *container.find(key_of_value(values[m])); - } - - StopBenchmarkTiming(); - - sink(r); // Keep compiler from optimizing away r. -} - -// Benchmark deletion of values from a container. -template -void BM_Delete(int n) { - typedef typename std::remove_const::type V; - typename KeyOfValue::type key_of_value; - - // Disable timing while we perform some initialization. - StopBenchmarkTiming(); - - T container; - vector values = GenerateValues(FLAGS_benchmark_values); - for (int i = 0; i < values.size(); i++) { - container.insert(values[i]); - } - - for (int i = 0; i < n; ) { - // Remove and re-insert 10% of the keys - int m = min(n - i, FLAGS_benchmark_values / 10); - - StartBenchmarkTiming(); - - for (int j = i; j < i + m; j++) { - int x = j % FLAGS_benchmark_values; - container.erase(key_of_value(values[x])); - } - - StopBenchmarkTiming(); - - for (int j = i; j < i + m; j++) { - int x = j % FLAGS_benchmark_values; - container.insert(values[x]); - } - - i += m; - } -} - -// Benchmark steady-state insert (into first half of range) and remove -// (from second second half of range), treating the container -// approximately like a queue with log-time access for all elements. -// This benchmark does not test the case where insertion and removal -// happen in the same region of the tree. This benchmark counts two -// value constructors. -template -void BM_QueueAddRem(int n) { - typedef typename std::remove_const::type V; - typename KeyOfValue::type key_of_value; - - // Disable timing while we perform some initialization. - StopBenchmarkTiming(); - assert(FLAGS_benchmark_values % 2 == 0); - - T container; - - const int half = FLAGS_benchmark_values / 2; - vector remove_keys(half); - vector add_keys(half); - - for (int i = 0; i < half; i++) { - remove_keys[i] = i; - add_keys[i] = i; - } - - RandGen rand(FLAGS_test_random_seed); - - random_shuffle(remove_keys.begin(), remove_keys.end(), rand); - random_shuffle(add_keys.begin(), add_keys.end(), rand); - - Generator g(FLAGS_benchmark_values + FLAGS_benchmark_max_iters); - - for (int i = 0; i < half; i++) { - container.insert(g(add_keys[i])); - container.insert(g(half + remove_keys[i])); - } - - // There are three parts each of size "half": - // 1 is being deleted from [offset - half, offset) - // 2 is standing [offset, offset + half) - // 3 is being inserted into [offset + half, offset + 2 * half) - int offset = 0; - - StartBenchmarkTiming(); - - for (int i = 0; i < n; i++) { - int idx = i % half; - - if (idx == 0) { - StopBenchmarkTiming(); - random_shuffle(remove_keys.begin(), remove_keys.end(), rand); - random_shuffle(add_keys.begin(), add_keys.end(), rand); - offset += half; - StartBenchmarkTiming(); - } - - int e = container.erase(key_of_value(g(offset - half + remove_keys[idx]))); - assert(e == 1); - container.insert(g(offset + half + add_keys[idx])); - } - - StopBenchmarkTiming(); -} - -// Mixed insertion and deletion in the same range using pre-constructed values. -template -void BM_MixedAddRem(int n) { - typedef typename std::remove_const::type V; - typename KeyOfValue::type key_of_value; - - // Disable timing while we perform some initialization. - StopBenchmarkTiming(); - assert(FLAGS_benchmark_values % 2 == 0); - - T container; - RandGen rand(FLAGS_test_random_seed); - - vector values = GenerateValues(FLAGS_benchmark_values * 2); - - // Create two random shuffles - vector remove_keys(FLAGS_benchmark_values); - vector add_keys(FLAGS_benchmark_values); - - // Insert the first half of the values (already in random order) - for (int i = 0; i < FLAGS_benchmark_values; i++) { - container.insert(values[i]); - - // remove_keys and add_keys will be swapped before each round, - // therefore fill add_keys here w/ the keys being inserted, so - // they'll be the first to be removed. - remove_keys[i] = i + FLAGS_benchmark_values; - add_keys[i] = i; - } - - StartBenchmarkTiming(); - - for (int i = 0; i < n; i++) { - int idx = i % FLAGS_benchmark_values; - - if (idx == 0) { - StopBenchmarkTiming(); - remove_keys.swap(add_keys); - random_shuffle(remove_keys.begin(), remove_keys.end(), rand); - random_shuffle(add_keys.begin(), add_keys.end(), rand); - StartBenchmarkTiming(); - } - - int e = container.erase(key_of_value(values[remove_keys[idx]])); - assert(e == 1); - container.insert(values[add_keys[idx]]); - } - - StopBenchmarkTiming(); -} - -// Insertion at end, removal from the beginning. This benchmark -// counts two value constructors. -template -void BM_Fifo(int n) { - typedef typename std::remove_const::type V; - - // Disable timing while we perform some initialization. - StopBenchmarkTiming(); - - T container; - Generator g(FLAGS_benchmark_values + FLAGS_benchmark_max_iters); - - for (int i = 0; i < FLAGS_benchmark_values; i++) { - container.insert(g(i)); - } - - StartBenchmarkTiming(); - - for (int i = 0; i < n; i++) { - container.erase(container.begin()); - container.insert(container.end(), g(i + FLAGS_benchmark_values)); - } - - StopBenchmarkTiming(); -} - -// Iteration (forward) through the tree -template -void BM_FwdIter(int n) { - typedef typename std::remove_const::type V; - - // Disable timing while we perform some initialization. - StopBenchmarkTiming(); - - T container; - vector values = GenerateValues(FLAGS_benchmark_values); - - for (int i = 0; i < FLAGS_benchmark_values; i++) { - container.insert(values[i]); - } - - typename T::iterator iter; - - V r = V(); - - StartBenchmarkTiming(); - - for (int i = 0; i < n; i++) { - int idx = i % FLAGS_benchmark_values; - - if (idx == 0) { - iter = container.begin(); - } - r = *iter; - ++iter; - } - - StopBenchmarkTiming(); - - sink(r); // Keep compiler from optimizing away r. -} - -typedef set stl_set_int32; -typedef set stl_set_int64; -typedef set stl_set_string; - -typedef map stl_map_int32; -typedef map stl_map_int64; -typedef map stl_map_string; - -typedef multiset stl_multiset_int32; -typedef multiset stl_multiset_int64; -typedef multiset stl_multiset_string; - -typedef multimap stl_multimap_int32; -typedef multimap stl_multimap_int64; -typedef multimap stl_multimap_string; - -#define MY_BENCHMARK_TYPES2(value, name, size) \ - typedef btree ## _set, allocator, size> \ - btree ## _ ## size ## _set_ ## name; \ - typedef btree ## _map, allocator, size> \ - btree ## _ ## size ## _map_ ## name; \ - typedef btree ## _multiset, allocator, size> \ - btree ## _ ## size ## _multiset_ ## name; \ - typedef btree ## _multimap, allocator, size> \ - btree ## _ ## size ## _multimap_ ## name - -#define MY_BENCHMARK_TYPES(value, name) \ - MY_BENCHMARK_TYPES2(value, name, 128); \ - MY_BENCHMARK_TYPES2(value, name, 160); \ - MY_BENCHMARK_TYPES2(value, name, 192); \ - MY_BENCHMARK_TYPES2(value, name, 224); \ - MY_BENCHMARK_TYPES2(value, name, 256); \ - MY_BENCHMARK_TYPES2(value, name, 288); \ - MY_BENCHMARK_TYPES2(value, name, 320); \ - MY_BENCHMARK_TYPES2(value, name, 352); \ - MY_BENCHMARK_TYPES2(value, name, 384); \ - MY_BENCHMARK_TYPES2(value, name, 416); \ - MY_BENCHMARK_TYPES2(value, name, 448); \ - MY_BENCHMARK_TYPES2(value, name, 480); \ - MY_BENCHMARK_TYPES2(value, name, 512); \ - MY_BENCHMARK_TYPES2(value, name, 1024); \ - MY_BENCHMARK_TYPES2(value, name, 1536); \ - MY_BENCHMARK_TYPES2(value, name, 2048) - -MY_BENCHMARK_TYPES(int32_t, int32); -MY_BENCHMARK_TYPES(int64_t, int64); -MY_BENCHMARK_TYPES(string, string); - -#define MY_BENCHMARK4(type, name, func) \ - void BM_ ## type ## _ ## name(int n) { BM_ ## func (n); } \ - BTREE_BENCHMARK(BM_ ## type ## _ ## name) - -// Define NODESIZE_TESTING when running btree_perf.py. - -#ifdef NODESIZE_TESTING -#define MY_BENCHMARK3(tree, type, name, func) \ - MY_BENCHMARK4(tree ## _128_ ## type, name, func); \ - MY_BENCHMARK4(tree ## _160_ ## type, name, func); \ - MY_BENCHMARK4(tree ## _192_ ## type, name, func); \ - MY_BENCHMARK4(tree ## _224_ ## type, name, func); \ - MY_BENCHMARK4(tree ## _256_ ## type, name, func); \ - MY_BENCHMARK4(tree ## _288_ ## type, name, func); \ - MY_BENCHMARK4(tree ## _320_ ## type, name, func); \ - MY_BENCHMARK4(tree ## _352_ ## type, name, func); \ - MY_BENCHMARK4(tree ## _384_ ## type, name, func); \ - MY_BENCHMARK4(tree ## _416_ ## type, name, func); \ - MY_BENCHMARK4(tree ## _448_ ## type, name, func); \ - MY_BENCHMARK4(tree ## _480_ ## type, name, func); \ - MY_BENCHMARK4(tree ## _512_ ## type, name, func); \ - MY_BENCHMARK4(tree ## _1024_ ## type, name, func); \ - MY_BENCHMARK4(tree ## _1536_ ## type, name, func); \ - MY_BENCHMARK4(tree ## _2048_ ## type, name, func) -#else -#define MY_BENCHMARK3(tree, type, name, func) \ - MY_BENCHMARK4(tree ## _256_ ## type, name, func); \ - MY_BENCHMARK4(tree ## _2048_ ## type, name, func) -#endif - -#define MY_BENCHMARK2(type, name, func) \ - MY_BENCHMARK4(stl_ ## type, name, func); \ - MY_BENCHMARK3(btree, type, name, func) - -#define MY_BENCHMARK(type) \ - MY_BENCHMARK2(type, insert, Insert); \ - MY_BENCHMARK2(type, lookup, Lookup); \ - MY_BENCHMARK2(type, fulllookup, FullLookup); \ - MY_BENCHMARK2(type, delete, Delete); \ - MY_BENCHMARK2(type, queueaddrem, QueueAddRem); \ - MY_BENCHMARK2(type, mixedaddrem, MixedAddRem); \ - MY_BENCHMARK2(type, fifo, Fifo); \ - MY_BENCHMARK2(type, fwditer, FwdIter) - -MY_BENCHMARK(set_int32); -MY_BENCHMARK(map_int32); -MY_BENCHMARK(set_int64); -MY_BENCHMARK(map_int64); -MY_BENCHMARK(set_string); -MY_BENCHMARK(map_string); - -MY_BENCHMARK(multiset_int32); -MY_BENCHMARK(multimap_int32); -MY_BENCHMARK(multiset_int64); -MY_BENCHMARK(multimap_int64); -MY_BENCHMARK(multiset_string); -MY_BENCHMARK(multimap_string); - -} // namespace -} // namespace btree - -int main(int argc, char **argv) { - btree::RunBenchmarks(); - return 0; -} diff --git a/tommyds/benchmark/lib/cpp-btree/btree_container.h b/tommyds/benchmark/lib/cpp-btree/btree_container.h deleted file mode 100644 index c9c62c3..0000000 --- a/tommyds/benchmark/lib/cpp-btree/btree_container.h +++ /dev/null @@ -1,350 +0,0 @@ -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef UTIL_BTREE_BTREE_CONTAINER_H__ -#define UTIL_BTREE_BTREE_CONTAINER_H__ - -#include -#include - -#include "btree.h" - -namespace btree { - -// A common base class for btree_set, btree_map, btree_multiset and -// btree_multimap. -template -class btree_container { - typedef btree_container self_type; - - public: - typedef typename Tree::params_type params_type; - typedef typename Tree::key_type key_type; - typedef typename Tree::value_type value_type; - typedef typename Tree::key_compare key_compare; - typedef typename Tree::allocator_type allocator_type; - typedef typename Tree::pointer pointer; - typedef typename Tree::const_pointer const_pointer; - typedef typename Tree::reference reference; - typedef typename Tree::const_reference const_reference; - typedef typename Tree::size_type size_type; - typedef typename Tree::difference_type difference_type; - typedef typename Tree::iterator iterator; - typedef typename Tree::const_iterator const_iterator; - typedef typename Tree::reverse_iterator reverse_iterator; - typedef typename Tree::const_reverse_iterator const_reverse_iterator; - - public: - // Default constructor. - btree_container(const key_compare &comp, const allocator_type &alloc) - : tree_(comp, alloc) { - } - - // Copy constructor. - btree_container(const self_type &x) - : tree_(x.tree_) { - } - - // Iterator routines. - iterator begin() { return tree_.begin(); } - const_iterator begin() const { return tree_.begin(); } - iterator end() { return tree_.end(); } - const_iterator end() const { return tree_.end(); } - reverse_iterator rbegin() { return tree_.rbegin(); } - const_reverse_iterator rbegin() const { return tree_.rbegin(); } - reverse_iterator rend() { return tree_.rend(); } - const_reverse_iterator rend() const { return tree_.rend(); } - - // Lookup routines. - iterator lower_bound(const key_type &key) { - return tree_.lower_bound(key); - } - const_iterator lower_bound(const key_type &key) const { - return tree_.lower_bound(key); - } - iterator upper_bound(const key_type &key) { - return tree_.upper_bound(key); - } - const_iterator upper_bound(const key_type &key) const { - return tree_.upper_bound(key); - } - std::pair equal_range(const key_type &key) { - return tree_.equal_range(key); - } - std::pair equal_range(const key_type &key) const { - return tree_.equal_range(key); - } - - // Utility routines. - void clear() { - tree_.clear(); - } - void swap(self_type &x) { - tree_.swap(x.tree_); - } - void dump(std::ostream &os) const { - tree_.dump(os); - } - void verify() const { - tree_.verify(); - } - - // Size routines. - size_type size() const { return tree_.size(); } - size_type max_size() const { return tree_.max_size(); } - bool empty() const { return tree_.empty(); } - size_type height() const { return tree_.height(); } - size_type internal_nodes() const { return tree_.internal_nodes(); } - size_type leaf_nodes() const { return tree_.leaf_nodes(); } - size_type nodes() const { return tree_.nodes(); } - size_type bytes_used() const { return tree_.bytes_used(); } - static double average_bytes_per_value() { - return Tree::average_bytes_per_value(); - } - double fullness() const { return tree_.fullness(); } - double overhead() const { return tree_.overhead(); } - - bool operator==(const self_type& x) const { - if (size() != x.size()) { - return false; - } - for (const_iterator i = begin(), xi = x.begin(); i != end(); ++i, ++xi) { - if (*i != *xi) { - return false; - } - } - return true; - } - - bool operator!=(const self_type& other) const { - return !operator==(other); - } - - - protected: - Tree tree_; -}; - -template -inline std::ostream& operator<<(std::ostream &os, const btree_container &b) { - b.dump(os); - return os; -} - -// A common base class for btree_set and safe_btree_set. -template -class btree_unique_container : public btree_container { - typedef btree_unique_container self_type; - typedef btree_container super_type; - - public: - typedef typename Tree::key_type key_type; - typedef typename Tree::value_type value_type; - typedef typename Tree::size_type size_type; - typedef typename Tree::key_compare key_compare; - typedef typename Tree::allocator_type allocator_type; - typedef typename Tree::iterator iterator; - typedef typename Tree::const_iterator const_iterator; - - public: - // Default constructor. - btree_unique_container(const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(comp, alloc) { - } - - // Copy constructor. - btree_unique_container(const self_type &x) - : super_type(x) { - } - - // Range constructor. - template - btree_unique_container(InputIterator b, InputIterator e, - const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(comp, alloc) { - insert(b, e); - } - - // Lookup routines. - iterator find(const key_type &key) { - return this->tree_.find_unique(key); - } - const_iterator find(const key_type &key) const { - return this->tree_.find_unique(key); - } - size_type count(const key_type &key) const { - return this->tree_.count_unique(key); - } - - // Insertion routines. - std::pair insert(const value_type &x) { - return this->tree_.insert_unique(x); - } - iterator insert(iterator position, const value_type &x) { - return this->tree_.insert_unique(position, x); - } - template - void insert(InputIterator b, InputIterator e) { - this->tree_.insert_unique(b, e); - } - - // Deletion routines. - int erase(const key_type &key) { - return this->tree_.erase_unique(key); - } - // Erase the specified iterator from the btree. The iterator must be valid - // (i.e. not equal to end()). Return an iterator pointing to the node after - // the one that was erased (or end() if none exists). - iterator erase(const iterator &iter) { - return this->tree_.erase(iter); - } - void erase(const iterator &first, const iterator &last) { - this->tree_.erase(first, last); - } -}; - -// A common base class for btree_map and safe_btree_map. -template -class btree_map_container : public btree_unique_container { - typedef btree_map_container self_type; - typedef btree_unique_container super_type; - - public: - typedef typename Tree::key_type key_type; - typedef typename Tree::data_type data_type; - typedef typename Tree::value_type value_type; - typedef typename Tree::mapped_type mapped_type; - typedef typename Tree::key_compare key_compare; - typedef typename Tree::allocator_type allocator_type; - - private: - // A pointer-like object which only generates its value when - // dereferenced. Used by operator[] to avoid constructing an empty data_type - // if the key already exists in the map. - struct generate_value { - generate_value(const key_type &k) - : key(k) { - } - value_type operator*() const { - return std::make_pair(key, data_type()); - } - const key_type &key; - }; - - public: - // Default constructor. - btree_map_container(const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(comp, alloc) { - } - - // Copy constructor. - btree_map_container(const self_type &x) - : super_type(x) { - } - - // Range constructor. - template - btree_map_container(InputIterator b, InputIterator e, - const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(comp, alloc) { - insert(b, e); - } - - // Insertion routines. - data_type& operator[](const key_type &key) { - return this->tree_.insert_unique(key, generate_value(key)).first->second; - } -}; - -// A common base class for btree_multiset and btree_multimap. -template -class btree_multi_container : public btree_container { - typedef btree_multi_container self_type; - typedef btree_container super_type; - - public: - typedef typename Tree::key_type key_type; - typedef typename Tree::value_type value_type; - typedef typename Tree::size_type size_type; - typedef typename Tree::key_compare key_compare; - typedef typename Tree::allocator_type allocator_type; - typedef typename Tree::iterator iterator; - typedef typename Tree::const_iterator const_iterator; - - public: - // Default constructor. - btree_multi_container(const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(comp, alloc) { - } - - // Copy constructor. - btree_multi_container(const self_type &x) - : super_type(x) { - } - - // Range constructor. - template - btree_multi_container(InputIterator b, InputIterator e, - const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(b, e, comp, alloc) { - insert(b, e); - } - - // Lookup routines. - iterator find(const key_type &key) { - return this->tree_.find_multi(key); - } - const_iterator find(const key_type &key) const { - return this->tree_.find_multi(key); - } - size_type count(const key_type &key) const { - return this->tree_.count_multi(key); - } - - // Insertion routines. - iterator insert(const value_type &x) { - return this->tree_.insert_multi(x); - } - iterator insert(iterator position, const value_type &x) { - return this->tree_.insert_multi(position, x); - } - template - void insert(InputIterator b, InputIterator e) { - this->tree_.insert_multi(b, e); - } - - // Deletion routines. - int erase(const key_type &key) { - return this->tree_.erase_multi(key); - } - // Erase the specified iterator from the btree. The iterator must be valid - // (i.e. not equal to end()). Return an iterator pointing to the node after - // the one that was erased (or end() if none exists). - iterator erase(const iterator &iter) { - return this->tree_.erase(iter); - } - void erase(const iterator &first, const iterator &last) { - this->tree_.erase(first, last); - } -}; - -} // namespace btree - -#endif // UTIL_BTREE_BTREE_CONTAINER_H__ diff --git a/tommyds/benchmark/lib/cpp-btree/btree_map.h b/tommyds/benchmark/lib/cpp-btree/btree_map.h deleted file mode 100644 index 07b799e..0000000 --- a/tommyds/benchmark/lib/cpp-btree/btree_map.h +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// A btree_map<> implements the STL unique sorted associative container -// interface and the pair associative container interface (a.k.a map<>) using a -// btree. A btree_multimap<> implements the STL multiple sorted associative -// container interface and the pair associtive container interface (a.k.a -// multimap<>) using a btree. See btree.h for details of the btree -// implementation and caveats. - -#ifndef UTIL_BTREE_BTREE_MAP_H__ -#define UTIL_BTREE_BTREE_MAP_H__ - -#include -#include -#include -#include -#include - -#include "btree.h" -#include "btree_container.h" - -namespace btree { - -// The btree_map class is needed mainly for it's constructors. -template , - typename Alloc = std::allocator >, - int TargetNodeSize = 256> -class btree_map : public btree_map_container< - btree > > { - - typedef btree_map self_type; - typedef btree_map_params< - Key, Value, Compare, Alloc, TargetNodeSize> params_type; - typedef btree btree_type; - typedef btree_map_container super_type; - - public: - typedef typename btree_type::key_compare key_compare; - typedef typename btree_type::allocator_type allocator_type; - - public: - // Default constructor. - btree_map(const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(comp, alloc) { - } - - // Copy constructor. - btree_map(const self_type &x) - : super_type(x) { - } - - // Range constructor. - template - btree_map(InputIterator b, InputIterator e, - const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(comp, alloc) { - } -}; - -template -inline void swap(btree_map &x, - btree_map &y) { - x.swap(y); -} - -// The btree_multimap class is needed mainly for it's constructors. -template , - typename Alloc = std::allocator >, - int TargetNodeSize = 256> -class btree_multimap : public btree_multi_container< - btree > > { - - typedef btree_multimap self_type; - typedef btree_map_params< - Key, Value, Compare, Alloc, TargetNodeSize> params_type; - typedef btree btree_type; - typedef btree_multi_container super_type; - - public: - typedef typename btree_type::key_compare key_compare; - typedef typename btree_type::allocator_type allocator_type; - typedef typename btree_type::data_type data_type; - typedef typename btree_type::mapped_type mapped_type; - - public: - // Default constructor. - btree_multimap(const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(comp, alloc) { - } - - // Copy constructor. - btree_multimap(const self_type &x) - : super_type(x) { - } - - // Range constructor. - template - btree_multimap(InputIterator b, InputIterator e, - const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(b, e, comp, alloc) { - } -}; - -template -inline void swap(btree_multimap &x, - btree_multimap &y) { - x.swap(y); -} - -} // namespace btree - -#endif // UTIL_BTREE_BTREE_MAP_H__ diff --git a/tommyds/benchmark/lib/cpp-btree/btree_set.h b/tommyds/benchmark/lib/cpp-btree/btree_set.h deleted file mode 100644 index 2bc9e58..0000000 --- a/tommyds/benchmark/lib/cpp-btree/btree_set.h +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// A btree_set<> implements the STL unique sorted associative container -// interface (a.k.a set<>) using a btree. A btree_multiset<> implements the STL -// multiple sorted associative container interface (a.k.a multiset<>) using a -// btree. See btree.h for details of the btree implementation and caveats. - -#ifndef UTIL_BTREE_BTREE_SET_H__ -#define UTIL_BTREE_BTREE_SET_H__ - -#include -#include -#include - -#include "btree.h" -#include "btree_container.h" - -namespace btree { - -// The btree_set class is needed mainly for it's constructors. -template , - typename Alloc = std::allocator, - int TargetNodeSize = 256> -class btree_set : public btree_unique_container< - btree > > { - - typedef btree_set self_type; - typedef btree_set_params params_type; - typedef btree btree_type; - typedef btree_unique_container super_type; - - public: - typedef typename btree_type::key_compare key_compare; - typedef typename btree_type::allocator_type allocator_type; - - public: - // Default constructor. - btree_set(const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(comp, alloc) { - } - - // Copy constructor. - btree_set(const self_type &x) - : super_type(x) { - } - - // Range constructor. - template - btree_set(InputIterator b, InputIterator e, - const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(b, e, comp, alloc) { - } -}; - -template -inline void swap(btree_set &x, btree_set &y) { - x.swap(y); -} - -// The btree_multiset class is needed mainly for it's constructors. -template , - typename Alloc = std::allocator, - int TargetNodeSize = 256> -class btree_multiset : public btree_multi_container< - btree > > { - - typedef btree_multiset self_type; - typedef btree_set_params params_type; - typedef btree btree_type; - typedef btree_multi_container super_type; - - public: - typedef typename btree_type::key_compare key_compare; - typedef typename btree_type::allocator_type allocator_type; - - public: - // Default constructor. - btree_multiset(const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(comp, alloc) { - } - - // Copy constructor. - btree_multiset(const self_type &x) - : super_type(x) { - } - - // Range constructor. - template - btree_multiset(InputIterator b, InputIterator e, - const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(b, e, comp, alloc) { - } -}; - -template -inline void swap(btree_multiset &x, - btree_multiset &y) { - x.swap(y); -} - -} // namespace btree - -#endif // UTIL_BTREE_BTREE_SET_H__ diff --git a/tommyds/benchmark/lib/cpp-btree/btree_test.cc b/tommyds/benchmark/lib/cpp-btree/btree_test.cc deleted file mode 100644 index 56a787b..0000000 --- a/tommyds/benchmark/lib/cpp-btree/btree_test.cc +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "gtest/gtest.h" -#include "btree_map.h" -#include "btree_set.h" -#include "btree_test.h" - -namespace btree { -namespace { - -template -void SetTest() { - typedef TestAllocator TestAlloc; - ASSERT_EQ(sizeof(btree_set), sizeof(void*)); - BtreeTest, std::allocator, N>, std::set >(); - BtreeAllocatorTest, TestAlloc, N> >(); -} - -template -void MapTest() { - typedef TestAllocator TestAlloc; - ASSERT_EQ(sizeof(btree_map), sizeof(void*)); - BtreeTest, std::allocator, N>, std::map >(); - BtreeAllocatorTest, TestAlloc, N> >(); - BtreeMapTest, std::allocator, N> >(); -} - -TEST(Btree, set_int32_32) { SetTest(); } -TEST(Btree, set_int32_64) { SetTest(); } -TEST(Btree, set_int32_128) { SetTest(); } -TEST(Btree, set_int32_256) { SetTest(); } -TEST(Btree, set_int64_256) { SetTest(); } -TEST(Btree, set_string_256) { SetTest(); } -TEST(Btree, set_pair_256) { SetTest, 256>(); } -TEST(Btree, map_int32_256) { MapTest(); } -TEST(Btree, map_int64_256) { MapTest(); } -TEST(Btree, map_string_256) { MapTest(); } -TEST(Btree, map_pair_256) { MapTest, 256>(); } - -// Large-node tests -TEST(Btree, map_int32_1024) { MapTest(); } -TEST(Btree, map_int32_1032) { MapTest(); } -TEST(Btree, map_int32_1040) { MapTest(); } -TEST(Btree, map_int32_1048) { MapTest(); } -TEST(Btree, map_int32_1056) { MapTest(); } - -TEST(Btree, map_int32_2048) { MapTest(); } -TEST(Btree, map_int32_4096) { MapTest(); } -TEST(Btree, set_int32_1024) { SetTest(); } -TEST(Btree, set_int32_2048) { SetTest(); } -TEST(Btree, set_int32_4096) { SetTest(); } -TEST(Btree, map_string_1024) { MapTest(); } -TEST(Btree, map_string_2048) { MapTest(); } -TEST(Btree, map_string_4096) { MapTest(); } -TEST(Btree, set_string_1024) { SetTest(); } -TEST(Btree, set_string_2048) { SetTest(); } -TEST(Btree, set_string_4096) { SetTest(); } - -template -void MultiSetTest() { - typedef TestAllocator TestAlloc; - ASSERT_EQ(sizeof(btree_multiset), sizeof(void*)); - BtreeMultiTest, std::allocator, N>, - std::multiset >(); - BtreeAllocatorTest, TestAlloc, N> >(); -} - -template -void MultiMapTest() { - typedef TestAllocator TestAlloc; - ASSERT_EQ(sizeof(btree_multimap), sizeof(void*)); - BtreeMultiTest, std::allocator, N>, - std::multimap >(); - BtreeMultiMapTest, std::allocator, N> >(); - BtreeAllocatorTest, TestAlloc, N> >(); -} - -TEST(Btree, multiset_int32_256) { MultiSetTest(); } -TEST(Btree, multiset_int64_256) { MultiSetTest(); } -TEST(Btree, multiset_string_256) { MultiSetTest(); } -TEST(Btree, multiset_pair_256) { MultiSetTest, 256>(); } -TEST(Btree, multimap_int32_256) { MultiMapTest(); } -TEST(Btree, multimap_int64_256) { MultiMapTest(); } -TEST(Btree, multimap_string_256) { MultiMapTest(); } -TEST(Btree, multimap_pair_256) { MultiMapTest, 256>(); } - -// Large-node tests -TEST(Btree, multimap_int32_1024) { MultiMapTest(); } -TEST(Btree, multimap_int32_2048) { MultiMapTest(); } -TEST(Btree, multimap_int32_4096) { MultiMapTest(); } -TEST(Btree, multiset_int32_1024) { MultiSetTest(); } -TEST(Btree, multiset_int32_2048) { MultiSetTest(); } -TEST(Btree, multiset_int32_4096) { MultiSetTest(); } -TEST(Btree, multimap_string_1024) { MultiMapTest(); } -TEST(Btree, multimap_string_2048) { MultiMapTest(); } -TEST(Btree, multimap_string_4096) { MultiMapTest(); } -TEST(Btree, multiset_string_1024) { MultiSetTest(); } -TEST(Btree, multiset_string_2048) { MultiSetTest(); } -TEST(Btree, multiset_string_4096) { MultiSetTest(); } - -// Verify that swapping btrees swaps the key comparision functors. -struct SubstringLess { - SubstringLess() : n(2) {} - SubstringLess(size_t length) - : n(length) { - } - bool operator()(const std::string &a, const std::string &b) const { - std::string as(a.data(), std::min(n, a.size())); - std::string bs(b.data(), std::min(n, b.size())); - return as < bs; - } - size_t n; -}; - -TEST(Btree, SwapKeyCompare) { - typedef btree_set SubstringSet; - SubstringSet s1(SubstringLess(1), SubstringSet::allocator_type()); - SubstringSet s2(SubstringLess(2), SubstringSet::allocator_type()); - - ASSERT_TRUE(s1.insert("a").second); - ASSERT_FALSE(s1.insert("aa").second); - - ASSERT_TRUE(s2.insert("a").second); - ASSERT_TRUE(s2.insert("aa").second); - ASSERT_FALSE(s2.insert("aaa").second); - - swap(s1, s2); - - ASSERT_TRUE(s1.insert("b").second); - ASSERT_TRUE(s1.insert("bb").second); - ASSERT_FALSE(s1.insert("bbb").second); - - ASSERT_TRUE(s2.insert("b").second); - ASSERT_FALSE(s2.insert("bb").second); -} - -TEST(Btree, UpperBoundRegression) { - // Regress a bug where upper_bound would default-construct a new key_compare - // instead of copying the existing one. - typedef btree_set SubstringSet; - SubstringSet my_set(SubstringLess(3)); - my_set.insert("aab"); - my_set.insert("abb"); - // We call upper_bound("aaa"). If this correctly uses the length 3 - // comparator, aaa < aab < abb, so we should get aab as the result. - // If it instead uses the default-constructed length 2 comparator, - // aa == aa < ab, so we'll get abb as our result. - SubstringSet::iterator it = my_set.upper_bound("aaa"); - ASSERT_TRUE(it != my_set.end()); - EXPECT_EQ("aab", *it); -} - - -TEST(Btree, IteratorIncrementBy) { - // Test that increment_by returns the same position as increment. - const int kSetSize = 2341; - btree_set my_set; - for (int i = 0; i < kSetSize; ++i) { - my_set.insert(i); - } - - { - // Simple increment vs. increment by. - btree_set::iterator a = my_set.begin(); - btree_set::iterator b = my_set.begin(); - a.increment(); - b.increment_by(1); - EXPECT_EQ(*a, *b); - } - - btree_set::iterator a = my_set.begin(); - for (int i = 1; i < kSetSize; ++i) { - ++a; - // increment_by - btree_set::iterator b = my_set.begin(); - b.increment_by(i); - EXPECT_EQ(*a, *b) << ": i=" << i; - } -} - -TEST(Btree, Comparison) { - const int kSetSize = 1201; - btree_set my_set; - for (int i = 0; i < kSetSize; ++i) { - my_set.insert(i); - } - btree_set my_set_copy(my_set); - EXPECT_TRUE(my_set_copy == my_set); - EXPECT_TRUE(my_set == my_set_copy); - EXPECT_FALSE(my_set_copy != my_set); - EXPECT_FALSE(my_set != my_set_copy); - - my_set.insert(kSetSize); - EXPECT_FALSE(my_set_copy == my_set); - EXPECT_FALSE(my_set == my_set_copy); - EXPECT_TRUE(my_set_copy != my_set); - EXPECT_TRUE(my_set != my_set_copy); - - my_set.erase(kSetSize - 1); - EXPECT_FALSE(my_set_copy == my_set); - EXPECT_FALSE(my_set == my_set_copy); - EXPECT_TRUE(my_set_copy != my_set); - EXPECT_TRUE(my_set != my_set_copy); - - btree_map my_map; - for (int i = 0; i < kSetSize; ++i) { - my_map[std::string(i, 'a')] = i; - } - btree_map my_map_copy(my_map); - EXPECT_TRUE(my_map_copy == my_map); - EXPECT_TRUE(my_map == my_map_copy); - EXPECT_FALSE(my_map_copy != my_map); - EXPECT_FALSE(my_map != my_map_copy); - - ++my_map_copy[std::string(7, 'a')]; - EXPECT_FALSE(my_map_copy == my_map); - EXPECT_FALSE(my_map == my_map_copy); - EXPECT_TRUE(my_map_copy != my_map); - EXPECT_TRUE(my_map != my_map_copy); - - my_map_copy = my_map; - my_map["hello"] = kSetSize; - EXPECT_FALSE(my_map_copy == my_map); - EXPECT_FALSE(my_map == my_map_copy); - EXPECT_TRUE(my_map_copy != my_map); - EXPECT_TRUE(my_map != my_map_copy); - - my_map.erase(std::string(kSetSize - 1, 'a')); - EXPECT_FALSE(my_map_copy == my_map); - EXPECT_FALSE(my_map == my_map_copy); - EXPECT_TRUE(my_map_copy != my_map); - EXPECT_TRUE(my_map != my_map_copy); -} - -} // namespace -} // namespace btree diff --git a/tommyds/benchmark/lib/cpp-btree/btree_test.h b/tommyds/benchmark/lib/cpp-btree/btree_test.h deleted file mode 100644 index 0ac26ef..0000000 --- a/tommyds/benchmark/lib/cpp-btree/btree_test.h +++ /dev/null @@ -1,932 +0,0 @@ -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef UTIL_BTREE_BTREE_TEST_H__ -#define UTIL_BTREE_BTREE_TEST_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gtest/gtest.h" -#include "gflags/gflags.h" -#include "btree_container.h" - -DECLARE_int32(test_values); -DECLARE_int32(benchmark_values); - -namespace std { - -// Provide operator<< support for std::pair. -template -ostream& operator<<(ostream &os, const std::pair &p) { - os << "(" << p.first << "," << p.second << ")"; - return os; -} - -// Provide pair equality testing that works as long as x.first is comparable to -// y.first and x.second is comparable to y.second. Needed in the test for -// comparing std::pair to std::pair. -template -bool operator==(const std::pair &x, const std::pair &y) { - return x.first == y.first && x.second == y.second; -} - -// Partial specialization of remove_const that propagates the removal through -// std::pair. -template -struct remove_const > { - typedef pair::type, - typename remove_const::type> type; -}; - -} // namespace std - -namespace btree { - -// Select the first member of a pair. -template -struct select1st : public std::unary_function<_Pair, typename _Pair::first_type> { - const typename _Pair::first_type& operator()(const _Pair& __x) const { - return __x.first; - } -}; - -// Utility class to provide an accessor for a key given a value. The default -// behavior is to treat the value as a pair and return the first element. -template -struct KeyOfValue { - typedef select1st type; -}; - -template -struct identity { - inline const T& operator()(const T& t) const { return t; } -}; - -// Partial specialization of KeyOfValue class for when the key and value are -// the same type such as in set<> and btree_set<>. -template -struct KeyOfValue { - typedef identity type; -}; - -// Counts the number of occurances of "c" in a buffer. -inline ptrdiff_t strcount(const char* buf_begin, const char* buf_end, char c) { - if (buf_begin == NULL) - return 0; - if (buf_end <= buf_begin) - return 0; - ptrdiff_t num = 0; - for (const char* bp = buf_begin; bp != buf_end; bp++) { - if (*bp == c) - num++; - } - return num; -} - -// for when the string is not null-terminated. -inline ptrdiff_t strcount(const char* buf, size_t len, char c) { - return strcount(buf, buf + len, c); -} - -inline ptrdiff_t strcount(const std::string& buf, char c) { - return strcount(buf.c_str(), buf.size(), c); -} - -// The base class for a sorted associative container checker. TreeType is the -// container type to check and CheckerType is the container type to check -// against. TreeType is expected to be btree_{set,map,multiset,multimap} and -// CheckerType is expected to be {set,map,multiset,multimap}. -template -class base_checker { - typedef base_checker self_type; - - public: - typedef typename TreeType::key_type key_type; - typedef typename TreeType::value_type value_type; - typedef typename TreeType::key_compare key_compare; - typedef typename TreeType::pointer pointer; - typedef typename TreeType::const_pointer const_pointer; - typedef typename TreeType::reference reference; - typedef typename TreeType::const_reference const_reference; - typedef typename TreeType::size_type size_type; - typedef typename TreeType::difference_type difference_type; - typedef typename TreeType::iterator iterator; - typedef typename TreeType::const_iterator const_iterator; - typedef typename TreeType::reverse_iterator reverse_iterator; - typedef typename TreeType::const_reverse_iterator const_reverse_iterator; - - public: - // Default constructor. - base_checker() - : const_tree_(tree_) { - } - // Copy constructor. - base_checker(const self_type &x) - : tree_(x.tree_), - const_tree_(tree_), - checker_(x.checker_) { - } - - // Iterator routines. - iterator begin() { return tree_.begin(); } - const_iterator begin() const { return tree_.begin(); } - iterator end() { return tree_.end(); } - const_iterator end() const { return tree_.end(); } - reverse_iterator rbegin() { return tree_.rbegin(); } - const_reverse_iterator rbegin() const { return tree_.rbegin(); } - reverse_iterator rend() { return tree_.rend(); } - const_reverse_iterator rend() const { return tree_.rend(); } - - // Helper routines. - template - IterType iter_check( - IterType tree_iter, CheckerIterType checker_iter) const { - if (tree_iter == tree_.end()) { - EXPECT_EQ(checker_iter, checker_.end()); - } else { - EXPECT_EQ(*tree_iter, *checker_iter); - } - return tree_iter; - } - template - IterType riter_check( - IterType tree_iter, CheckerIterType checker_iter) const { - if (tree_iter == tree_.rend()) { - EXPECT_EQ(checker_iter, checker_.rend()); - } else { - EXPECT_EQ(*tree_iter, *checker_iter); - } - return tree_iter; - } - void value_check(const value_type &x) { - typename KeyOfValue::type key_of_value; - const key_type &key = key_of_value(x); - EXPECT_EQ(*find(key), x); - lower_bound(key); - upper_bound(key); - equal_range(key); - count(key); - } - void erase_check(const key_type &key) { - EXPECT_TRUE(tree_.find(key) == const_tree_.end()); - EXPECT_TRUE(const_tree_.find(key) == tree_.end()); - EXPECT_TRUE(tree_.equal_range(key).first == - const_tree_.equal_range(key).second); - } - - // Lookup routines. - iterator lower_bound(const key_type &key) { - return iter_check(tree_.lower_bound(key), checker_.lower_bound(key)); - } - const_iterator lower_bound(const key_type &key) const { - return iter_check(tree_.lower_bound(key), checker_.lower_bound(key)); - } - iterator upper_bound(const key_type &key) { - return iter_check(tree_.upper_bound(key), checker_.upper_bound(key)); - } - const_iterator upper_bound(const key_type &key) const { - return iter_check(tree_.upper_bound(key), checker_.upper_bound(key)); - } - std::pair equal_range(const key_type &key) { - std::pair checker_res = - checker_.equal_range(key); - std::pair tree_res = tree_.equal_range(key); - iter_check(tree_res.first, checker_res.first); - iter_check(tree_res.second, checker_res.second); - return tree_res; - } - std::pair equal_range(const key_type &key) const { - std::pair checker_res = - checker_.equal_range(key); - std::pair tree_res = tree_.equal_range(key); - iter_check(tree_res.first, checker_res.first); - iter_check(tree_res.second, checker_res.second); - return tree_res; - } - iterator find(const key_type &key) { - return iter_check(tree_.find(key), checker_.find(key)); - } - const_iterator find(const key_type &key) const { - return iter_check(tree_.find(key), checker_.find(key)); - } - size_type count(const key_type &key) const { - size_type res = checker_.count(key); - EXPECT_EQ(res, tree_.count(key)); - return res; - } - - // Assignment operator. - self_type& operator=(const self_type &x) { - tree_ = x.tree_; - checker_ = x.checker_; - return *this; - } - - // Deletion routines. - int erase(const key_type &key) { - int size = tree_.size(); - int res = checker_.erase(key); - EXPECT_EQ(res, tree_.count(key)); - EXPECT_EQ(res, tree_.erase(key)); - EXPECT_EQ(tree_.count(key), 0); - EXPECT_EQ(tree_.size(), size - res); - erase_check(key); - return res; - } - iterator erase(iterator iter) { - key_type key = iter.key(); - int size = tree_.size(); - int count = tree_.count(key); - typename CheckerType::iterator checker_iter = checker_.find(key); - for (iterator tmp(tree_.find(key)); tmp != iter; ++tmp) { - ++checker_iter; - } - typename CheckerType::iterator checker_next = checker_iter; - ++checker_next; - checker_.erase(checker_iter); - iter = tree_.erase(iter); - EXPECT_EQ(tree_.size(), checker_.size()); - EXPECT_EQ(tree_.size(), size - 1); - EXPECT_EQ(tree_.count(key), count - 1); - if (count == 1) { - erase_check(key); - } - return iter_check(iter, checker_next); - } - - void erase(iterator begin, iterator end) { - int size = tree_.size(); - int count = distance(begin, end); - typename CheckerType::iterator checker_begin = checker_.find(begin.key()); - for (iterator tmp(tree_.find(begin.key())); tmp != begin; ++tmp) { - ++checker_begin; - } - typename CheckerType::iterator checker_end = - end == tree_.end() ? checker_.end() : checker_.find(end.key()); - if (end != tree_.end()) { - for (iterator tmp(tree_.find(end.key())); tmp != end; ++tmp) { - ++checker_end; - } - } - checker_.erase(checker_begin, checker_end); - tree_.erase(begin, end); - EXPECT_EQ(tree_.size(), checker_.size()); - EXPECT_EQ(tree_.size(), size - count); - } - - // Utility routines. - void clear() { - tree_.clear(); - checker_.clear(); - } - void swap(self_type &x) { - tree_.swap(x.tree_); - checker_.swap(x.checker_); - } - - void verify() const { - tree_.verify(); - EXPECT_EQ(tree_.size(), checker_.size()); - - // Move through the forward iterators using increment. - typename CheckerType::const_iterator - checker_iter(checker_.begin()); - const_iterator tree_iter(tree_.begin()); - for (; tree_iter != tree_.end(); - ++tree_iter, ++checker_iter) { - EXPECT_EQ(*tree_iter, *checker_iter); - } - - // Move through the forward iterators using decrement. - for (int n = tree_.size() - 1; n >= 0; --n) { - iter_check(tree_iter, checker_iter); - --tree_iter; - --checker_iter; - } - EXPECT_TRUE(tree_iter == tree_.begin()); - EXPECT_TRUE(checker_iter == checker_.begin()); - - // Move through the reverse iterators using increment. - typename CheckerType::const_reverse_iterator - checker_riter(checker_.rbegin()); - const_reverse_iterator tree_riter(tree_.rbegin()); - for (; tree_riter != tree_.rend(); - ++tree_riter, ++checker_riter) { - EXPECT_EQ(*tree_riter, *checker_riter); - } - - // Move through the reverse iterators using decrement. - for (int n = tree_.size() - 1; n >= 0; --n) { - riter_check(tree_riter, checker_riter); - --tree_riter; - --checker_riter; - } - EXPECT_EQ(tree_riter, tree_.rbegin()); - EXPECT_EQ(checker_riter, checker_.rbegin()); - } - - // Access to the underlying btree. - const TreeType& tree() const { return tree_; } - - // Size routines. - size_type size() const { - EXPECT_EQ(tree_.size(), checker_.size()); - return tree_.size(); - } - size_type max_size() const { return tree_.max_size(); } - bool empty() const { - EXPECT_EQ(tree_.empty(), checker_.empty()); - return tree_.empty(); - } - size_type height() const { return tree_.height(); } - size_type internal_nodes() const { return tree_.internal_nodes(); } - size_type leaf_nodes() const { return tree_.leaf_nodes(); } - size_type nodes() const { return tree_.nodes(); } - size_type bytes_used() const { return tree_.bytes_used(); } - double fullness() const { return tree_.fullness(); } - double overhead() const { return tree_.overhead(); } - - protected: - TreeType tree_; - const TreeType &const_tree_; - CheckerType checker_; -}; - -// A checker for unique sorted associative containers. TreeType is expected to -// be btree_{set,map} and CheckerType is expected to be {set,map}. -template -class unique_checker : public base_checker { - typedef base_checker super_type; - typedef unique_checker self_type; - - public: - typedef typename super_type::iterator iterator; - typedef typename super_type::value_type value_type; - - public: - // Default constructor. - unique_checker() - : super_type() { - } - // Copy constructor. - unique_checker(const self_type &x) - : super_type(x) { - } - // Range constructor. - template - unique_checker(InputIterator b, InputIterator e) { - insert(b, e); - } - - // Insertion routines. - std::pair insert(const value_type &x) { - int size = this->tree_.size(); - std::pair checker_res = - this->checker_.insert(x); - std::pair tree_res = this->tree_.insert(x); - EXPECT_EQ(*tree_res.first, *checker_res.first); - EXPECT_EQ(tree_res.second, checker_res.second); - EXPECT_EQ(this->tree_.size(), this->checker_.size()); - EXPECT_EQ(this->tree_.size(), size + tree_res.second); - return tree_res; - } - iterator insert(iterator position, const value_type &x) { - int size = this->tree_.size(); - std::pair checker_res = - this->checker_.insert(x); - iterator tree_res = this->tree_.insert(position, x); - EXPECT_EQ(*tree_res, *checker_res.first); - EXPECT_EQ(this->tree_.size(), this->checker_.size()); - EXPECT_EQ(this->tree_.size(), size + checker_res.second); - return tree_res; - } - template - void insert(InputIterator b, InputIterator e) { - for (; b != e; ++b) { - insert(*b); - } - } -}; - -// A checker for multiple sorted associative containers. TreeType is expected -// to be btree_{multiset,multimap} and CheckerType is expected to be -// {multiset,multimap}. -template -class multi_checker : public base_checker { - typedef base_checker super_type; - typedef multi_checker self_type; - - public: - typedef typename super_type::iterator iterator; - typedef typename super_type::value_type value_type; - - public: - // Default constructor. - multi_checker() - : super_type() { - } - // Copy constructor. - multi_checker(const self_type &x) - : super_type(x) { - } - // Range constructor. - template - multi_checker(InputIterator b, InputIterator e) { - insert(b, e); - } - - // Insertion routines. - iterator insert(const value_type &x) { - int size = this->tree_.size(); - typename CheckerType::iterator checker_res = this->checker_.insert(x); - iterator tree_res = this->tree_.insert(x); - EXPECT_EQ(*tree_res, *checker_res); - EXPECT_EQ(this->tree_.size(), this->checker_.size()); - EXPECT_EQ(this->tree_.size(), size + 1); - return tree_res; - } - iterator insert(iterator position, const value_type &x) { - int size = this->tree_.size(); - typename CheckerType::iterator checker_res = this->checker_.insert(x); - iterator tree_res = this->tree_.insert(position, x); - EXPECT_EQ(*tree_res, *checker_res); - EXPECT_EQ(this->tree_.size(), this->checker_.size()); - EXPECT_EQ(this->tree_.size(), size + 1); - return tree_res; - } - template - void insert(InputIterator b, InputIterator e) { - for (; b != e; ++b) { - insert(*b); - } - } -}; - -char* GenerateDigits(char buf[16], int val, int maxval) { - EXPECT_LE(val, maxval); - int p = 15; - buf[p--] = 0; - while (maxval > 0) { - buf[p--] = '0' + (val % 10); - val /= 10; - maxval /= 10; - } - return buf + p + 1; -} - -template -struct Generator { - int maxval; - Generator(int m) - : maxval(m) { - } - K operator()(int i) const { - EXPECT_LE(i, maxval); - return i; - } -}; - -template <> -struct Generator { - int maxval; - Generator(int m) - : maxval(m) { - } - std::string operator()(int i) const { - char buf[16]; - return GenerateDigits(buf, i, maxval); - } -}; - -template -struct Generator > { - Generator::type> tgen; - Generator::type> ugen; - - Generator(int m) - : tgen(m), - ugen(m) { - } - std::pair operator()(int i) const { - return std::make_pair(tgen(i), ugen(i)); - } -}; - -// Generate values for our tests and benchmarks. Value range is [0, maxval]. -const std::vector& GenerateNumbers(int n, int maxval) { - static std::vector values; - static std::set unique_values; - - if (values.size() < n) { - - for (int i = values.size(); i < n; i++) { - int value; - do { - value = rand() % (maxval + 1); - } while (unique_values.find(value) != unique_values.end()); - - values.push_back(value); - unique_values.insert(value); - } - } - - return values; -} - -// Generates values in the range -// [0, 4 * min(FLAGS_benchmark_values, FLAGS_test_values)] -template -std::vector GenerateValues(int n) { - int two_times_max = 2 * std::max(FLAGS_benchmark_values, FLAGS_test_values); - int four_times_max = 2 * two_times_max; - EXPECT_LE(n, two_times_max); - const std::vector &nums = GenerateNumbers(n, four_times_max); - Generator gen(four_times_max); - std::vector vec; - - for (int i = 0; i < n; i++) { - vec.push_back(gen(nums[i])); - } - - return vec; -} - -template -void DoTest(const char *name, T *b, const std::vector &values) { - typename KeyOfValue::type key_of_value; - - T &mutable_b = *b; - const T &const_b = *b; - - // Test insert. - for (int i = 0; i < values.size(); ++i) { - mutable_b.insert(values[i]); - mutable_b.value_check(values[i]); - } - - const_b.verify(); - printf(" %s fullness=%0.2f overhead=%0.2f bytes-per-value=%0.2f\n", - name, const_b.fullness(), const_b.overhead(), - double(const_b.bytes_used()) / const_b.size()); - - // Test copy constructor. - T b_copy(const_b); - EXPECT_EQ(b_copy.size(), const_b.size()); - EXPECT_LE(b_copy.height(), const_b.height()); - EXPECT_LE(b_copy.internal_nodes(), const_b.internal_nodes()); - EXPECT_LE(b_copy.leaf_nodes(), const_b.leaf_nodes()); - for (int i = 0; i < values.size(); ++i) { - EXPECT_EQ(*b_copy.find(key_of_value(values[i])), values[i]); - } - - // Test range constructor. - T b_range(const_b.begin(), const_b.end()); - EXPECT_EQ(b_range.size(), const_b.size()); - EXPECT_LE(b_range.height(), const_b.height()); - EXPECT_LE(b_range.internal_nodes(), const_b.internal_nodes()); - EXPECT_LE(b_range.leaf_nodes(), const_b.leaf_nodes()); - for (int i = 0; i < values.size(); ++i) { - EXPECT_EQ(*b_range.find(key_of_value(values[i])), values[i]); - } - - // Test range insertion for values that already exist. - b_range.insert(b_copy.begin(), b_copy.end()); - b_range.verify(); - - // Test range insertion for new values. - b_range.clear(); - b_range.insert(b_copy.begin(), b_copy.end()); - EXPECT_EQ(b_range.size(), b_copy.size()); - EXPECT_EQ(b_range.height(), b_copy.height()); - EXPECT_EQ(b_range.internal_nodes(), b_copy.internal_nodes()); - EXPECT_EQ(b_range.leaf_nodes(), b_copy.leaf_nodes()); - for (int i = 0; i < values.size(); ++i) { - EXPECT_EQ(*b_range.find(key_of_value(values[i])), values[i]); - } - - // Test assignment to self. Nothing should change. - b_range.operator=(b_range); - EXPECT_EQ(b_range.size(), b_copy.size()); - EXPECT_EQ(b_range.height(), b_copy.height()); - EXPECT_EQ(b_range.internal_nodes(), b_copy.internal_nodes()); - EXPECT_EQ(b_range.leaf_nodes(), b_copy.leaf_nodes()); - - // Test assignment of new values. - b_range.clear(); - b_range = b_copy; - EXPECT_EQ(b_range.size(), b_copy.size()); - EXPECT_EQ(b_range.height(), b_copy.height()); - EXPECT_EQ(b_range.internal_nodes(), b_copy.internal_nodes()); - EXPECT_EQ(b_range.leaf_nodes(), b_copy.leaf_nodes()); - - // Test swap. - b_range.clear(); - b_range.swap(b_copy); - EXPECT_EQ(b_copy.size(), 0); - EXPECT_EQ(b_range.size(), const_b.size()); - for (int i = 0; i < values.size(); ++i) { - EXPECT_EQ(*b_range.find(key_of_value(values[i])), values[i]); - } - b_range.swap(b_copy); - - // Test erase via values. - for (int i = 0; i < values.size(); ++i) { - mutable_b.erase(key_of_value(values[i])); - // Erasing a non-existent key should have no effect. - EXPECT_EQ(mutable_b.erase(key_of_value(values[i])), 0); - } - - const_b.verify(); - EXPECT_EQ(const_b.internal_nodes(), 0); - EXPECT_EQ(const_b.leaf_nodes(), 0); - EXPECT_EQ(const_b.size(), 0); - - // Test erase via iterators. - mutable_b = b_copy; - for (int i = 0; i < values.size(); ++i) { - mutable_b.erase(mutable_b.find(key_of_value(values[i]))); - } - - const_b.verify(); - EXPECT_EQ(const_b.internal_nodes(), 0); - EXPECT_EQ(const_b.leaf_nodes(), 0); - EXPECT_EQ(const_b.size(), 0); - - // Test insert with hint. - for (int i = 0; i < values.size(); i++) { - mutable_b.insert(mutable_b.upper_bound(key_of_value(values[i])), values[i]); - } - - const_b.verify(); - - // Test dumping of the btree to an ostream. There should be 1 line for each - // value. - std::stringstream strm; - strm << mutable_b.tree(); - EXPECT_EQ(mutable_b.size(), strcount(strm.str(), '\n')); - - // Test range erase. - mutable_b.erase(mutable_b.begin(), mutable_b.end()); - EXPECT_EQ(mutable_b.size(), 0); - const_b.verify(); - - // First half. - mutable_b = b_copy; - typename T::iterator mutable_iter_end = mutable_b.begin(); - for (int i = 0; i < values.size() / 2; ++i) ++mutable_iter_end; - mutable_b.erase(mutable_b.begin(), mutable_iter_end); - EXPECT_EQ(mutable_b.size(), values.size() - values.size() / 2); - const_b.verify(); - - // Second half. - mutable_b = b_copy; - typename T::iterator mutable_iter_begin = mutable_b.begin(); - for (int i = 0; i < values.size() / 2; ++i) ++mutable_iter_begin; - mutable_b.erase(mutable_iter_begin, mutable_b.end()); - EXPECT_EQ(mutable_b.size(), values.size() / 2); - const_b.verify(); - - // Second quarter. - mutable_b = b_copy; - mutable_iter_begin = mutable_b.begin(); - for (int i = 0; i < values.size() / 4; ++i) ++mutable_iter_begin; - mutable_iter_end = mutable_iter_begin; - for (int i = 0; i < values.size() / 4; ++i) ++mutable_iter_end; - mutable_b.erase(mutable_iter_begin, mutable_iter_end); - EXPECT_EQ(mutable_b.size(), values.size() - values.size() / 4); - const_b.verify(); - - mutable_b.clear(); -} - -template -void ConstTest() { - typedef typename T::value_type value_type; - typename KeyOfValue::type key_of_value; - - T mutable_b; - const T &const_b = mutable_b; - - // Insert a single value into the container and test looking it up. - value_type value = Generator(2)(2); - mutable_b.insert(value); - EXPECT_TRUE(mutable_b.find(key_of_value(value)) != const_b.end()); - EXPECT_TRUE(const_b.find(key_of_value(value)) != mutable_b.end()); - EXPECT_EQ(*const_b.lower_bound(key_of_value(value)), value); - EXPECT_TRUE(const_b.upper_bound(key_of_value(value)) == const_b.end()); - EXPECT_EQ(*const_b.equal_range(key_of_value(value)).first, value); - - // We can only create a non-const iterator from a non-const container. - typename T::iterator mutable_iter(mutable_b.begin()); - EXPECT_TRUE(mutable_iter == const_b.begin()); - EXPECT_TRUE(mutable_iter != const_b.end()); - EXPECT_TRUE(const_b.begin() == mutable_iter); - EXPECT_TRUE(const_b.end() != mutable_iter); - typename T::reverse_iterator mutable_riter(mutable_b.rbegin()); - EXPECT_TRUE(mutable_riter == const_b.rbegin()); - EXPECT_TRUE(mutable_riter != const_b.rend()); - EXPECT_TRUE(const_b.rbegin() == mutable_riter); - EXPECT_TRUE(const_b.rend() != mutable_riter); - - // We can create a const iterator from a non-const iterator. - typename T::const_iterator const_iter(mutable_iter); - EXPECT_TRUE(const_iter == mutable_b.begin()); - EXPECT_TRUE(const_iter != mutable_b.end()); - EXPECT_TRUE(mutable_b.begin() == const_iter); - EXPECT_TRUE(mutable_b.end() != const_iter); - typename T::const_reverse_iterator const_riter(mutable_riter); - EXPECT_EQ(const_riter, mutable_b.rbegin()); - EXPECT_TRUE(const_riter != mutable_b.rend()); - EXPECT_EQ(mutable_b.rbegin(), const_riter); - EXPECT_TRUE(mutable_b.rend() != const_riter); - - // Make sure various methods can be invoked on a const container. - const_b.verify(); - EXPECT_FALSE(const_b.empty()); - EXPECT_EQ(const_b.size(), 1); - EXPECT_GT(const_b.max_size(), 0); - EXPECT_EQ(const_b.height(), 1); - EXPECT_EQ(const_b.count(key_of_value(value)), 1); - EXPECT_EQ(const_b.internal_nodes(), 0); - EXPECT_EQ(const_b.leaf_nodes(), 1); - EXPECT_EQ(const_b.nodes(), 1); - EXPECT_GT(const_b.bytes_used(), 0); - EXPECT_GT(const_b.fullness(), 0); - EXPECT_GT(const_b.overhead(), 0); -} - -template -void BtreeTest() { - ConstTest(); - - typedef typename std::remove_const::type V; - std::vector random_values = GenerateValues(FLAGS_test_values); - - unique_checker container; - - // Test key insertion/deletion in sorted order. - std::vector sorted_values(random_values); - sort(sorted_values.begin(), sorted_values.end()); - DoTest("sorted: ", &container, sorted_values); - - // Test key insertion/deletion in reverse sorted order. - reverse(sorted_values.begin(), sorted_values.end()); - DoTest("rsorted: ", &container, sorted_values); - - // Test key insertion/deletion in random order. - DoTest("random: ", &container, random_values); -} - -template -void BtreeMultiTest() { - ConstTest(); - - typedef typename std::remove_const::type V; - const std::vector& random_values = GenerateValues(FLAGS_test_values); - - multi_checker container; - - // Test keys in sorted order. - std::vector sorted_values(random_values); - sort(sorted_values.begin(), sorted_values.end()); - DoTest("sorted: ", &container, sorted_values); - - // Test keys in reverse sorted order. - reverse(sorted_values.begin(), sorted_values.end()); - DoTest("rsorted: ", &container, sorted_values); - - // Test keys in random order. - DoTest("random: ", &container, random_values); - - // Test keys in random order w/ duplicates. - std::vector duplicate_values(random_values); - duplicate_values.insert( - duplicate_values.end(), random_values.begin(), random_values.end()); - DoTest("duplicates:", &container, duplicate_values); - - // Test all identical keys. - std::vector identical_values(100); - fill(identical_values.begin(), identical_values.end(), Generator(2)(2)); - DoTest("identical: ", &container, identical_values); -} - -template > -class TestAllocator : public Alloc { - public: - typedef typename Alloc::pointer pointer; - typedef typename Alloc::size_type size_type; - - TestAllocator() : bytes_used_(NULL) { } - TestAllocator(int64_t *bytes_used) : bytes_used_(bytes_used) { } - - // Constructor used for rebinding - template - TestAllocator(const TestAllocator& x) - : Alloc(x), - bytes_used_(x.bytes_used()) { - } - - pointer allocate(size_type n, std::allocator::const_pointer hint = 0) { - EXPECT_TRUE(bytes_used_ != NULL); - *bytes_used_ += n * sizeof(T); - return Alloc::allocate(n, hint); - } - - void deallocate(pointer p, size_type n) { - Alloc::deallocate(p, n); - EXPECT_TRUE(bytes_used_ != NULL); - *bytes_used_ -= n * sizeof(T); - } - - // Rebind allows an allocator to be used for a different type - template struct rebind { - typedef TestAllocator::other> other; - }; - - int64_t* bytes_used() const { return bytes_used_; } - - private: - int64_t *bytes_used_; -}; - -template -void BtreeAllocatorTest() { - typedef typename T::value_type value_type; - - int64_t alloc1 = 0; - int64_t alloc2 = 0; - T b1(typename T::key_compare(), &alloc1); - T b2(typename T::key_compare(), &alloc2); - - // This should swap the allocators! - swap(b1, b2); - - for (int i = 0; i < 1000; i++) { - b1.insert(Generator(1000)(i)); - } - - // We should have allocated out of alloc2! - EXPECT_LE(b1.bytes_used(), alloc2 + sizeof(b1)); - EXPECT_GT(alloc2, alloc1); -} - -template -void BtreeMapTest() { - typedef typename T::value_type value_type; - typedef typename T::mapped_type mapped_type; - - mapped_type m = Generator(0)(0); - (void) m; - - T b; - - // Verify we can insert using operator[]. - for (int i = 0; i < 1000; i++) { - value_type v = Generator(1000)(i); - b[v.first] = v.second; - } - EXPECT_EQ(b.size(), 1000); - - // Test whether we can use the "->" operator on iterators and - // reverse_iterators. This stresses the btree_map_params::pair_pointer - // mechanism. - EXPECT_EQ(b.begin()->first, Generator(1000)(0).first); - EXPECT_EQ(b.begin()->second, Generator(1000)(0).second); - EXPECT_EQ(b.rbegin()->first, Generator(1000)(999).first); - EXPECT_EQ(b.rbegin()->second, Generator(1000)(999).second); -} - -template -void BtreeMultiMapTest() { - typedef typename T::mapped_type mapped_type; - mapped_type m = Generator(0)(0); - (void) m; -} - -} // namespace btree - -#endif // UTIL_BTREE_BTREE_TEST_H__ diff --git a/tommyds/benchmark/lib/cpp-btree/btree_test_flags.cc b/tommyds/benchmark/lib/cpp-btree/btree_test_flags.cc deleted file mode 100644 index bf608a9..0000000 --- a/tommyds/benchmark/lib/cpp-btree/btree_test_flags.cc +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "gflags/gflags.h" - -DEFINE_int32(test_values, 10000, - "The number of values to use for tests."); -DEFINE_int32(benchmark_values, 1000000, - "The number of values to use for benchmarks."); diff --git a/tommyds/benchmark/lib/cpp-btree/safe_btree.h b/tommyds/benchmark/lib/cpp-btree/safe_btree.h deleted file mode 100644 index 2d85c70..0000000 --- a/tommyds/benchmark/lib/cpp-btree/safe_btree.h +++ /dev/null @@ -1,395 +0,0 @@ -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// A safe_btree<> wraps around a btree<> and removes the caveat that insertion -// and deletion invalidate iterators. A safe_btree<> maintains a generation -// number that is incremented on every mutation. A safe_btree<>::iterator keeps -// a pointer to the safe_btree<> it came from, the generation of the tree when -// it was last validated and the key the underlying btree<>::iterator points -// to. If an iterator is accessed and its generation differs from the tree -// generation it is revalidated. -// -// References and pointers returned by safe_btree iterators are not safe. -// -// See the incorrect usage examples mentioned in safe_btree_set.h and -// safe_btree_map.h. - -#ifndef UTIL_BTREE_SAFE_BTREE_H__ -#define UTIL_BTREE_SAFE_BTREE_H__ - -#include -#include -#include - -#include "btree.h" - -namespace btree { - -template -class safe_btree_iterator { - public: - typedef typename Iterator::key_type key_type; - typedef typename Iterator::value_type value_type; - typedef typename Iterator::size_type size_type; - typedef typename Iterator::difference_type difference_type; - typedef typename Iterator::pointer pointer; - typedef typename Iterator::reference reference; - typedef typename Iterator::const_pointer const_pointer; - typedef typename Iterator::const_reference const_reference; - typedef typename Iterator::iterator_category iterator_category; - typedef typename Tree::iterator iterator; - typedef typename Tree::const_iterator const_iterator; - typedef safe_btree_iterator self_type; - - void update() const { - if (iter_ != tree_->internal_btree()->end()) { - // A positive generation indicates a valid key. - generation_ = tree_->generation(); - key_ = iter_.key(); - } else { - // Use a negative generation to indicate iter_ points to end(). - generation_ = -tree_->generation(); - } - } - - public: - safe_btree_iterator() - : generation_(0), - key_(), - iter_(), - tree_(NULL) { - } - safe_btree_iterator(const iterator &x) - : generation_(x.generation()), - key_(x.key()), - iter_(x.iter()), - tree_(x.tree()) { - } - safe_btree_iterator(Tree *tree, const Iterator &iter) - : generation_(), - key_(), - iter_(iter), - tree_(tree) { - update(); - } - - Tree* tree() const { return tree_; } - int64_t generation() const { return generation_; } - - Iterator* mutable_iter() const { - if (generation_ != tree_->generation()) { - if (generation_ > 0) { - // This does the wrong thing for a multi{set,map}. If my iter was - // pointing to the 2nd of 2 values with the same key, then this will - // reset it to point to the first. This is why we don't provide a - // safe_btree_multi{set,map}. - iter_ = tree_->internal_btree()->lower_bound(key_); - update(); - } else if (-generation_ != tree_->generation()) { - iter_ = tree_->internal_btree()->end(); - generation_ = -tree_->generation(); - } - } - return &iter_; - } - const Iterator& iter() const { - return *mutable_iter(); - } - - // Equality/inequality operators. - bool operator==(const const_iterator &x) const { - return iter() == x.iter(); - } - bool operator!=(const const_iterator &x) const { - return iter() != x.iter(); - } - - // Accessors for the key/value the iterator is pointing at. - const key_type& key() const { - return key_; - } - // This reference value is potentially invalidated by any non-const - // method on the tree; it is NOT safe. - reference operator*() const { - assert(generation_ > 0); - return iter().operator*(); - } - // This pointer value is potentially invalidated by any non-const - // method on the tree; it is NOT safe. - pointer operator->() const { - assert(generation_ > 0); - return iter().operator->(); - } - - // Increment/decrement operators. - self_type& operator++() { - ++(*mutable_iter()); - update(); - return *this; - } - self_type& operator--() { - --(*mutable_iter()); - update(); - return *this; - } - self_type operator++(int) { - self_type tmp = *this; - ++*this; - return tmp; - } - self_type operator--(int) { - self_type tmp = *this; - --*this; - return tmp; - } - - private: - // The generation of the tree when "iter" was updated. - mutable int64_t generation_; - // The key the iterator points to. - mutable key_type key_; - // The underlying iterator. - mutable Iterator iter_; - // The tree the iterator is associated with. - Tree *tree_; -}; - -template -class safe_btree { - typedef safe_btree self_type; - - typedef btree btree_type; - typedef typename btree_type::iterator tree_iterator; - typedef typename btree_type::const_iterator tree_const_iterator; - - public: - typedef typename btree_type::params_type params_type; - typedef typename btree_type::key_type key_type; - typedef typename btree_type::data_type data_type; - typedef typename btree_type::mapped_type mapped_type; - typedef typename btree_type::value_type value_type; - typedef typename btree_type::key_compare key_compare; - typedef typename btree_type::allocator_type allocator_type; - typedef typename btree_type::pointer pointer; - typedef typename btree_type::const_pointer const_pointer; - typedef typename btree_type::reference reference; - typedef typename btree_type::const_reference const_reference; - typedef typename btree_type::size_type size_type; - typedef typename btree_type::difference_type difference_type; - typedef safe_btree_iterator iterator; - typedef safe_btree_iterator< - const self_type, tree_const_iterator> const_iterator; - typedef std::reverse_iterator const_reverse_iterator; - typedef std::reverse_iterator reverse_iterator; - - public: - // Default constructor. - safe_btree(const key_compare &comp, const allocator_type &alloc) - : tree_(comp, alloc), - generation_(1) { - } - - // Copy constructor. - safe_btree(const self_type &x) - : tree_(x.tree_), - generation_(1) { - } - - iterator begin() { - return iterator(this, tree_.begin()); - } - const_iterator begin() const { - return const_iterator(this, tree_.begin()); - } - iterator end() { - return iterator(this, tree_.end()); - } - const_iterator end() const { - return const_iterator(this, tree_.end()); - } - reverse_iterator rbegin() { - return reverse_iterator(end()); - } - const_reverse_iterator rbegin() const { - return const_reverse_iterator(end()); - } - reverse_iterator rend() { - return reverse_iterator(begin()); - } - const_reverse_iterator rend() const { - return const_reverse_iterator(begin()); - } - - // Lookup routines. - iterator lower_bound(const key_type &key) { - return iterator(this, tree_.lower_bound(key)); - } - const_iterator lower_bound(const key_type &key) const { - return const_iterator(this, tree_.lower_bound(key)); - } - iterator upper_bound(const key_type &key) { - return iterator(this, tree_.upper_bound(key)); - } - const_iterator upper_bound(const key_type &key) const { - return const_iterator(this, tree_.upper_bound(key)); - } - std::pair equal_range(const key_type &key) { - std::pair p = tree_.equal_range(key); - return std::make_pair(iterator(this, p.first), - iterator(this, p.second)); - } - std::pair equal_range(const key_type &key) const { - std::pair p = tree_.equal_range(key); - return std::make_pair(const_iterator(this, p.first), - const_iterator(this, p.second)); - } - iterator find_unique(const key_type &key) { - return iterator(this, tree_.find_unique(key)); - } - const_iterator find_unique(const key_type &key) const { - return const_iterator(this, tree_.find_unique(key)); - } - iterator find_multi(const key_type &key) { - return iterator(this, tree_.find_multi(key)); - } - const_iterator find_multi(const key_type &key) const { - return const_iterator(this, tree_.find_multi(key)); - } - size_type count_unique(const key_type &key) const { - return tree_.count_unique(key); - } - size_type count_multi(const key_type &key) const { - return tree_.count_multi(key); - } - - // Insertion routines. - template - std::pair insert_unique(const key_type &key, ValuePointer value) { - std::pair p = tree_.insert_unique(key, value); - generation_ += p.second; - return std::make_pair(iterator(this, p.first), p.second); - } - std::pair insert_unique(const value_type &v) { - std::pair p = tree_.insert_unique(v); - generation_ += p.second; - return std::make_pair(iterator(this, p.first), p.second); - } - iterator insert_unique(iterator position, const value_type &v) { - tree_iterator tree_pos = position.iter(); - ++generation_; - return iterator(this, tree_.insert_unique(tree_pos, v)); - } - template - void insert_unique(InputIterator b, InputIterator e) { - for (; b != e; ++b) { - insert_unique(*b); - } - } - iterator insert_multi(const value_type &v) { - ++generation_; - return iterator(this, tree_.insert_multi(v)); - } - iterator insert_multi(iterator position, const value_type &v) { - tree_iterator tree_pos = position.iter(); - ++generation_; - return iterator(this, tree_.insert_multi(tree_pos, v)); - } - template - void insert_multi(InputIterator b, InputIterator e) { - for (; b != e; ++b) { - insert_multi(*b); - } - } - self_type& operator=(const self_type &x) { - if (&x == this) { - // Don't copy onto ourselves. - return *this; - } - ++generation_; - tree_ = x.tree_; - return *this; - } - - // Deletion routines. - void erase(const iterator &begin, const iterator &end) { - tree_.erase(begin.iter(), end.iter()); - ++generation_; - } - // Erase the specified iterator from the btree. The iterator must be valid - // (i.e. not equal to end()). Return an iterator pointing to the node after - // the one that was erased (or end() if none exists). - iterator erase(iterator iter) { - tree_iterator res = tree_.erase(iter.iter()); - ++generation_; - return iterator(this, res); - } - int erase_unique(const key_type &key) { - int res = tree_.erase_unique(key); - generation_ += res; - return res; - } - int erase_multi(const key_type &key) { - int res = tree_.erase_multi(key); - generation_ += res; - return res; - } - - // Access to the underlying btree. - btree_type* internal_btree() { return &tree_; } - const btree_type* internal_btree() const { return &tree_; } - - // Utility routines. - void clear() { - ++generation_; - tree_.clear(); - } - void swap(self_type &x) { - ++generation_; - ++x.generation_; - tree_.swap(x.tree_); - } - void dump(std::ostream &os) const { - tree_.dump(os); - } - void verify() const { - tree_.verify(); - } - int64_t generation() const { - return generation_; - } - key_compare key_comp() const { return tree_.key_comp(); } - - // Size routines. - size_type size() const { return tree_.size(); } - size_type max_size() const { return tree_.max_size(); } - bool empty() const { return tree_.empty(); } - size_type height() const { return tree_.height(); } - size_type internal_nodes() const { return tree_.internal_nodes(); } - size_type leaf_nodes() const { return tree_.leaf_nodes(); } - size_type nodes() const { return tree_.nodes(); } - size_type bytes_used() const { return tree_.bytes_used(); } - static double average_bytes_per_value() { - return btree_type::average_bytes_per_value(); - } - double fullness() const { return tree_.fullness(); } - double overhead() const { return tree_.overhead(); } - - private: - btree_type tree_; - int64_t generation_; -}; - -} // namespace btree - -#endif // UTIL_BTREE_SAFE_BTREE_H__ diff --git a/tommyds/benchmark/lib/cpp-btree/safe_btree_map.h b/tommyds/benchmark/lib/cpp-btree/safe_btree_map.h deleted file mode 100644 index a0668f1..0000000 --- a/tommyds/benchmark/lib/cpp-btree/safe_btree_map.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// The safe_btree_map<> is like btree_map<> except that it removes the caveat -// about insertion and deletion invalidating existing iterators at a small cost -// in making iterators larger and slower. -// -// Revalidation occurs whenever an iterator is accessed. References -// and pointers returned by safe_btree_map<> iterators are not stable, -// they are potentially invalidated by any non-const method on the map. -// -// BEGIN INCORRECT EXAMPLE -// for (auto i = safe_map->begin(); i != safe_map->end(); ++i) { -// const T *value = &i->second; // DO NOT DO THIS -// [code that modifies safe_map and uses value]; -// } -// END INCORRECT EXAMPLE -#ifndef UTIL_BTREE_SAFE_BTREE_MAP_H__ -#define UTIL_BTREE_SAFE_BTREE_MAP_H__ - -#include -#include -#include - -#include "btree_container.h" -#include "btree_map.h" -#include "safe_btree.h" - -namespace btree { - -// The safe_btree_map class is needed mainly for its constructors. -template , - typename Alloc = std::allocator >, - int TargetNodeSize = 256> -class safe_btree_map : public btree_map_container< - safe_btree > > { - - typedef safe_btree_map self_type; - typedef btree_map_params< - Key, Value, Compare, Alloc, TargetNodeSize> params_type; - typedef safe_btree btree_type; - typedef btree_map_container super_type; - - public: - typedef typename btree_type::key_compare key_compare; - typedef typename btree_type::allocator_type allocator_type; - - public: - // Default constructor. - safe_btree_map(const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(comp, alloc) { - } - - // Copy constructor. - safe_btree_map(const self_type &x) - : super_type(x) { - } - - // Range constructor. - template - safe_btree_map(InputIterator b, InputIterator e, - const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(b, e, comp, alloc) { - } -}; - -template -inline void swap(safe_btree_map &x, - safe_btree_map &y) { - x.swap(y); -} - -} // namespace btree - -#endif // UTIL_BTREE_SAFE_BTREE_MAP_H__ diff --git a/tommyds/benchmark/lib/cpp-btree/safe_btree_set.h b/tommyds/benchmark/lib/cpp-btree/safe_btree_set.h deleted file mode 100644 index a6cd541..0000000 --- a/tommyds/benchmark/lib/cpp-btree/safe_btree_set.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// The safe_btree_set<> is like btree_set<> except that it removes the caveat -// about insertion and deletion invalidating existing iterators at a small cost -// in making iterators larger and slower. -// -// Revalidation occurs whenever an iterator is accessed. References -// and pointers returned by safe_btree_map<> iterators are not stable, -// they are potentially invalidated by any non-const method on the set. -// -// BEGIN INCORRECT EXAMPLE -// for (auto i = safe_set->begin(); i != safe_set->end(); ++i) { -// const T &value = *i; // DO NOT DO THIS -// [code that modifies safe_set and uses value]; -// } -// END INCORRECT EXAMPLE - -#ifndef UTIL_BTREE_SAFE_BTREE_SET_H__ -#define UTIL_BTREE_SAFE_BTREE_SET_H__ - -#include -#include - -#include "btree_container.h" -#include "btree_set.h" -#include "safe_btree.h" - -namespace btree { - -// The safe_btree_set class is needed mainly for its constructors. -template , - typename Alloc = std::allocator, - int TargetNodeSize = 256> -class safe_btree_set : public btree_unique_container< - safe_btree > > { - - typedef safe_btree_set self_type; - typedef btree_set_params params_type; - typedef safe_btree btree_type; - typedef btree_unique_container super_type; - - public: - typedef typename btree_type::key_compare key_compare; - typedef typename btree_type::allocator_type allocator_type; - - public: - // Default constructor. - safe_btree_set(const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(comp, alloc) { - } - - // Copy constructor. - safe_btree_set(const self_type &x) - : super_type(x) { - } - - // Range constructor. - template - safe_btree_set(InputIterator b, InputIterator e, - const key_compare &comp = key_compare(), - const allocator_type &alloc = allocator_type()) - : super_type(b, e, comp, alloc) { - } -}; - -template -inline void swap(safe_btree_set &x, - safe_btree_set &y) { - x.swap(y); -} - -} // namespace btree - -#endif // UTIL_BTREE_SAFE_BTREE_SET_H__ diff --git a/tommyds/benchmark/lib/cpp-btree/safe_btree_test.cc b/tommyds/benchmark/lib/cpp-btree/safe_btree_test.cc deleted file mode 100644 index 0d77ae0..0000000 --- a/tommyds/benchmark/lib/cpp-btree/safe_btree_test.cc +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// TODO(pmattis): Add some tests that iterators are not invalidated by -// insertion and deletion. - -#include -#include -#include -#include -#include - -#include "gtest/gtest.h" -#include "btree_test.h" -#include "safe_btree_map.h" -#include "safe_btree_set.h" - -class UnsafeArena; - -namespace btree { -namespace { - -template -void SetTest() { - typedef TestAllocator TestAlloc; - BtreeTest, std::allocator, N>, std::set >(); - BtreeAllocatorTest, TestAlloc, N> >(); -} - -template -void MapTest() { - typedef TestAllocator TestAlloc; - BtreeTest, std::allocator, N>, std::map >(); - BtreeAllocatorTest, TestAlloc, N> >(); - BtreeMapTest, std::allocator, N> >(); -} - -TEST(SafeBtree, set_int32_32) { SetTest(); } -TEST(SafeBtree, set_int32_64) { SetTest(); } -TEST(SafeBtree, set_int32_128) { SetTest(); } -TEST(SafeBtree, set_int32_256) { SetTest(); } -TEST(SafeBtree, set_int64_256) { SetTest(); } -TEST(SafeBtree, set_string_256) { SetTest(); } -TEST(SafeBtree, set_pair_256) { SetTest, 256>(); } -TEST(SafeBtree, map_int32_256) { MapTest(); } -TEST(SafeBtree, map_int64_256) { MapTest(); } -TEST(SafeBtree, map_string_256) { MapTest(); } -TEST(SafeBtree, map_pair_256) { MapTest, 256>(); } - -TEST(SafeBtree, Comparison) { - const int kSetSize = 1201; - safe_btree_set my_set; - for (int i = 0; i < kSetSize; ++i) { - my_set.insert(i); - } - safe_btree_set my_set_copy(my_set); - EXPECT_TRUE(my_set_copy == my_set); - EXPECT_TRUE(my_set == my_set_copy); - EXPECT_FALSE(my_set_copy != my_set); - EXPECT_FALSE(my_set != my_set_copy); - - my_set.insert(kSetSize); - EXPECT_FALSE(my_set_copy == my_set); - EXPECT_FALSE(my_set == my_set_copy); - EXPECT_TRUE(my_set_copy != my_set); - EXPECT_TRUE(my_set != my_set_copy); - - my_set.erase(kSetSize - 1); - EXPECT_FALSE(my_set_copy == my_set); - EXPECT_FALSE(my_set == my_set_copy); - EXPECT_TRUE(my_set_copy != my_set); - EXPECT_TRUE(my_set != my_set_copy); - - safe_btree_map my_map; - for (int i = 0; i < kSetSize; ++i) { - my_map[std::string(i, 'a')] = i; - } - safe_btree_map my_map_copy(my_map); - EXPECT_TRUE(my_map_copy == my_map); - EXPECT_TRUE(my_map == my_map_copy); - EXPECT_FALSE(my_map_copy != my_map); - EXPECT_FALSE(my_map != my_map_copy); - - ++my_map_copy[std::string(7, 'a')]; - EXPECT_FALSE(my_map_copy == my_map); - EXPECT_FALSE(my_map == my_map_copy); - EXPECT_TRUE(my_map_copy != my_map); - EXPECT_TRUE(my_map != my_map_copy); - - my_map_copy = my_map; - my_map["hello"] = kSetSize; - EXPECT_FALSE(my_map_copy == my_map); - EXPECT_FALSE(my_map == my_map_copy); - EXPECT_TRUE(my_map_copy != my_map); - EXPECT_TRUE(my_map != my_map_copy); - - my_map.erase(std::string(kSetSize - 1, 'a')); - EXPECT_FALSE(my_map_copy == my_map); - EXPECT_FALSE(my_map == my_map_copy); - EXPECT_TRUE(my_map_copy != my_map); - EXPECT_TRUE(my_map != my_map_copy); -} - -} // namespace -} // namespace btree diff --git a/tommyds/benchmark/lib/cube/binary-search-tesseract-1.0.c b/tommyds/benchmark/lib/cube/binary-search-tesseract-1.0.c deleted file mode 100644 index fdde479..0000000 --- a/tommyds/benchmark/lib/cube/binary-search-tesseract-1.0.c +++ /dev/null @@ -1,923 +0,0 @@ -/* - Copyright 2014 Gregorius van den Hoven - gregoriusvandenhoven@gmail.com -*/ - -/* - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/* - Binary Search Tesseract v1.0 -*/ - -#include -#include -#include - -#define BSC_M 64 - -#define BSC_X_MAX 128 -#define BSC_Y_MAX 64 -#define BSC_Z_MAX 32 - -#define BSC_X_MIN 32 -#define BSC_Y_MIN 16 -#define BSC_Z_MIN 8 - -struct cube -{ - int *w_floor; - struct w_node **w_axis; - unsigned char *x_size; - int *w_volume; - int volume; - short w_size; -}; - -struct w_node -{ - int x_floor[BSC_X_MAX]; - struct x_node *x_axis[BSC_X_MAX]; - unsigned char y_size[BSC_X_MAX]; - short x_volume[BSC_X_MAX]; -}; - -struct x_node -{ - int y_floor[BSC_Y_MAX]; - struct y_node *y_axis[BSC_Y_MAX]; - unsigned char z_size[BSC_Y_MAX]; -}; - -struct y_node -{ - int z_keys[BSC_Z_MAX]; - void *z_vals[BSC_Z_MAX]; -}; - -void *find_key(struct cube *cube, int key, short *w, short *x, short *y, short *z); - -void split_w_node(struct cube *cube, short w); -void merge_w_node(struct cube *cube, short w1, short w2); - -void split_x_node(struct cube *cube, short w, short x); -void merge_x_node(struct cube *cube, short w, short x1, short x2); - -void split_y_node(struct cube *cube, short w, short x, short y); -void merge_y_node(struct cube *cube, short w, short x, short y1, short y2); - -void insert_z_node(struct cube *cube, short w, short x, short y, short z, int key, void *val); -void *remove_z_node(struct cube *cube, short w, short x, short y, short z); - -inline void *find_index(struct cube *cube, int index, short *w_index, short *x_index, short *y_index, short *z_index) -{ - struct w_node *w_node; - struct x_node *x_node; - struct y_node *y_node; - register short w, x, y; - int total; - - if (index < 0 || index >= cube->volume) - { - return NULL; - } - - if (index < cube->volume / 2) - { - total = 0; - - for (w = 0 ; w < cube->w_size ; w++) - { - w_node = cube->w_axis[w]; - - if (total + cube->w_volume[w] > index) - { - if (index > total + cube->w_volume[w] / 2) - { - total += cube->w_volume[w]; - - goto backward_x; - } - forward_x: - - for (x = 0 ; x < cube->x_size[w] ; x++) - { - x_node = w_node->x_axis[x]; - - if (total + w_node->x_volume[x] > index) - { - if (index > total + w_node->x_volume[x] / 2) - { - total += w_node->x_volume[x]; - - goto backward_y; - } - forward_y: - - for (y = 0 ; y < w_node->y_size[x] ; y++) - { - y_node = x_node->y_axis[y]; - - if (total + x_node->z_size[y] > index) - { - *w_index = w; - *x_index = x; - *y_index = y; - *z_index = index - total; - - return y_node->z_vals[index - total]; - } - total += x_node->z_size[y]; - } - } - total += w_node->x_volume[x]; - } - } - total += cube->w_volume[w]; - } - } - else - { - total = cube->volume; - - for (w = cube->w_size - 1 ; w >= 0 ; w--) - { - w_node = cube->w_axis[w]; - - if (total - cube->w_volume[w] <= index) - { - if (index < total - cube->w_volume[w] / 2) - { - total -= cube->w_volume[w]; - - goto forward_x; - } - backward_x: - - for (x = cube->x_size[w] - 1 ; x >= 0 ; x--) - { - x_node = w_node->x_axis[x]; - - if (total - w_node->x_volume[x] <= index) - { - if (index < total - w_node->x_volume[x] / 2) - { - total -= w_node->x_volume[x]; - - goto forward_y; - } - backward_y: - - for (y = w_node->y_size[x] - 1 ; y >= 0 ; y--) - { - y_node = x_node->y_axis[y]; - - if (total - x_node->z_size[y] <= index) - { - *w_index = w; - *x_index = x; - *y_index = y; - *z_index = x_node->z_size[y] - (total - index); - - return y_node->z_vals[x_node->z_size[y] - (total - index)]; - } - total -= x_node->z_size[y]; - } - } - total -= w_node->x_volume[x]; - } - } - total -= cube->w_volume[w]; - } - } - return NULL; -} - - -void *get_key(struct cube *cube, int key) -{ - short w, x, y, z; - - return find_key(cube, key, &w, &x, &y, &z); -} - -void *del_key(struct cube *cube, int key) -{ - short w, x, y, z; - - if (find_key(cube, key, &w, &x, &y, &z)) - { - return remove_z_node(cube, w, x, y, z); - } - return NULL; -} - -void *get_index(struct cube *cube, int index) -{ - short w, x, y, z; - - return find_index(cube, index, &w, &x, &y, &z); -} - -void *del_index(struct cube *cube, int index) -{ - short w, x, y, z; - - if (find_index(cube, index, &w, &x, &y, &z)) - { - return remove_z_node(cube, w, x, y, z); - } - return NULL; -} - -struct cube *create_cube(void) -{ - struct cube *cube; - - cube = (struct cube *) calloc(1, sizeof(struct cube)); - - return cube; -} - -void destroy_cube(struct cube *cube) -{ - if (cube->w_size) - { - struct w_node *w_node; - struct x_node *x_node; - struct y_node *y_node; - - register short w, x, y; - - for (w = cube->w_size - 1 ; w >= 0 ; w--) - { - w_node = cube->w_axis[w]; - - for (x = cube->x_size[w] - 1 ; x >= 0 ; x--) - { - x_node = w_node->x_axis[x]; - - for (y = w_node->y_size[x] - 1 ; y >= 0 ; y--) - { - y_node = x_node->y_axis[y]; - - free(y_node); - } - free(x_node); - } - free(w_node); - } - free(cube->w_floor); - free(cube->w_axis); - free(cube->w_volume); - free(cube->x_size); - } - free(cube); -} - -void set_key(struct cube *cube, int key, void *val) -{ - struct w_node *w_node; - struct x_node *x_node; - struct y_node *y_node; - - short mid, w, x, y, z; - - if (cube->w_size == 0) - { - cube->w_floor = (int *) malloc(BSC_M * sizeof(int)); - cube->w_axis = (struct w_node **) malloc(BSC_M * sizeof(struct w_node *)); - cube->w_volume = (int *) malloc(BSC_M * sizeof(int)); - cube->x_size = (unsigned char *) malloc(BSC_M * sizeof(unsigned char)); - - w_node = cube->w_axis[0] = (struct w_node *) malloc(sizeof(struct w_node)); - - x_node = w_node->x_axis[0] = (struct x_node *) malloc(sizeof(struct x_node)); - - y_node = x_node->y_axis[0] = (struct y_node *) malloc(sizeof(struct y_node)); - - x_node->z_size[0] = 0; - - cube->w_size = cube->x_size[0] = w_node->y_size[0] = 1; - cube->volume = cube->w_volume[0] = w_node->x_volume[0] = 0; - - w = x = y = z = 0; - - cube->w_floor[0] = w_node->x_floor[0] = x_node->y_floor[0] = key; - - goto insert; - } - - if (key < cube->w_floor[0]) - { - w_node = cube->w_axis[0]; - x_node = w_node->x_axis[0]; - y_node = x_node->y_axis[0]; - - w = x = y = z = 0; - - cube->w_floor[0] = w_node->x_floor[0] = x_node->y_floor[0] = key; - - goto insert; - } - - // w - - mid = w = cube->w_size - 1; - - while (mid > 3) - { - mid /= 2; - - if (key < cube->w_floor[w - mid]) w -= mid; - } - while (key < cube->w_floor[w]) --w; - - w_node = cube->w_axis[w]; - - // x - - mid = x = cube->x_size[w] - 1; - - while (mid > 3) - { - mid /= 2; - - if (key < w_node->x_floor[x - mid]) x -= mid; - } - while (key < w_node->x_floor[x]) --x; - - x_node = w_node->x_axis[x]; - - // y - - mid = y = w_node->y_size[x] - 1; - - while (mid > 7) - { - mid /= 4; - - if (key < x_node->y_floor[y - mid]) - { - y -= mid; - if (key < x_node->y_floor[y - mid]) - { - y -= mid; - if (key < x_node->y_floor[y - mid]) - { - y -= mid; - } - } - } - } - while (key < x_node->y_floor[y]) --y; - - y_node = x_node->y_axis[y]; - - // z - - mid = z = x_node->z_size[y] - 1; - - while (mid > 7) - { - mid /= 4; - - if (key < y_node->z_keys[z - mid]) - { - z -= mid; - if (key < y_node->z_keys[z - mid]) - { - z -= mid; - if (key < y_node->z_keys[z - mid]) - { - z -= mid; - } - } - } - } - while (key < y_node->z_keys[z]) --z; - - if (key == y_node->z_keys[z]) - { - y_node->z_vals[z] = val; - - return; - } - - ++z; - - insert: - - ++cube->volume; - ++cube->w_volume[w]; - ++w_node->x_volume[x]; - - ++x_node->z_size[y]; - - if (z + 1 != x_node->z_size[y]) - { - memmove(&y_node->z_keys[z + 1], &y_node->z_keys[z], (x_node->z_size[y] - z - 1) * sizeof(int)); - memmove(&y_node->z_vals[z + 1], &y_node->z_vals[z], (x_node->z_size[y] - z - 1) * sizeof(void *)); - } - - y_node->z_keys[z] = key; - y_node->z_vals[z] = val; - - if (x_node->z_size[y] == BSC_Z_MAX) - { - split_y_node(cube, w, x, y); - - if (w_node->y_size[x] == BSC_Y_MAX) - { - split_x_node(cube, w, x); - - if (cube->x_size[w] == BSC_X_MAX) - { - split_w_node(cube, w); - } - } - } -} - -inline void *find_key(struct cube *cube, int key, short *w_index, short *x_index, short *y_index, short *z_index) -{ - struct w_node *w_node; - struct x_node *x_node; - struct y_node *y_node; - - short mid, w, x, y, z; - - if (cube->w_size == 0 || key < cube->w_floor[0]) - { - *w_index = *x_index = *y_index = *z_index = 0; - - return NULL; - } - - // w - - mid = w = cube->w_size - 1; - - while (mid > 3) - { - mid /= 2; - - if (key < cube->w_floor[w - mid]) w -= mid; - } - while (key < cube->w_floor[w]) --w; - - w_node = cube->w_axis[w]; - - // x - - mid = x = cube->x_size[w] - 1; - - while (mid > 3) - { - mid /= 2; - - if (key < w_node->x_floor[x - mid]) x -= mid; - } - while (key < w_node->x_floor[x]) --x; - - x_node = w_node->x_axis[x]; - - // y - - mid = y = w_node->y_size[x] - 1; - - while (mid > 7) - { - mid /= 4; - - if (key < x_node->y_floor[y - mid]) - { - y -= mid; - if (key < x_node->y_floor[y - mid]) - { - y -= mid; - if (key < x_node->y_floor[y - mid]) - { - y -= mid; - } - } - } - } - while (key < x_node->y_floor[y]) --y; - - y_node = x_node->y_axis[y]; - - // z - - mid = z = x_node->z_size[y] - 1; - - while (mid > 7) - { - mid /= 4; - - if (key < y_node->z_keys[z - mid]) - { - z -= mid; - if (key < y_node->z_keys[z - mid]) - { - z -= mid; - if (key < y_node->z_keys[z - mid]) - { - z -= mid; - } - } - } - } - while (key < y_node->z_keys[z]) --z; - - *w_index = w; - *x_index = x; - *y_index = y; - - if (key == y_node->z_keys[z]) - { - *z_index = z; - - return y_node->z_vals[z]; - } - - *z_index = z + 1; - - return NULL; -} - -inline void insert_w_node(struct cube *cube, short w) -{ - ++cube->w_size; - - if (cube->w_size % BSC_M == 0) - { - cube->w_floor = (int *) realloc(cube->w_floor, (cube->w_size + BSC_M) * sizeof(int)); - cube->w_axis = (struct w_node **) realloc(cube->w_axis, (cube->w_size + BSC_M) * sizeof(struct w_node *)); - cube->w_volume = (int *) realloc(cube->w_volume, (cube->w_size + BSC_M) * sizeof(int)); - cube->x_size = (unsigned char *) realloc(cube->x_size, (cube->w_size + BSC_M) * sizeof(unsigned char)); - } - - if (w + 1 != cube->w_size) - { - memmove(&cube->w_floor[w + 1], &cube->w_floor[w], (cube->w_size - w - 1) * sizeof(int)); - memmove(&cube->w_axis[w + 1], &cube->w_axis[w], (cube->w_size - w - 1) * sizeof(struct w_node *)); - memmove(&cube->w_volume[w + 1], &cube->w_volume[w], (cube->w_size - w - 1) * sizeof(int)); - memmove(&cube->x_size[w + 1], &cube->x_size[w], (cube->w_size - w - 1) * sizeof(unsigned char)); - } - - cube->w_axis[w] = (struct w_node *) malloc(sizeof(struct w_node)); -} - -void remove_w_node(struct cube *cube, short w) -{ - cube->w_size--; - - free(cube->w_axis[w]); - - if (cube->w_size) - { - if (cube->w_size != w) - { - memmove(&cube->w_floor[w], &cube->w_floor[w + 1], (cube->w_size - w) * sizeof(int)); - memmove(&cube->w_axis[w], &cube->w_axis[w + 1], (cube->w_size - w) * sizeof(struct w_node *)); - memmove(&cube->w_volume[w], &cube->w_volume[w + 1], (cube->w_size - w) * sizeof(int)); - memmove(&cube->x_size[w], &cube->x_size[w + 1], (cube->w_size - w) * sizeof(unsigned char)); - } - } - else - { - free(cube->w_floor); - free(cube->w_axis); - free(cube->w_volume); - free(cube->x_size); - } -} - -inline void insert_x_node(struct cube *cube, short w, short x) -{ - struct w_node *w_node = cube->w_axis[w]; - - short x_size = ++cube->x_size[w]; - - if (x_size != x + 1) - { - memmove(&w_node->x_floor[x + 1], &w_node->x_floor[x], (x_size - x - 1) * sizeof(int)); - memmove(&w_node->x_axis[x + 1], &w_node->x_axis[x], (x_size - x - 1) * sizeof(struct x_node *)); - memmove(&w_node->x_volume[x + 1], &w_node->x_volume[x], (x_size - x - 1) * sizeof(short)); - memmove(&w_node->y_size[x + 1], &w_node->y_size[x], (x_size - x - 1) * sizeof(unsigned char)); - } - - w_node->x_axis[x] = (struct x_node *) malloc(sizeof(struct x_node)); -} - -void remove_x_node(struct cube *cube, short w, short x) -{ - struct w_node *w_node = cube->w_axis[w]; - - cube->x_size[w]--; - - free(w_node->x_axis[x]); - - if (cube->x_size[w]) - { - if (cube->x_size[w] != x) - { - memmove(&w_node->x_floor[x], &w_node->x_floor[x + 1], (cube->x_size[w] - x ) * sizeof(int)); - memmove(&w_node->x_axis[x], &w_node->x_axis[x + 1], (cube->x_size[w] - x ) * sizeof(struct x_node *)); - memmove(&w_node->x_volume[x], &w_node->x_volume[x + 1], (cube->x_size[w] - x ) * sizeof(short)); - memmove(&w_node->y_size[x], &w_node->y_size[x + 1], (cube->x_size[w] - x ) * sizeof(unsigned char)); - } - - if (x == 0) - { - cube->w_floor[w] = w_node->x_floor[0]; - } - } - else - { - remove_w_node(cube, w); - } -} - -inline void insert_y_node(struct cube *cube, short w, short x, short y) -{ - struct x_node *x_node = cube->w_axis[w]->x_axis[x]; - - short y_size = ++cube->w_axis[w]->y_size[x]; - - if (y_size != y + 1) - { - memmove(&x_node->y_floor[y + 1], &x_node->y_floor[y], (y_size - y - 1) * sizeof(int)); - memmove(&x_node->y_axis[y + 1], &x_node->y_axis[y], (y_size - y - 1) * sizeof(struct y_node *)); - memmove(&x_node->z_size[y + 1], &x_node->z_size[y], (y_size - y - 1) * sizeof(unsigned char)); - } - - x_node->y_axis[y] = (struct y_node *) malloc(sizeof(struct y_node)); -} - -void remove_y_node(struct cube *cube, short w, short x, short y) -{ - struct w_node *w_node = cube->w_axis[w]; - struct x_node *x_node = w_node->x_axis[x]; - - w_node->y_size[x]--; - - free(x_node->y_axis[y]); - - if (w_node->y_size[x]) - { - if (w_node->y_size[x] != y) - { - - memmove(&x_node->y_floor[y], &x_node->y_floor[y + 1], (w_node->y_size[x] - y ) * sizeof(int)); - memmove(&x_node->y_axis[y], &x_node->y_axis[y + 1], (w_node->y_size[x] - y ) * sizeof(struct y_node *)); - memmove(&x_node->z_size[y], &x_node->z_size[y + 1], (w_node->y_size[x] - y ) * sizeof(unsigned char)); - } - - if (y == 0) - { - cube->w_axis[w]->x_floor[x] = x_node->y_floor[0]; - - if (x == 0) - { - cube->w_floor[w] = x_node->y_floor[0]; - } - } - } - else - { - remove_x_node(cube, w, x); - } -} - -inline void *remove_z_node(struct cube *cube, short w, short x, short y, short z) -{ - struct w_node *w_node = cube->w_axis[w]; - struct x_node *x_node = w_node->x_axis[x]; - struct y_node *y_node = x_node->y_axis[y]; - void *val; - - cube->volume--; - - cube->w_volume[w]--; - w_node->x_volume[x]--; - - x_node->z_size[y]--; - - val = y_node->z_vals[z]; - - if (x_node->z_size[y] != z) - { - memmove(&y_node->z_keys[z], &y_node->z_keys[z + 1], (x_node->z_size[y] - z) * sizeof(int)); - memmove(&y_node->z_vals[z], &y_node->z_vals[z + 1], (x_node->z_size[y] - z) * sizeof(void *)); - } - - if (x_node->z_size[y]) - { - if (z == 0) - { - x_node->y_floor[y] = y_node->z_keys[z]; - - if (y == 0) - { - w_node->x_floor[x] = y_node->z_keys[z]; - - if (x == 0) - { - cube->w_floor[w] = y_node->z_keys[z]; - } - } - } - - if (y && x_node->z_size[y] < BSC_Z_MIN && x_node->z_size[y - 1] < BSC_Z_MIN) - { - merge_y_node(cube, w, x, y - 1, y); - - if (x && w_node->y_size[x] < BSC_Y_MIN && w_node->y_size[x - 1] < BSC_Y_MIN) - { - merge_x_node(cube, w, x - 1, x); - - if (w && cube->x_size[w] < BSC_X_MIN && cube->x_size[w - 1] < BSC_X_MIN) - { - merge_w_node(cube, w - 1, w); - } - } - } - } - else - { - remove_y_node(cube, w, x, y); - } - return val; -} - -void split_w_node(struct cube *cube, short w) -{ - struct w_node *w_node1, *w_node2; - short x; - int volume; - - insert_w_node(cube, w + 1); - - w_node1 = cube->w_axis[w]; - w_node2 = cube->w_axis[w + 1]; - - cube->x_size[w + 1] = cube->x_size[w] / 2; - cube->x_size[w] -= cube->x_size[w + 1]; - - memcpy(&w_node2->x_floor[0], &w_node1->x_floor[cube->x_size[w]], cube->x_size[w + 1] * sizeof(int)); - memcpy(&w_node2->x_axis[0], &w_node1->x_axis[cube->x_size[w]], cube->x_size[w + 1] * sizeof(struct x_node *)); - memcpy(&w_node2->x_volume[0], &w_node1->x_volume[cube->x_size[w]], cube->x_size[w + 1] * sizeof(short)); - memcpy(&w_node2->y_size[0], &w_node1->y_size[cube->x_size[w]], cube->x_size[w + 1] * sizeof(unsigned char)); - - for (x = volume = 0 ; x < cube->x_size[w] ; x++) - { - volume += w_node1->x_volume[x]; - } - - cube->w_volume[w + 1] = cube->w_volume[w] - volume; - cube->w_volume[w] = volume; - - cube->w_floor[w + 1] = w_node2->x_floor[0]; -} - -void merge_w_node(struct cube *cube, short w1, short w2) -{ - struct w_node *w_node1 = cube->w_axis[w1]; - struct w_node *w_node2 = cube->w_axis[w2]; - - memcpy(&w_node1->x_floor[cube->x_size[w1]], &w_node2->x_floor[0], cube->x_size[w2] * sizeof(int)); - memcpy(&w_node1->x_axis[cube->x_size[w1]], &w_node2->x_axis[0], cube->x_size[w2] * sizeof(struct x_node *)); - memcpy(&w_node1->x_volume[cube->x_size[w1]], &w_node2->x_volume[0], cube->x_size[w2] * sizeof(short)); - memcpy(&w_node1->y_size[cube->x_size[w1]], &w_node2->y_size[0], cube->x_size[w2] * sizeof(unsigned char)); - - cube->x_size[w1] += cube->x_size[w2]; - - cube->w_volume[w1] += cube->w_volume[w2]; - - remove_w_node(cube, w2); -} - -void split_x_node(struct cube *cube, short w, short x) -{ - struct w_node *w_node = cube->w_axis[w]; - struct x_node *x_node1, *x_node2; - short y; - int volume; - - insert_x_node(cube, w, x + 1); - - x_node1 = w_node->x_axis[x]; - x_node2 = w_node->x_axis[x + 1]; - - w_node->y_size[x + 1] = w_node->y_size[x] / 2; - w_node->y_size[x] -= w_node->y_size[x + 1]; - - memcpy(&x_node2->y_floor[0], &x_node1->y_floor[w_node->y_size[x]], w_node->y_size[x + 1] * sizeof(int)); - memcpy(&x_node2->y_axis[0], &x_node1->y_axis[w_node->y_size[x]], w_node->y_size[x + 1] * sizeof(struct y_node *)); - memcpy(&x_node2->z_size[0], &x_node1->z_size[w_node->y_size[x]], w_node->y_size[x + 1] * sizeof(unsigned char)); - - for (y = volume = 0 ; y < w_node->y_size[x] ; y++) - { - volume += x_node1->z_size[y]; - } - - w_node->x_volume[x + 1] = w_node->x_volume[x] - volume; - w_node->x_volume[x] = volume; - - cube->w_axis[w]->x_floor[x + 1] = x_node2->y_floor[0]; -} - -void merge_x_node(struct cube *cube, short w, short x1, short x2) -{ - struct w_node *w_node = cube->w_axis[w]; - struct x_node *x_node1 = w_node->x_axis[x1]; - struct x_node *x_node2 = w_node->x_axis[x2]; - - memcpy(&x_node1->y_floor[w_node->y_size[x1]], &x_node2->y_floor[0], w_node->y_size[x2] * sizeof(int)); - memcpy(&x_node1->y_axis[w_node->y_size[x1]], &x_node2->y_axis[0], w_node->y_size[x2] * sizeof(struct y_node *)); - memcpy(&x_node1->z_size[w_node->y_size[x1]], &x_node2->z_size[0], w_node->y_size[x2] * sizeof(unsigned char)); - - w_node->y_size[x1] += w_node->y_size[x2]; - - w_node->x_volume[x1] += w_node->x_volume[x2]; - - remove_x_node(cube, w, x2); -} - -void split_y_node(struct cube *cube, short w, short x, short y) -{ - struct x_node *x_node = cube->w_axis[w]->x_axis[x]; - struct y_node *y_node1, *y_node2; - - insert_y_node(cube, w, x, y + 1); - - y_node1 = x_node->y_axis[y]; - y_node2 = x_node->y_axis[y + 1]; - - x_node->z_size[y + 1] = x_node->z_size[y] / 2; - x_node->z_size[y] -= x_node->z_size[y + 1]; - - memcpy(&y_node2->z_keys[0], &y_node1->z_keys[x_node->z_size[y]], x_node->z_size[y + 1] * sizeof(int)); - memcpy(&y_node2->z_vals[0], &y_node1->z_vals[x_node->z_size[y]], x_node->z_size[y + 1] * sizeof(void *)); - - x_node->y_floor[y + 1] = y_node2->z_keys[0]; -} - -void merge_y_node(struct cube *cube, short w, short x, short y1, short y2) -{ - struct x_node *x_node = cube->w_axis[w]->x_axis[x]; - - struct y_node *y_node1 = x_node->y_axis[y1]; - struct y_node *y_node2 = x_node->y_axis[y2]; - - memcpy(&y_node1->z_keys[x_node->z_size[y1]], &y_node2->z_keys[0], x_node->z_size[y2] * sizeof(int)); - memcpy(&y_node1->z_vals[x_node->z_size[y1]], &y_node2->z_vals[0], x_node->z_size[y2] * sizeof(void *)); - - x_node->z_size[y1] += x_node->z_size[y2]; - - remove_y_node(cube, w, x, y2); -} - -size_t size_cube(struct cube *cube) -{ - struct w_node *w_node; - short w, x, y; - size_t size = 0; - - for (w = cube->w_size - 1 ; w >= 0 ; w--) - { - w_node = cube->w_axis[w]; - - for (x = cube->x_size[w] - 1 ; x >= 0 ; x--) - { - for (y = w_node->y_size[x] - 1 ; y >= 0 ; y--) - { - size += sizeof(struct y_node); - } - size += sizeof(struct x_node); - } - size += sizeof(struct w_node); - } - - size += BSC_M * sizeof(int); - size += BSC_M * sizeof(struct w_node *); - size += BSC_M * sizeof(int); - size += BSC_M * sizeof(unsigned char); - size += sizeof(struct cube); - - return size; -} - diff --git a/tommyds/benchmark/lib/google/README b/tommyds/benchmark/lib/google/README deleted file mode 100644 index 150161d..0000000 --- a/tommyds/benchmark/lib/google/README +++ /dev/null @@ -1,14 +0,0 @@ -This is a C version of sparsehash (and also, maybe, densehash) that I -wrote way back when, and served as the inspiration for the C++ -version. The API for the C version is much uglier than the C++, -because of the lack of template support. I believe the class works, -but I'm not convinced it's really flexible or easy enough to use. - -It would be nice to rework this C class to follow the C++ API as -closely as possible (eg have a set_deleted_key() instead of using a -#define like this code does now). I believe the code compiles and -runs, if anybody is interested in using it now, but it's subject to -major change in the future, as people work on it. - -Craig Silverstein -20 March 2005 diff --git a/tommyds/benchmark/lib/google/dense_hash_map b/tommyds/benchmark/lib/google/dense_hash_map deleted file mode 100644 index 875ec41..0000000 --- a/tommyds/benchmark/lib/google/dense_hash_map +++ /dev/null @@ -1,332 +0,0 @@ -// Copyright (c) 2005, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// 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. - -// ---- -// Author: Craig Silverstein -// -// This is just a very thin wrapper over densehashtable.h, just -// like sgi stl's stl_hash_map is a very thin wrapper over -// stl_hashtable. The major thing we define is operator[], because -// we have a concept of a data_type which stl_hashtable doesn't -// (it only has a key and a value). -// -// NOTE: this is exactly like sparse_hash_map.h, with the word -// "sparse" replaced by "dense", except for the addition of -// set_empty_key(). -// -// YOU MUST CALL SET_EMPTY_KEY() IMMEDIATELY AFTER CONSTRUCTION. -// -// Otherwise your program will die in mysterious ways. (Note if you -// use the constructor that takes an InputIterator range, you pass in -// the empty key in the constructor, rather than after. As a result, -// this constructor differs from the standard STL version.) -// -// In other respects, we adhere mostly to the STL semantics for -// hash-map. One important exception is that insert() may invalidate -// iterators entirely -- STL semantics are that insert() may reorder -// iterators, but they all still refer to something valid in the -// hashtable. Not so for us. Likewise, insert() may invalidate -// pointers into the hashtable. (Whether insert invalidates iterators -// and pointers depends on whether it results in a hashtable resize). -// On the plus side, delete() doesn't invalidate iterators or pointers -// at all, or even change the ordering of elements. -// -// Here are a few "power user" tips: -// -// 1) set_deleted_key(): -// If you want to use erase() you *must* call set_deleted_key(), -// in addition to set_empty_key(), after construction. -// The deleted and empty keys must differ. -// -// 2) resize(0): -// When an item is deleted, its memory isn't freed right -// away. This allows you to iterate over a hashtable, -// and call erase(), without invalidating the iterator. -// To force the memory to be freed, call resize(0). -// For tr1 compatibility, this can also be called as rehash(0). -// -// 3) min_load_factor(0.0) -// Setting the minimum load factor to 0.0 guarantees that -// the hash table will never shrink. -// -// Roughly speaking: -// (1) dense_hash_map: fastest, uses the most memory unless entries are small -// (2) sparse_hash_map: slowest, uses the least memory -// (3) hash_map / unordered_map (STL): in the middle -// -// Typically I use sparse_hash_map when I care about space and/or when -// I need to save the hashtable on disk. I use hash_map otherwise. I -// don't personally use dense_hash_set ever; some people use it for -// small sets with lots of lookups. -// -// - dense_hash_map has, typically, about 78% memory overhead (if your -// data takes up X bytes, the hash_map uses .78X more bytes in overhead). -// - sparse_hash_map has about 4 bits overhead per entry. -// - sparse_hash_map can be 3-7 times slower than the others for lookup and, -// especially, inserts. See time_hash_map.cc for details. -// -// See /usr/(local/)?doc/sparsehash-*/dense_hash_map.html -// for information about how to use this class. - -#ifndef _DENSE_HASH_MAP_H_ -#define _DENSE_HASH_MAP_H_ - -#include -#include // for FILE * in read()/write() -#include // for the default template args -#include // for equal_to -#include // for alloc<> -#include // for pair<> -#include HASH_FUN_H // defined in config.h -#include -#include - - -_START_GOOGLE_NAMESPACE_ - -using STL_NAMESPACE::pair; - -template , // defined in sparseconfig.h - class EqualKey = STL_NAMESPACE::equal_to, - class Alloc = libc_allocator_with_realloc > > -class dense_hash_map { - private: - // Apparently select1st is not stl-standard, so we define our own - struct SelectKey { - const Key& operator()(const pair& p) const { - return p.first; - } - }; - struct SetKey { - void operator()(pair* value, const Key& new_key) const { - *const_cast(&value->first) = new_key; - // It would be nice to clear the rest of value here as well, in - // case it's taking up a lot of memory. We do this by clearing - // the value. This assumes T has a zero-arg constructor! - value->second = T(); - } - }; - // For operator[]. - struct DefaultValue { - STL_NAMESPACE::pair operator()(const Key& key) { - return STL_NAMESPACE::make_pair(key, T()); - } - }; - - // The actual data - typedef dense_hashtable, Key, HashFcn, SelectKey, - SetKey, EqualKey, Alloc> ht; - ht rep; - - public: - typedef typename ht::key_type key_type; - typedef T data_type; - typedef T mapped_type; - typedef typename ht::value_type value_type; - typedef typename ht::hasher hasher; - typedef typename ht::key_equal key_equal; - typedef Alloc allocator_type; - - typedef typename ht::size_type size_type; - typedef typename ht::difference_type difference_type; - typedef typename ht::pointer pointer; - typedef typename ht::const_pointer const_pointer; - typedef typename ht::reference reference; - typedef typename ht::const_reference const_reference; - - typedef typename ht::iterator iterator; - typedef typename ht::const_iterator const_iterator; - typedef typename ht::local_iterator local_iterator; - typedef typename ht::const_local_iterator const_local_iterator; - - // Iterator functions - iterator begin() { return rep.begin(); } - iterator end() { return rep.end(); } - const_iterator begin() const { return rep.begin(); } - const_iterator end() const { return rep.end(); } - - - // These come from tr1's unordered_map. For us, a bucket has 0 or 1 elements. - local_iterator begin(size_type i) { return rep.begin(i); } - local_iterator end(size_type i) { return rep.end(i); } - const_local_iterator begin(size_type i) const { return rep.begin(i); } - const_local_iterator end(size_type i) const { return rep.end(i); } - - // Accessor functions - allocator_type get_allocator() const { return rep.get_allocator(); } - hasher hash_funct() const { return rep.hash_funct(); } - hasher hash_function() const { return hash_funct(); } - key_equal key_eq() const { return rep.key_eq(); } - - - // Constructors - explicit dense_hash_map(size_type expected_max_items_in_table = 0, - const hasher& hf = hasher(), - const key_equal& eql = key_equal(), - const allocator_type& alloc = allocator_type()) - : rep(expected_max_items_in_table, hf, eql, SelectKey(), SetKey(), alloc) { - } - - template - dense_hash_map(InputIterator f, InputIterator l, - const key_type& empty_key_val, - size_type expected_max_items_in_table = 0, - const hasher& hf = hasher(), - const key_equal& eql = key_equal(), - const allocator_type& alloc = allocator_type()) - : rep(expected_max_items_in_table, hf, eql, SelectKey(), SetKey(), alloc) { - set_empty_key(empty_key_val); - rep.insert(f, l); - } - // We use the default copy constructor - // We use the default operator=() - // We use the default destructor - - void clear() { rep.clear(); } - // This clears the hash map without resizing it down to the minimum - // bucket count, but rather keeps the number of buckets constant - void clear_no_resize() { rep.clear_no_resize(); } - void swap(dense_hash_map& hs) { rep.swap(hs.rep); } - - - // Functions concerning size - size_type size() const { return rep.size(); } - size_type max_size() const { return rep.max_size(); } - bool empty() const { return rep.empty(); } - size_type bucket_count() const { return rep.bucket_count(); } - size_type max_bucket_count() const { return rep.max_bucket_count(); } - - // These are tr1 methods. bucket() is the bucket the key is or would be in. - size_type bucket_size(size_type i) const { return rep.bucket_size(i); } - size_type bucket(const key_type& key) const { return rep.bucket(key); } - float load_factor() const { - return size() * 1.0f / bucket_count(); - } - float max_load_factor() const { - float shrink, grow; - rep.get_resizing_parameters(&shrink, &grow); - return grow; - } - void max_load_factor(float new_grow) { - float shrink, grow; - rep.get_resizing_parameters(&shrink, &grow); - rep.set_resizing_parameters(shrink, new_grow); - } - // These aren't tr1 methods but perhaps ought to be. - float min_load_factor() const { - float shrink, grow; - rep.get_resizing_parameters(&shrink, &grow); - return shrink; - } - void min_load_factor(float new_shrink) { - float shrink, grow; - rep.get_resizing_parameters(&shrink, &grow); - rep.set_resizing_parameters(new_shrink, grow); - } - // Deprecated; use min_load_factor() or max_load_factor() instead. - void set_resizing_parameters(float shrink, float grow) { - rep.set_resizing_parameters(shrink, grow); - } - - void resize(size_type hint) { rep.resize(hint); } - void rehash(size_type hint) { resize(hint); } // the tr1 name - - // Lookup routines - iterator find(const key_type& key) { return rep.find(key); } - const_iterator find(const key_type& key) const { return rep.find(key); } - - data_type& operator[](const key_type& key) { // This is our value-add! - // If key is in the hashtable, returns find(key)->second, - // otherwise returns insert(value_type(key, T()).first->second. - // Note it does not create an empty T unless the find fails. - return rep.template find_or_insert(key).second; - } - - size_type count(const key_type& key) const { return rep.count(key); } - - pair equal_range(const key_type& key) { - return rep.equal_range(key); - } - pair equal_range(const key_type& key) const { - return rep.equal_range(key); - } - - // Insertion routines - pair insert(const value_type& obj) { return rep.insert(obj); } - template - void insert(InputIterator f, InputIterator l) { rep.insert(f, l); } - void insert(const_iterator f, const_iterator l) { rep.insert(f, l); } - // required for std::insert_iterator; the passed-in iterator is ignored - iterator insert(iterator, const value_type& obj) { return insert(obj).first; } - - - // Deletion and empty routines - // THESE ARE NON-STANDARD! I make you specify an "impossible" key - // value to identify deleted and empty buckets. You can change the - // deleted key as time goes on, or get rid of it entirely to be insert-only. - void set_empty_key(const key_type& key) { // YOU MUST CALL THIS! - rep.set_empty_key(value_type(key, data_type())); // rep wants a value - } - key_type empty_key() const { - return rep.empty_key().first; // rep returns a value - } - - void set_deleted_key(const key_type& key) { rep.set_deleted_key(key); } - void clear_deleted_key() { rep.clear_deleted_key(); } - key_type deleted_key() const { return rep.deleted_key(); } - - // These are standard - size_type erase(const key_type& key) { return rep.erase(key); } - void erase(iterator it) { rep.erase(it); } - void erase(iterator f, iterator l) { rep.erase(f, l); } - - - // Comparison - bool operator==(const dense_hash_map& hs) const { return rep == hs.rep; } - bool operator!=(const dense_hash_map& hs) const { return rep != hs.rep; } - - - // I/O -- this is an add-on for writing metainformation to disk - bool write_metadata(FILE *fp) { return rep.write_metadata(fp); } - bool read_metadata(FILE *fp) { return rep.read_metadata(fp); } - bool write_nopointer_data(FILE *fp) { return rep.write_nopointer_data(fp); } - bool read_nopointer_data(FILE *fp) { return rep.read_nopointer_data(fp); } -}; - -// We need a global swap as well -template -inline void swap(dense_hash_map& hm1, - dense_hash_map& hm2) { - hm1.swap(hm2); -} - -_END_GOOGLE_NAMESPACE_ - -#endif /* _DENSE_HASH_MAP_H_ */ diff --git a/tommyds/benchmark/lib/google/dense_hash_set b/tommyds/benchmark/lib/google/dense_hash_set deleted file mode 100644 index aabb043..0000000 --- a/tommyds/benchmark/lib/google/dense_hash_set +++ /dev/null @@ -1,308 +0,0 @@ -// Copyright (c) 2005, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// 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. - -// --- -// Author: Craig Silverstein -// -// This is just a very thin wrapper over densehashtable.h, just -// like sgi stl's stl_hash_set is a very thin wrapper over -// stl_hashtable. The major thing we define is operator[], because -// we have a concept of a data_type which stl_hashtable doesn't -// (it only has a key and a value). -// -// This is more different from dense_hash_map than you might think, -// because all iterators for sets are const (you obviously can't -// change the key, and for sets there is no value). -// -// NOTE: this is exactly like sparse_hash_set.h, with the word -// "sparse" replaced by "dense", except for the addition of -// set_empty_key(). -// -// YOU MUST CALL SET_EMPTY_KEY() IMMEDIATELY AFTER CONSTRUCTION. -// -// Otherwise your program will die in mysterious ways. (Note if you -// use the constructor that takes an InputIterator range, you pass in -// the empty key in the constructor, rather than after. As a result, -// this constructor differs from the standard STL version.) -// -// In other respects, we adhere mostly to the STL semantics for -// hash-map. One important exception is that insert() may invalidate -// iterators entirely -- STL semantics are that insert() may reorder -// iterators, but they all still refer to something valid in the -// hashtable. Not so for us. Likewise, insert() may invalidate -// pointers into the hashtable. (Whether insert invalidates iterators -// and pointers depends on whether it results in a hashtable resize). -// On the plus side, delete() doesn't invalidate iterators or pointers -// at all, or even change the ordering of elements. -// -// Here are a few "power user" tips: -// -// 1) set_deleted_key(): -// If you want to use erase() you must call set_deleted_key(), -// in addition to set_empty_key(), after construction. -// The deleted and empty keys must differ. -// -// 2) resize(0): -// When an item is deleted, its memory isn't freed right -// away. This allows you to iterate over a hashtable, -// and call erase(), without invalidating the iterator. -// To force the memory to be freed, call resize(0). -// For tr1 compatibility, this can also be called as rehash(0). -// -// 3) min_load_factor(0.0) -// Setting the minimum load factor to 0.0 guarantees that -// the hash table will never shrink. -// -// Roughly speaking: -// (1) dense_hash_set: fastest, uses the most memory unless entries are small -// (2) sparse_hash_set: slowest, uses the least memory -// (3) hash_set / unordered_set (STL): in the middle -// -// Typically I use sparse_hash_set when I care about space and/or when -// I need to save the hashtable on disk. I use hash_set otherwise. I -// don't personally use dense_hash_set ever; some people use it for -// small sets with lots of lookups. -// -// - dense_hash_set has, typically, about 78% memory overhead (if your -// data takes up X bytes, the hash_set uses .78X more bytes in overhead). -// - sparse_hash_set has about 4 bits overhead per entry. -// - sparse_hash_set can be 3-7 times slower than the others for lookup and, -// especially, inserts. See time_hash_map.cc for details. -// -// See /usr/(local/)?doc/sparsehash-*/dense_hash_set.html -// for information about how to use this class. - -#ifndef _DENSE_HASH_SET_H_ -#define _DENSE_HASH_SET_H_ - -#include -#include // for FILE * in read()/write() -#include // for the default template args -#include // for equal_to -#include // for alloc<> -#include // for pair<> -#include HASH_FUN_H // defined in config.h -#include -#include - - -_START_GOOGLE_NAMESPACE_ - -using STL_NAMESPACE::pair; - -template , // defined in sparseconfig.h - class EqualKey = STL_NAMESPACE::equal_to, - class Alloc = libc_allocator_with_realloc > -class dense_hash_set { - private: - // Apparently identity is not stl-standard, so we define our own - struct Identity { - Value& operator()(Value& v) const { return v; } - const Value& operator()(const Value& v) const { return v; } - }; - struct SetKey { - void operator()(Value* value, const Value& new_key) const { - *value = new_key; - } - }; - - // The actual data - typedef dense_hashtable ht; - ht rep; - - public: - typedef typename ht::key_type key_type; - typedef typename ht::value_type value_type; - typedef typename ht::hasher hasher; - typedef typename ht::key_equal key_equal; - typedef Alloc allocator_type; - - typedef typename ht::size_type size_type; - typedef typename ht::difference_type difference_type; - typedef typename ht::const_pointer pointer; - typedef typename ht::const_pointer const_pointer; - typedef typename ht::const_reference reference; - typedef typename ht::const_reference const_reference; - - typedef typename ht::const_iterator iterator; - typedef typename ht::const_iterator const_iterator; - typedef typename ht::const_local_iterator local_iterator; - typedef typename ht::const_local_iterator const_local_iterator; - - - // Iterator functions -- recall all iterators are const - iterator begin() const { return rep.begin(); } - iterator end() const { return rep.end(); } - - // These come from tr1's unordered_set. For us, a bucket has 0 or 1 elements. - local_iterator begin(size_type i) const { return rep.begin(i); } - local_iterator end(size_type i) const { return rep.end(i); } - - - // Accessor functions - allocator_type get_allocator() const { return rep.get_allocator(); } - hasher hash_funct() const { return rep.hash_funct(); } - hasher hash_function() const { return hash_funct(); } // tr1 name - key_equal key_eq() const { return rep.key_eq(); } - - - // Constructors - explicit dense_hash_set(size_type expected_max_items_in_table = 0, - const hasher& hf = hasher(), - const key_equal& eql = key_equal(), - const allocator_type& alloc = allocator_type()) - : rep(expected_max_items_in_table, hf, eql, Identity(), SetKey(), alloc) { - } - - template - dense_hash_set(InputIterator f, InputIterator l, - const key_type& empty_key_val, - size_type expected_max_items_in_table = 0, - const hasher& hf = hasher(), - const key_equal& eql = key_equal(), - const allocator_type& alloc = allocator_type()) - : rep(expected_max_items_in_table, hf, eql, Identity(), SetKey(), alloc) { - set_empty_key(empty_key_val); - rep.insert(f, l); - } - // We use the default copy constructor - // We use the default operator=() - // We use the default destructor - - void clear() { rep.clear(); } - // This clears the hash set without resizing it down to the minimum - // bucket count, but rather keeps the number of buckets constant - void clear_no_resize() { rep.clear_no_resize(); } - void swap(dense_hash_set& hs) { rep.swap(hs.rep); } - - - // Functions concerning size - size_type size() const { return rep.size(); } - size_type max_size() const { return rep.max_size(); } - bool empty() const { return rep.empty(); } - size_type bucket_count() const { return rep.bucket_count(); } - size_type max_bucket_count() const { return rep.max_bucket_count(); } - - // These are tr1 methods. bucket() is the bucket the key is or would be in. - size_type bucket_size(size_type i) const { return rep.bucket_size(i); } - size_type bucket(const key_type& key) const { return rep.bucket(key); } - float load_factor() const { - return size() * 1.0f / bucket_count(); - } - float max_load_factor() const { - float shrink, grow; - rep.get_resizing_parameters(&shrink, &grow); - return grow; - } - void max_load_factor(float new_grow) { - float shrink, grow; - rep.get_resizing_parameters(&shrink, &grow); - rep.set_resizing_parameters(shrink, new_grow); - } - // These aren't tr1 methods but perhaps ought to be. - float min_load_factor() const { - float shrink, grow; - rep.get_resizing_parameters(&shrink, &grow); - return shrink; - } - void min_load_factor(float new_shrink) { - float shrink, grow; - rep.get_resizing_parameters(&shrink, &grow); - rep.set_resizing_parameters(new_shrink, grow); - } - // Deprecated; use min_load_factor() or max_load_factor() instead. - void set_resizing_parameters(float shrink, float grow) { - rep.set_resizing_parameters(shrink, grow); - } - - void resize(size_type hint) { rep.resize(hint); } - void rehash(size_type hint) { resize(hint); } // the tr1 name - - // Lookup routines - iterator find(const key_type& key) const { return rep.find(key); } - - size_type count(const key_type& key) const { return rep.count(key); } - - pair equal_range(const key_type& key) const { - return rep.equal_range(key); - } - - - // Insertion routines - pair insert(const value_type& obj) { - pair p = rep.insert(obj); - return pair(p.first, p.second); // const to non-const - } - template - void insert(InputIterator f, InputIterator l) { rep.insert(f, l); } - void insert(const_iterator f, const_iterator l) { rep.insert(f, l); } - // required for std::insert_iterator; the passed-in iterator is ignored - iterator insert(iterator, const value_type& obj) { return insert(obj).first; } - - - // Deletion and empty routines - // THESE ARE NON-STANDARD! I make you specify an "impossible" key - // value to identify deleted and empty buckets. You can change the - // deleted key as time goes on, or get rid of it entirely to be insert-only. - void set_empty_key(const key_type& key) { rep.set_empty_key(key); } - key_type empty_key() const { return rep.empty_key(); } - - void set_deleted_key(const key_type& key) { rep.set_deleted_key(key); } - void clear_deleted_key() { rep.clear_deleted_key(); } - key_type deleted_key() const { return rep.deleted_key(); } - - // These are standard - size_type erase(const key_type& key) { return rep.erase(key); } - void erase(iterator it) { rep.erase(it); } - void erase(iterator f, iterator l) { rep.erase(f, l); } - - - // Comparison - bool operator==(const dense_hash_set& hs) const { return rep == hs.rep; } - bool operator!=(const dense_hash_set& hs) const { return rep != hs.rep; } - - - // I/O -- this is an add-on for writing metainformation to disk - bool write_metadata(FILE *fp) { return rep.write_metadata(fp); } - bool read_metadata(FILE *fp) { return rep.read_metadata(fp); } - bool write_nopointer_data(FILE *fp) { return rep.write_nopointer_data(fp); } - bool read_nopointer_data(FILE *fp) { return rep.read_nopointer_data(fp); } -}; - -template -inline void swap(dense_hash_set& hs1, - dense_hash_set& hs2) { - hs1.swap(hs2); -} - -_END_GOOGLE_NAMESPACE_ - -#endif /* _DENSE_HASH_SET_H_ */ diff --git a/tommyds/benchmark/lib/google/libchash.c b/tommyds/benchmark/lib/google/libchash.c deleted file mode 100644 index 577f504..0000000 --- a/tommyds/benchmark/lib/google/libchash.c +++ /dev/null @@ -1,1539 +0,0 @@ -/* Copyright (c) 1998 - 2005, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * 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. - * - * --- - * Author: Craig Silverstein - * - * This library is intended to be used for in-memory hash tables, - * though it provides rudimentary permanent-storage capabilities. - * It attempts to be fast, portable, and small. The best algorithm - * to fulfill these goals is an internal probing hashing algorithm, - * as in Knuth, _Art of Computer Programming_, vol III. Unlike - * chained (open) hashing, it doesn't require a pointer for every - * item, yet it is still constant time lookup in practice. - * - * Also to save space, we let the contents (both data and key) that - * you insert be a union: if the key/data is small, we store it - * directly in the hashtable, otherwise we store a pointer to it. - * To keep you from having to figure out which, use KEY_PTR and - * PTR_KEY to convert between the arguments to these functions and - * a pointer to the real data. For instance: - * char key[] = "ab", *key2; - * HTItem *bck; HashTable *ht; - * HashInsert(ht, PTR_KEY(ht, key), 0); - * bck = HashFind(ht, PTR_KEY(ht, "ab")); - * key2 = KEY_PTR(ht, bck->key); - * - * There are a rich set of operations supported: - * AllocateHashTable() -- Allocates a hashtable structure and - * returns it. - * cchKey: if it's a positive number, then each key is a - * fixed-length record of that length. If it's 0, - * the key is assumed to be a \0-terminated string. - * fSaveKey: normally, you are responsible for allocating - * space for the key. If this is 1, we make a - * copy of the key for you. - * ClearHashTable() -- Removes everything from a hashtable - * FreeHashTable() -- Frees memory used by a hashtable - * - * HashFind() -- takes a key (use PTR_KEY) and returns the - * HTItem containing that key, or NULL if the - * key is not in the hashtable. - * HashFindLast() -- returns the item found by last HashFind() - * HashFindOrInsert() -- inserts the key/data pair if the key - * is not already in the hashtable, or - * returns the appropraite HTItem if it is. - * HashFindOrInsertItem() -- takes key/data as an HTItem. - * HashInsert() -- adds a key/data pair to the hashtable. What - * it does if the key is already in the table - * depends on the value of SAMEKEY_OVERWRITE. - * HashInsertItem() -- takes key/data as an HTItem. - * HashDelete() -- removes a key/data pair from the hashtable, - * if it's there. RETURNS 1 if it was there, - * 0 else. - * If you use sparse tables and never delete, the full data - * space is available. Otherwise we steal -2 (maybe -3), - * so you can't have data fields with those values. - * HashDeleteLast() -- deletes the item returned by the last Find(). - * - * HashFirstBucket() -- used to iterate over the buckets in a - * hashtable. DON'T INSERT OR DELETE WHILE - * ITERATING! You can't nest iterations. - * HashNextBucket() -- RETURNS NULL at the end of iterating. - * - * HashSetDeltaGoalSize() -- if you're going to insert 1000 items - * at once, call this fn with arg 1000. - * It grows the table more intelligently. - * - * HashSave() -- saves the hashtable to a file. It saves keys ok, - * but it doesn't know how to interpret the data field, - * so if the data field is a pointer to some complex - * structure, you must send a function that takes a - * file pointer and a pointer to the structure, and - * write whatever you want to write. It should return - * the number of bytes written. If the file is NULL, - * it should just return the number of bytes it would - * write, without writing anything. - * If your data field is just an integer, not a - * pointer, just send NULL for the function. - * HashLoad() -- loads a hashtable. It needs a function that takes - * a file and the size of the structure, and expects - * you to read in the structure and return a pointer - * to it. You must do memory allocation, etc. If - * the data is just a number, send NULL. - * HashLoadKeys() -- unlike HashLoad(), doesn't load the data off disk - * until needed. This saves memory, but if you look - * up the same key a lot, it does a disk access each - * time. - * You can't do Insert() or Delete() on hashtables that were loaded - * from disk. - * - * See libchash.h for parameters you can modify. Make sure LOG_WORD_SIZE - * is defined correctly for your machine! (5 for 32 bit words, 6 for 64). - */ - -#include -#include -#include /* for strcmp, memcmp, etc */ -#include /* ULTRIX needs this for in.h */ -#ifndef WIN32 -//#include /* for reading/writing hashtables */ -#endif -#include -#include "libchash.h" /* all the types */ - - /* if keys are stored directly but cchKey is less than sizeof(ulong), */ - /* this cuts off the bits at the end */ -char grgKeyTruncMask[sizeof(ulong)][sizeof(ulong)]; -#define KEY_TRUNC(ht, key) \ - ( STORES_PTR(ht) || (ht)->cchKey == sizeof(ulong) \ - ? (key) : ((key) & *(ulong *)&(grgKeyTruncMask[(ht)->cchKey][0])) ) - - /* round num up to a multiple of wordsize. (LOG_WORD_SIZE-3 is in bytes) */ -#define WORD_ROUND(num) ( ((num-1) | ((1<<(LOG_WORD_SIZE-3))-1)) + 1 ) -#define NULL_TERMINATED 0 /* val of cchKey if keys are null-term strings */ - - /* Useful operations we do to keys: compare them, copy them, free them */ - -#define KEY_CMP(ht, key1, key2) ( !STORES_PTR(ht) ? (key1) - (key2) : \ - (key1) == (key2) ? 0 : \ - HashKeySize(ht) == NULL_TERMINATED ? \ - strcmp((char *)key1, (char *)key2) :\ - memcmp((void *)key1, (void *)key2, \ - HashKeySize(ht)) ) - -#define COPY_KEY(ht, keyTo, keyFrom) do \ - if ( !STORES_PTR(ht) || !(ht)->fSaveKeys ) \ - (keyTo) = (keyFrom); /* just copy pointer or info */\ - else if ( (ht)->cchKey == NULL_TERMINATED ) /* copy 0-term.ed str */\ - { \ - (keyTo) = (ulong)HTsmalloc( WORD_ROUND(strlen((char *)(keyFrom))+1) ); \ - strcpy((char *)(keyTo), (char *)(keyFrom)); \ - } \ - else \ - { \ - (keyTo) = (ulong) HTsmalloc( WORD_ROUND((ht)->cchKey) ); \ - memcpy( (char *)(keyTo), (char *)(keyFrom), (ht)->cchKey); \ - } \ - while ( 0 ) - -#define FREE_KEY(ht, key) do \ - if ( STORES_PTR(ht) && (ht)->fSaveKeys ) \ - if ( (ht)->cchKey == NULL_TERMINATED ) \ - HTfree((char *)(key), WORD_ROUND(strlen((char *)(key))+1)); \ - else \ - HTfree((char *)(key), WORD_ROUND((ht)->cchKey)); \ - while ( 0 ) - - /* the following are useful for bitmaps */ - /* Format is like this (if 1 word = 4 bits): 3210 7654 ba98 fedc ... */ -typedef ulong HTBitmapPart; /* this has to be unsigned, for >> */ -typedef HTBitmapPart HTBitmap[1<> LOG_WORD_SIZE) << (LOG_WORD_SIZE-3) ) -#define MOD2(i, logmod) ( (i) & ((1<<(logmod))-1) ) -#define DIV_NUM_ENTRIES(i) ( (i) >> LOG_WORD_SIZE ) -#define MOD_NUM_ENTRIES(i) ( MOD2(i, LOG_WORD_SIZE) ) -#define MODBIT(i) ( ((ulong)1) << MOD_NUM_ENTRIES(i) ) - -#define TEST_BITMAP(bm, i) ( (bm)[DIV_NUM_ENTRIES(i)] & MODBIT(i) ? 1 : 0 ) -#define SET_BITMAP(bm, i) (bm)[DIV_NUM_ENTRIES(i)] |= MODBIT(i) -#define CLEAR_BITMAP(bm, i) (bm)[DIV_NUM_ENTRIES(i)] &= ~MODBIT(i) - - /* the following are useful for reading and writing hashtables */ -#define READ_UL(fp, data) \ - do { \ - long _ul; \ - fread(&_ul, sizeof(_ul), 1, (fp)); \ - data = ntohl(_ul); \ - } while (0) - -#define WRITE_UL(fp, data) \ - do { \ - long _ul = htonl((long)(data)); \ - fwrite(&_ul, sizeof(_ul), 1, (fp)); \ - } while (0) - - /* Moves data from disk to memory if necessary. Note dataRead cannot be * - * NULL, because then we might as well (and do) load the data into memory */ -#define LOAD_AND_RETURN(ht, loadCommand) /* lC returns an HTItem * */ \ - if ( !(ht)->fpData ) /* data is stored in memory */ \ - return (loadCommand); \ - else /* must read data off of disk */ \ - { \ - int cchData; \ - HTItem *bck; \ - if ( (ht)->bckData.data ) free((char *)(ht)->bckData.data); \ - ht->bckData.data = (ulong)NULL; /* needed if loadCommand fails */ \ - bck = (loadCommand); \ - if ( bck == NULL ) /* loadCommand failed: key not found */ \ - return NULL; \ - else \ - (ht)->bckData = *bck; \ - fseek(ht->fpData, (ht)->bckData.data, SEEK_SET); \ - READ_UL((ht)->fpData, cchData); \ - (ht)->bckData.data = (ulong)(ht)->dataRead((ht)->fpData, cchData); \ - return &((ht)->bckData); \ - } - - -/* ======================================================================== */ -/* UTILITY ROUTINES */ -/* ---------------------- */ - -/* HTsmalloc() -- safe malloc - * allocates memory, or crashes if the allocation fails. - */ -static void *HTsmalloc(unsigned long size) -{ - void *retval; - - if ( size == 0 ) - return NULL; - retval = (void *)malloc(size); - if ( !retval ) - { - fprintf(stderr, "HTsmalloc: Unable to allocate %lu bytes of memory\n", - size); - exit(1); - } - return retval; -} - -/* HTscalloc() -- safe calloc - * allocates memory and initializes it to 0, or crashes if - * the allocation fails. - */ -static void *HTscalloc(unsigned long size) -{ - void *retval; - - retval = (void *)calloc(size, 1); - if ( !retval && size > 0 ) - { - fprintf(stderr, "HTscalloc: Unable to allocate %lu bytes of memory\n", - size); - exit(1); - } - return retval; -} - -/* HTsrealloc() -- safe calloc - * grows the amount of memory from a source, or crashes if - * the allocation fails. - */ -static void *HTsrealloc(void *ptr, unsigned long new_size, long delta) -{ - if ( ptr == NULL ) - return HTsmalloc(new_size); - ptr = realloc(ptr, new_size); - if ( !ptr && new_size > 0 ) - { - fprintf(stderr, "HTsrealloc: Unable to reallocate %lu bytes of memory\n", - new_size); - exit(1); - } - return ptr; -} - -/* HTfree() -- keep track of memory use - * frees memory using free, but updates count of how much memory - * is being used. - */ -static void HTfree(void *ptr, unsigned long size) -{ - if ( size > 0 ) /* some systems seem to not like freeing NULL */ - free(ptr); -} - -/*************************************************************************\ -| HTcopy() | -| Sometimes we interpret data as a ulong. But ulongs must be | -| aligned on some machines, so instead of casting we copy. | -\*************************************************************************/ - -unsigned long HTcopy(char *ul) -{ - unsigned long retval; - - memcpy(&retval, ul, sizeof(retval)); - return retval; -} - -/*************************************************************************\ -| HTSetupKeyTrunc() | -| If keys are stored directly but cchKey is less than | -| sizeof(ulong), this cuts off the bits at the end. | -\*************************************************************************/ - -static void HTSetupKeyTrunc(void) -{ - int i, j; - - for ( i = 0; i < sizeof(unsigned long); i++ ) - for ( j = 0; j < sizeof(unsigned long); j++ ) - grgKeyTruncMask[i][j] = j < i ? 255 : 0; /* chars have 8 bits */ -} - - -/* ======================================================================== */ -/* TABLE ROUTINES */ -/* -------------------- */ - -/* The idea is that a hashtable with (logically) t buckets is divided - * into t/M groups of M buckets each. (M is a constant set in - * LOG_BM_WORDS for efficiency.) Each group is stored sparsely. - * Thus, inserting into the table causes some array to grow, which is - * slow but still constant time. Lookup involves doing a - * logical-position-to-sparse-position lookup, which is also slow but - * constant time. The larger M is, the slower these operations are - * but the less overhead (slightly). - * - * To store the sparse array, we store a bitmap B, where B[i] = 1 iff - * bucket i is non-empty. Then to look up bucket i we really look up - * array[# of 1s before i in B]. This is constant time for fixed M. - * - * Terminology: the position of an item in the overall table (from - * 1 .. t) is called its "location." The logical position in a group - * (from 1 .. M ) is called its "position." The actual location in - * the array (from 1 .. # of non-empty buckets in the group) is - * called its "offset." - * - * The following operations are supported: - * o Allocate an array with t buckets, all empty - * o Free a array (but not whatever was stored in the buckets) - * o Tell whether or not a bucket is empty - * o Return a bucket with a given location - * o Set the value of a bucket at a given location - * o Iterate through all the buckets in the array - * o Read and write an occupancy bitmap to disk - * o Return how much memory is being allocated by the array structure - */ - -#ifndef SparseBucket /* by default, each bucket holds an HTItem */ -#define SparseBucket HTItem -#endif - -typedef struct SparseBin { - SparseBucket *binSparse; - HTBitmap bmOccupied; /* bmOccupied[i] is 1 if bucket i has an item */ - short cOccupied; /* size of binSparse; useful for iterators, eg */ -} SparseBin; - -typedef struct SparseIterator { - long posGroup; - long posOffset; - SparseBin *binSparse; /* state info, to avoid args for NextBucket() */ - ulong cBuckets; -} SparseIterator; - -#define LOG_LOW_BIN_SIZE ( LOG_BM_WORDS+LOG_WORD_SIZE ) -#define SPARSE_GROUPS(cBuckets) ( (((cBuckets)-1) >> LOG_LOW_BIN_SIZE) + 1 ) - - /* we need a small function to figure out # of items set in the bm */ -static HTOffset EntriesUpto(HTBitmapPart *bm, int i) -{ /* returns # of set bits in 0..i-1 */ - HTOffset retval = 0; - static HTOffset rgcBits[256] = /* # of bits set in one char */ - {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8}; - - if ( i == 0 ) return 0; - for ( ; i > sizeof(*bm)*8; i -= sizeof(*bm)*8, bm++ ) - { /* think of it as loop unrolling */ -#if LOG_WORD_SIZE >= 3 /* 1 byte per word, or more */ - retval += rgcBits[*bm & 255]; /* get the low byte */ -#if LOG_WORD_SIZE >= 4 /* at least 2 bytes */ - retval += rgcBits[(*bm >> 8) & 255]; -#if LOG_WORD_SIZE >= 5 /* at least 4 bytes */ - retval += rgcBits[(*bm >> 16) & 255]; - retval += rgcBits[(*bm >> 24) & 255]; -#if LOG_WORD_SIZE >= 6 /* 8 bytes! */ - retval += rgcBits[(*bm >> 32) & 255]; - retval += rgcBits[(*bm >> 40) & 255]; - retval += rgcBits[(*bm >> 48) & 255]; - retval += rgcBits[(*bm >> 56) & 255]; -#if LOG_WORD_SIZE >= 7 /* not a concern for a while... */ -#error Need to rewrite EntriesUpto to support such big words -#endif /* >8 bytes */ -#endif /* 8 bytes */ -#endif /* 4 bytes */ -#endif /* 2 bytes */ -#endif /* 1 byte */ - } - switch ( i ) { /* from 0 to 63 */ - case 0: - return retval; -#if LOG_WORD_SIZE >= 3 /* 1 byte per word, or more */ - case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: - return (retval + rgcBits[*bm & ((1 << i)-1)]); -#if LOG_WORD_SIZE >= 4 /* at least 2 bytes */ - case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: - return (retval + rgcBits[*bm & 255] + - rgcBits[(*bm >> 8) & ((1 << (i-8))-1)]); -#if LOG_WORD_SIZE >= 5 /* at least 4 bytes */ - case 17: case 18: case 19: case 20: case 21: case 22: case 23: case 24: - return (retval + rgcBits[*bm & 255] + rgcBits[(*bm >> 8) & 255] + - rgcBits[(*bm >> 16) & ((1 << (i-16))-1)]); - case 25: case 26: case 27: case 28: case 29: case 30: case 31: case 32: - return (retval + rgcBits[*bm & 255] + rgcBits[(*bm >> 8) & 255] + - rgcBits[(*bm >> 16) & 255] + - rgcBits[(*bm >> 24) & ((1 << (i-24))-1)]); -#if LOG_WORD_SIZE >= 6 /* 8 bytes! */ - case 33: case 34: case 35: case 36: case 37: case 38: case 39: case 40: - return (retval + rgcBits[*bm & 255] + rgcBits[(*bm >> 8) & 255] + - rgcBits[(*bm >> 16) & 255] + rgcBits[(*bm >> 24) & 255] + - rgcBits[(*bm >> 32) & ((1 << (i-32))-1)]); - case 41: case 42: case 43: case 44: case 45: case 46: case 47: case 48: - return (retval + rgcBits[*bm & 255] + rgcBits[(*bm >> 8) & 255] + - rgcBits[(*bm >> 16) & 255] + rgcBits[(*bm >> 24) & 255] + - rgcBits[(*bm >> 32) & 255] + - rgcBits[(*bm >> 40) & ((1 << (i-40))-1)]); - case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: - return (retval + rgcBits[*bm & 255] + rgcBits[(*bm >> 8) & 255] + - rgcBits[(*bm >> 16) & 255] + rgcBits[(*bm >> 24) & 255] + - rgcBits[(*bm >> 32) & 255] + rgcBits[(*bm >> 40) & 255] + - rgcBits[(*bm >> 48) & ((1 << (i-48))-1)]); - case 57: case 58: case 59: case 60: case 61: case 62: case 63: case 64: - return (retval + rgcBits[*bm & 255] + rgcBits[(*bm >> 8) & 255] + - rgcBits[(*bm >> 16) & 255] + rgcBits[(*bm >> 24) & 255] + - rgcBits[(*bm >> 32) & 255] + rgcBits[(*bm >> 40) & 255] + - rgcBits[(*bm >> 48) & 255] + - rgcBits[(*bm >> 56) & ((1 << (i-56))-1)]); -#endif /* 8 bytes */ -#endif /* 4 bytes */ -#endif /* 2 bytes */ -#endif /* 1 byte */ - } - assert("" == "word size is too big in EntriesUpto()"); - return -1; -} -#define SPARSE_POS_TO_OFFSET(bm, i) ( EntriesUpto(&((bm)[0]), i) ) -#define SPARSE_BUCKET(bin, location) \ - ( (bin)[(location) >> LOG_LOW_BIN_SIZE].binSparse + \ - SPARSE_POS_TO_OFFSET((bin)[(location)>>LOG_LOW_BIN_SIZE].bmOccupied, \ - MOD2(location, LOG_LOW_BIN_SIZE)) ) - - -/*************************************************************************\ -| SparseAllocate() | -| SparseFree() | -| Allocates, sets-to-empty, and frees a sparse array. All you need | -| to tell me is how many buckets you want. I return the number of | -| buckets I actually allocated, setting the array as a parameter. | -| Note that you have to set auxilliary parameters, like cOccupied. | -\*************************************************************************/ - -static ulong SparseAllocate(SparseBin **pbinSparse, ulong cBuckets) -{ - int cGroups = SPARSE_GROUPS(cBuckets); - - *pbinSparse = (SparseBin *) HTscalloc(sizeof(**pbinSparse) * cGroups); - return cGroups << LOG_LOW_BIN_SIZE; -} - -static SparseBin *SparseFree(SparseBin *binSparse, ulong cBuckets) -{ - ulong iGroup, cGroups = SPARSE_GROUPS(cBuckets); - - for ( iGroup = 0; iGroup < cGroups; iGroup++ ) - HTfree(binSparse[iGroup].binSparse, (sizeof(*binSparse[iGroup].binSparse) - * binSparse[iGroup].cOccupied)); - HTfree(binSparse, sizeof(*binSparse) * cGroups); - return NULL; -} - -/*************************************************************************\ -| SparseIsEmpty() | -| SparseFind() | -| You give me a location (ie a number between 1 and t), and I | -| return the bucket at that location, or NULL if the bucket is | -| empty. It's OK to call Find() on an empty table. | -\*************************************************************************/ - -static int SparseIsEmpty(SparseBin *binSparse, ulong location) -{ - return !TEST_BITMAP(binSparse[location>>LOG_LOW_BIN_SIZE].bmOccupied, - MOD2(location, LOG_LOW_BIN_SIZE)); -} - -static SparseBucket *SparseFind(SparseBin *binSparse, ulong location) -{ - if ( SparseIsEmpty(binSparse, location) ) - return NULL; - return SPARSE_BUCKET(binSparse, location); -} - -/*************************************************************************\ -| SparseInsert() | -| You give me a location, and contents to put there, and I insert | -| into that location and RETURN a pointer to the location. If | -| bucket was already occupied, I write over the contents only if | -| *pfOverwrite is 1. We set *pfOverwrite to 1 if there was someone | -| there (whether or not we overwrote) and 0 else. | -\*************************************************************************/ - -static SparseBucket *SparseInsert(SparseBin *binSparse, SparseBucket *bckInsert, - ulong location, int *pfOverwrite) -{ - SparseBucket *bckPlace; - HTOffset offset; - - bckPlace = SparseFind(binSparse, location); - if ( bckPlace ) /* means we replace old contents */ - { - if ( *pfOverwrite ) - *bckPlace = *bckInsert; - *pfOverwrite = 1; - return bckPlace; - } - - binSparse += (location >> LOG_LOW_BIN_SIZE); - offset = SPARSE_POS_TO_OFFSET(binSparse->bmOccupied, - MOD2(location, LOG_LOW_BIN_SIZE)); - binSparse->binSparse = (SparseBucket *) - HTsrealloc(binSparse->binSparse, - sizeof(*binSparse->binSparse) * ++binSparse->cOccupied, - sizeof(*binSparse->binSparse)); - memmove(binSparse->binSparse + offset+1, - binSparse->binSparse + offset, - (binSparse->cOccupied-1 - offset) * sizeof(*binSparse->binSparse)); - binSparse->binSparse[offset] = *bckInsert; - SET_BITMAP(binSparse->bmOccupied, MOD2(location, LOG_LOW_BIN_SIZE)); - *pfOverwrite = 0; - return binSparse->binSparse + offset; -} - -/*************************************************************************\ -| SparseFirstBucket() | -| SparseNextBucket() | -| SparseCurrentBit() | -| Iterate through the occupied buckets of a dense hashtable. You | -| must, of course, have allocated space yourself for the iterator. | -\*************************************************************************/ - -static SparseBucket *SparseNextBucket(SparseIterator *iter) -{ - if ( iter->posOffset != -1 && /* not called from FirstBucket()? */ - (++iter->posOffset < iter->binSparse[iter->posGroup].cOccupied) ) - return iter->binSparse[iter->posGroup].binSparse + iter->posOffset; - - iter->posOffset = 0; /* start the next group */ - for ( iter->posGroup++; iter->posGroup < SPARSE_GROUPS(iter->cBuckets); - iter->posGroup++ ) - if ( iter->binSparse[iter->posGroup].cOccupied > 0 ) - return iter->binSparse[iter->posGroup].binSparse; /* + 0 */ - return NULL; /* all remaining groups were empty */ -} - -static SparseBucket *SparseFirstBucket(SparseIterator *iter, - SparseBin *binSparse, ulong cBuckets) -{ - iter->binSparse = binSparse; /* set it up for NextBucket() */ - iter->cBuckets = cBuckets; - iter->posOffset = -1; /* when we advance, we're at 0 */ - iter->posGroup = -1; - return SparseNextBucket(iter); -} - -/*************************************************************************\ -| SparseWrite() | -| SparseRead() | -| These are routines for storing a sparse hashtable onto disk. We | -| store the number of buckets and a bitmap indicating which buckets | -| are allocated (occupied). The actual contents of the buckets | -| must be stored separately. | -\*************************************************************************/ - -static void SparseWrite(FILE *fp, SparseBin *binSparse, ulong cBuckets) -{ - ulong i, j; - - WRITE_UL(fp, cBuckets); - for ( i = 0; i < SPARSE_GROUPS(cBuckets); i++ ) - for ( j = 0; j < (1<rgBuckets, cBuckets); -} - -static ulong DenseAllocate(DenseBin **pbin, ulong cBuckets) -{ - *pbin = (DenseBin *) HTsmalloc(sizeof(*pbin)); - (*pbin)->rgBuckets = (DenseBucket *) HTsmalloc(sizeof(*(*pbin)->rgBuckets) - * cBuckets); - DenseClear(*pbin, cBuckets); - return cBuckets; -} - -static DenseBin *DenseFree(DenseBin *bin, ulong cBuckets) -{ - HTfree(bin->rgBuckets, sizeof(*bin->rgBuckets) * cBuckets); - HTfree(bin, sizeof(*bin)); - return NULL; -} - -static int DenseIsEmpty(DenseBin *bin, ulong location) -{ - return DENSE_IS_EMPTY(bin->rgBuckets, location); -} - -static DenseBucket *DenseFind(DenseBin *bin, ulong location) -{ - if ( DenseIsEmpty(bin, location) ) - return NULL; - return bin->rgBuckets + location; -} - -static DenseBucket *DenseInsert(DenseBin *bin, DenseBucket *bckInsert, - ulong location, int *pfOverwrite) -{ - DenseBucket *bckPlace; - - bckPlace = DenseFind(bin, location); - if ( bckPlace ) /* means something is already there */ - { - if ( *pfOverwrite ) - *bckPlace = *bckInsert; - *pfOverwrite = 1; /* set to 1 to indicate someone was there */ - return bckPlace; - } - else - { - bin->rgBuckets[location] = *bckInsert; - *pfOverwrite = 0; - return bin->rgBuckets + location; - } -} - -static DenseBucket *DenseNextBucket(DenseIterator *iter) -{ - for ( iter->pos++; iter->pos < iter->cBuckets; iter->pos++ ) - if ( !DenseIsEmpty(iter->bin, iter->pos) ) - return iter->bin->rgBuckets + iter->pos; - return NULL; /* all remaining groups were empty */ -} - -static DenseBucket *DenseFirstBucket(DenseIterator *iter, - DenseBin *bin, ulong cBuckets) -{ - iter->bin = bin; /* set it up for NextBucket() */ - iter->cBuckets = cBuckets; - iter->pos = -1; /* thus the next bucket will be 0 */ - return DenseNextBucket(iter); -} - -static void DenseWrite(FILE *fp, DenseBin *bin, ulong cBuckets) -{ - ulong pos = 0, bit, bm; - - WRITE_UL(fp, cBuckets); - while ( pos < cBuckets ) - { - bm = 0; - for ( bit = 0; bit < 8*sizeof(ulong); bit++ ) - { - if ( !DenseIsEmpty(bin, pos) ) - SET_BITMAP(&bm, bit); /* in fks-hash.h */ - if ( ++pos == cBuckets ) - break; - } - WRITE_UL(fp, bm); - } -} - -static ulong DenseRead(FILE *fp, DenseBin **pbin) -{ - ulong pos = 0, bit, bm, cBuckets; - - READ_UL(fp, cBuckets); - cBuckets = DenseAllocate(pbin, cBuckets); - while ( pos < cBuckets ) - { - READ_UL(fp, bm); - for ( bit = 0; bit < 8*sizeof(ulong); bit++ ) - { - if ( TEST_BITMAP(&bm, bit) ) /* in fks-hash.h */ - DENSE_SET_OCCUPIED((*pbin)->rgBuckets, pos); - else - DENSE_SET_EMPTY((*pbin)->rgBuckets, pos); - if ( ++pos == cBuckets ) - break; - } - } - return cBuckets; -} - -static ulong DenseMemory(ulong cBuckets, ulong cOccupied) -{ - return cBuckets * sizeof(DenseBucket); -} - - -/* ======================================================================== */ -/* HASHING ROUTINES */ -/* ---------------------- */ - -/* Implements a simple quadratic hashing scheme. We have a single hash - * table of size t and a single hash function h(x). When inserting an - * item, first we try h(x) % t. If it's occupied, we try h(x) + - * i*(i-1)/2 % t for increasing values of i until we hit a not-occupied - * space. To make this dynamic, we double the size of the hash table as - * soon as more than half the cells are occupied. When deleting, we can - * choose to shrink the hashtable when less than a quarter of the - * cells are occupied, or we can choose never to shrink the hashtable. - * For lookup, we check h(x) + i*(i-1)/2 % t (starting with i=0) until - * we get a match or we hit an empty space. Note that as a result, - * we can't make a cell empty on deletion, or lookups may end prematurely. - * Instead we mark the cell as "deleted." We thus steal the value - * DELETED as a possible "data" value. As long as data are pointers, - * that's ok. - * The hash increment we use, i(i-1)/2, is not the standard quadratic - * hash increment, which is i^2. i(i-1)/2 covers the entire bucket space - * when the hashtable size is a power of two, as it is for us. In fact, - * the first n probes cover n distinct buckets; then it repeats. This - * guarantees insertion will always succeed. - * If you linear hashing, set JUMP in chash.h. You can also change - * various other parameters there. - */ - -/*************************************************************************\ -| Hash() | -| The hash function I use is due to Bob Jenkins (see | -| http://burtleburtle.net/bob/hash/evahash.html | -| According to http://burtleburtle.net/bob/c/lookup2.c, | -| his implementation is public domain.) | -| It takes 36 instructions, in 18 cycles if you're lucky. | -| hashing depends on the fact the hashtable size is always a | -| power of 2. cBuckets is probably ht->cBuckets. | -\*************************************************************************/ - -#if LOG_WORD_SIZE == 5 /* 32 bit words */ - -#define mix(a,b,c) \ -{ \ - a -= b; a -= c; a ^= (c>>13); \ - b -= c; b -= a; b ^= (a<<8); \ - c -= a; c -= b; c ^= (b>>13); \ - a -= b; a -= c; a ^= (c>>12); \ - b -= c; b -= a; b ^= (a<<16); \ - c -= a; c -= b; c ^= (b>>5); \ - a -= b; a -= c; a ^= (c>>3); \ - b -= c; b -= a; b ^= (a<<10); \ - c -= a; c -= b; c ^= (b>>15); \ -} -#ifdef WORD_HASH /* play with this on little-endian machines */ -#define WORD_AT(ptr) ( *(ulong *)(ptr) ) -#else -#define WORD_AT(ptr) ( (ptr)[0] + ((ulong)(ptr)[1]<<8) + \ - ((ulong)(ptr)[2]<<16) + ((ulong)(ptr)[3]<<24) ) -#endif - -#elif LOG_WORD_SIZE == 6 /* 64 bit words */ - -#define mix(a,b,c) \ -{ \ - a -= b; a -= c; a ^= (c>>43); \ - b -= c; b -= a; b ^= (a<<9); \ - c -= a; c -= b; c ^= (b>>8); \ - a -= b; a -= c; a ^= (c>>38); \ - b -= c; b -= a; b ^= (a<<23); \ - c -= a; c -= b; c ^= (b>>5); \ - a -= b; a -= c; a ^= (c>>35); \ - b -= c; b -= a; b ^= (a<<49); \ - c -= a; c -= b; c ^= (b>>11); \ - a -= b; a -= c; a ^= (c>>12); \ - b -= c; b -= a; b ^= (a<<18); \ - c -= a; c -= b; c ^= (b>>22); \ -} -#ifdef WORD_HASH /* alpha is little-endian, btw */ -#define WORD_AT(ptr) ( *(ulong *)(ptr) ) -#else -#define WORD_AT(ptr) ( (ptr)[0] + ((ulong)(ptr)[1]<<8) + \ - ((ulong)(ptr)[2]<<16) + ((ulong)(ptr)[3]<<24) + \ - ((ulong)(ptr)[4]<<32) + ((ulong)(ptr)[5]<<40) + \ - ((ulong)(ptr)[6]<<48) + ((ulong)(ptr)[7]<<56) ) -#endif - -#else /* neither 32 or 64 bit words */ -#error This hash function can only hash 32 or 64 bit words. Sorry. -#endif - -static ulong Hash(HashTable *ht, char *key, ulong cBuckets) -{ - ulong a, b, c, cchKey, cchKeyOrig; - - cchKeyOrig = ht->cchKey == NULL_TERMINATED ? strlen(key) : ht->cchKey; - a = b = c = 0x9e3779b9; /* the golden ratio; an arbitrary value */ - - for ( cchKey = cchKeyOrig; cchKey >= 3 * sizeof(ulong); - cchKey -= 3 * sizeof(ulong), key += 3 * sizeof(ulong) ) - { - a += WORD_AT(key); - b += WORD_AT(key + sizeof(ulong)); - c += WORD_AT(key + sizeof(ulong)*2); - mix(a,b,c); - } - - c += cchKeyOrig; - switch ( cchKey ) { /* deal with rest. Cases fall through */ -#if LOG_WORD_SIZE == 5 - case 11: c += (ulong)key[10]<<24; - case 10: c += (ulong)key[9]<<16; - case 9 : c += (ulong)key[8]<<8; - /* the first byte of c is reserved for the length */ - case 8 : b += WORD_AT(key+4); a+= WORD_AT(key); break; - case 7 : b += (ulong)key[6]<<16; - case 6 : b += (ulong)key[5]<<8; - case 5 : b += key[4]; - case 4 : a += WORD_AT(key); break; - case 3 : a += (ulong)key[2]<<16; - case 2 : a += (ulong)key[1]<<8; - case 1 : a += key[0]; - /* case 0 : nothing left to add */ -#elif LOG_WORD_SIZE == 6 - case 23: c += (ulong)key[22]<<56; - case 22: c += (ulong)key[21]<<48; - case 21: c += (ulong)key[20]<<40; - case 20: c += (ulong)key[19]<<32; - case 19: c += (ulong)key[18]<<24; - case 18: c += (ulong)key[17]<<16; - case 17: c += (ulong)key[16]<<8; - /* the first byte of c is reserved for the length */ - case 16: b += WORD_AT(key+8); a+= WORD_AT(key); break; - case 15: b += (ulong)key[14]<<48; - case 14: b += (ulong)key[13]<<40; - case 13: b += (ulong)key[12]<<32; - case 12: b += (ulong)key[11]<<24; - case 11: b += (ulong)key[10]<<16; - case 10: b += (ulong)key[ 9]<<8; - case 9: b += (ulong)key[ 8]; - case 8: a += WORD_AT(key); break; - case 7: a += (ulong)key[ 6]<<48; - case 6: a += (ulong)key[ 5]<<40; - case 5: a += (ulong)key[ 4]<<32; - case 4: a += (ulong)key[ 3]<<24; - case 3: a += (ulong)key[ 2]<<16; - case 2: a += (ulong)key[ 1]<<8; - case 1: a += (ulong)key[ 0]; - /* case 0: nothing left to add */ -#endif - } - mix(a,b,c); - return c & (cBuckets-1); -} - - -/*************************************************************************\ -| Rehash() | -| You give me a hashtable, a new size, and a bucket to follow, and | -| I resize the hashtable's bin to be the new size, rehashing | -| everything in it. I keep particular track of the bucket you pass | -| in, and RETURN a pointer to where the item in the bucket got to. | -| (If you pass in NULL, I return an arbitrary pointer.) | -\*************************************************************************/ - -static HTItem *Rehash(HashTable *ht, ulong cNewBuckets, HTItem *bckWatch) -{ - Table *tableNew; - ulong iBucketFirst; - HTItem *bck, *bckNew = NULL; - ulong offset; /* the i in h(x) + i*(i-1)/2 */ - int fOverwrite = 0; /* not an issue: there can be no collisions */ - - assert( ht->table ); - cNewBuckets = Table(Allocate)(&tableNew, cNewBuckets); - /* Since we RETURN the new position of bckWatch, we want * - * to make sure it doesn't get moved due to some table * - * rehashing that comes after it's inserted. Thus, we * - * have to put it in last. This makes the loop weird. */ - for ( bck = HashFirstBucket(ht); ; bck = HashNextBucket(ht) ) - { - if ( bck == NULL ) /* we're done iterating, so look at bckWatch */ - { - bck = bckWatch; - if ( bck == NULL ) /* I guess bckWatch wasn't specified */ - break; - } - else if ( bck == bckWatch ) - continue; /* ignore if we see it during the iteration */ - - offset = 0; /* a new i for a new bucket */ - for ( iBucketFirst = Hash(ht, KEY_PTR(ht, bck->key), cNewBuckets); - !Table(IsEmpty)(tableNew, iBucketFirst); - iBucketFirst = (iBucketFirst + JUMP(KEY_PTR(ht,bck->key), offset)) - & (cNewBuckets-1) ) - ; - bckNew = Table(Insert)(tableNew, bck, iBucketFirst, &fOverwrite); - if ( bck == bckWatch ) /* we're done with the last thing to do */ - break; - } - Table(Free)(ht->table, ht->cBuckets); - ht->table = tableNew; - ht->cBuckets = cNewBuckets; - ht->cDeletedItems = 0; - return bckNew; /* new position of bckWatch, which was inserted last */ -} - -/*************************************************************************\ -| Find() | -| Does the quadratic searching stuff. RETURNS NULL if we don't | -| find an object with the given key, and a pointer to the Item | -| holding the key, if we do. Also sets posLastFind. If piEmpty is | -| non-NULL, we set it to the first open bucket we pass; helpful for | -| doing a later insert if the search fails, for instance. | -\*************************************************************************/ - -static HTItem *Find(HashTable *ht, ulong key, ulong *piEmpty) -{ - ulong iBucketFirst; - HTItem *item; - ulong offset = 0; /* the i in h(x) + i*(i-1)/2 */ - int fFoundEmpty = 0; /* set when we pass over an empty bucket */ - - ht->posLastFind = NULL; /* set up for failure: a new find starts */ - if ( ht->table == NULL ) /* empty hash table: find is bound to fail */ - return NULL; - - iBucketFirst = Hash(ht, KEY_PTR(ht, key), ht->cBuckets); - while ( 1 ) /* now try all i > 0 */ - { - item = Table(Find)(ht->table, iBucketFirst); - if ( item == NULL ) /* it's not in the table */ - { - if ( piEmpty && !fFoundEmpty ) *piEmpty = iBucketFirst; - return NULL; - } - else - { - if ( IS_BCK_DELETED(item) ) /* always 0 ifdef INSERT_ONLY */ - { - if ( piEmpty && !fFoundEmpty ) - { - *piEmpty = iBucketFirst; - fFoundEmpty = 1; - } - } else - if ( !KEY_CMP(ht, key, item->key) ) /* must be occupied */ - { - ht->posLastFind = item; - return item; /* we found it! */ - } - } - iBucketFirst = ((iBucketFirst + JUMP(KEY_PTR(ht, key), offset)) - & (ht->cBuckets-1)); - } -} - -/*************************************************************************\ -| Insert() | -| If an item with the key already exists in the hashtable, RETURNS | -| a pointer to the item (replacing its data if fOverwrite is 1). | -| If not, we find the first place-to-insert (which Find() is nice | -| enough to set for us) and insert the item there, RETURNing a | -| pointer to the item. We might grow the hashtable if it's getting | -| full. Note we include buckets holding DELETED when determining | -| fullness, because they slow down searching. | -\*************************************************************************/ - -static ulong NextPow2(ulong x) /* returns next power of 2 > x, or 2^31 */ -{ - if ( ((x << 1) >> 1) != x ) /* next power of 2 overflows */ - x >>= 1; /* so we return highest power of 2 we can */ - while ( (x & (x-1)) != 0 ) /* blacks out all but the top bit */ - x &= (x-1); - return x << 1; /* makes it the *next* power of 2 */ -} - -static HTItem *Insert(HashTable *ht, ulong key, ulong data, int fOverwrite) -{ - HTItem *item, bckInsert; - ulong iEmpty; /* first empty bucket key probes */ - - if ( ht->table == NULL ) /* empty hash table: find is bound to fail */ - return NULL; - item = Find(ht, key, &iEmpty); - ht->posLastFind = NULL; /* last operation is insert, not find */ - if ( item ) - { - if ( fOverwrite ) - item->data = data; /* key already matches */ - return item; - } - - COPY_KEY(ht, bckInsert.key, key); /* make our own copy of the key */ - bckInsert.data = data; /* oh, and the data too */ - item = Table(Insert)(ht->table, &bckInsert, iEmpty, &fOverwrite); - if ( fOverwrite ) /* we overwrote a deleted bucket */ - ht->cDeletedItems--; - ht->cItems++; /* insert couldn't have overwritten */ - if ( ht->cDeltaGoalSize > 0 ) /* closer to our goal size */ - ht->cDeltaGoalSize--; - if ( ht->cItems + ht->cDeletedItems >= ht->cBuckets * OCCUPANCY_PCT - || ht->cDeltaGoalSize < 0 ) /* we must've overestimated # of deletes */ - item = Rehash(ht, - NextPow2((ulong)(((ht->cDeltaGoalSize > 0 ? - ht->cDeltaGoalSize : 0) - + ht->cItems) / OCCUPANCY_PCT)), - item); - return item; -} - -/*************************************************************************\ -| Delete() | -| Removes the item from the hashtable, and if fShrink is 1, will | -| shrink the hashtable if it's too small (ie even after halving, | -| the ht would be less than half full, though in order to avoid | -| oscillating table size, we insist that after halving the ht would | -| be less than 40% full). RETURNS 1 if the item was found, 0 else. | -| If fLastFindSet is true, then this function is basically | -| DeleteLastFind. | -\*************************************************************************/ - -static int Delete(HashTable *ht, ulong key, int fShrink, int fLastFindSet) -{ - if ( !fLastFindSet && !Find(ht, key, NULL) ) - return 0; - SET_BCK_DELETED(ht, ht->posLastFind); /* find set this, how nice */ - ht->cItems--; - ht->cDeletedItems++; - if ( ht->cDeltaGoalSize < 0 ) /* heading towards our goal of deletion */ - ht->cDeltaGoalSize++; - - if ( fShrink && ht->cItems < ht->cBuckets * OCCUPANCY_PCT*0.4 - && ht->cDeltaGoalSize >= 0 /* wait until we're done deleting */ - && (ht->cBuckets >> 1) >= MIN_HASH_SIZE ) /* shrink */ - Rehash(ht, - NextPow2((ulong)((ht->cItems+ht->cDeltaGoalSize)/OCCUPANCY_PCT)), - NULL); - ht->posLastFind = NULL; /* last operation is delete, not find */ - return 1; -} - - -/* ======================================================================== */ -/* USER-VISIBLE API */ -/* ---------------------- */ - -/*************************************************************************\ -| AllocateHashTable() | -| ClearHashTable() | -| FreeHashTable() | -| Allocate() allocates a hash table and sets up size parameters. | -| Free() frees it. Clear() deletes all the items from the hash | -| table, but frees not. | -| cchKey is < 0 if the keys you send me are meant to be pointers | -| to \0-terminated strings. Then -cchKey is the maximum key size. | -| If cchKey < one word (ulong), the keys you send me are the keys | -| themselves; else the keys you send me are pointers to the data. | -| If fSaveKeys is 1, we copy any keys given to us to insert. We | -| also free these keys when freeing the hash table. If it's 0, the | -| user is responsible for key space management. | -| AllocateHashTable() RETURNS a hash table; the others TAKE one. | -\*************************************************************************/ - -HashTable *AllocateHashTable(int cchKey, int fSaveKeys) -{ - HashTable *ht; - - ht = (HashTable *) HTsmalloc(sizeof(*ht)); /* set everything to 0 */ - ht->cBuckets = Table(Allocate)(&ht->table, MIN_HASH_SIZE); - ht->cchKey = cchKey <= 0 ? NULL_TERMINATED : cchKey; - ht->cItems = 0; - ht->cDeletedItems = 0; - ht->fSaveKeys = fSaveKeys; - ht->cDeltaGoalSize = 0; - ht->iter = HTsmalloc( sizeof(TableIterator) ); - - ht->fpData = NULL; /* set by HashLoad, maybe */ - ht->bckData.data = (ulong) NULL; /* this must be done */ - HTSetupKeyTrunc(); /* in util.c */ - return ht; -} - -void ClearHashTable(HashTable *ht) -{ - HTItem *bck; - - if ( STORES_PTR(ht) && ht->fSaveKeys ) /* need to free keys */ - for ( bck = HashFirstBucket(ht); bck; bck = HashNextBucket(ht) ) - { - FREE_KEY(ht, bck->key); - if ( ht->fSaveKeys == 2 ) /* this means key stored in one block */ - break; /* ...so only free once */ - } - Table(Free)(ht->table, ht->cBuckets); - ht->cBuckets = Table(Allocate)(&ht->table, MIN_HASH_SIZE); - - ht->cItems = 0; - ht->cDeletedItems = 0; - ht->cDeltaGoalSize = 0; - ht->posLastFind = NULL; - ht->fpData = NULL; /* no longer HashLoading */ - if ( ht->bckData.data ) free( (char *)(ht)->bckData.data); - ht->bckData.data = (ulong) NULL; -} - -void FreeHashTable(HashTable *ht) -{ - ClearHashTable(ht); - if ( ht->iter ) HTfree(ht->iter, sizeof(TableIterator)); - if ( ht->table ) Table(Free)(ht->table, ht->cBuckets); - free(ht); -} - -/*************************************************************************\ -| HashFind() | -| HashFindLast() | -| HashFind(): looks in h(x) + i(i-1)/2 % t as i goes up from 0 | -| until we either find the key or hit an empty bucket. RETURNS a | -| pointer to the item in the hit bucket, if we find it, else | -| RETURNS NULL. | -| HashFindLast() returns the item returned by the last | -| HashFind(), which may be NULL if the last HashFind() failed. | -| LOAD_AND_RETURN reads the data from off disk, if necessary. | -\*************************************************************************/ - -HTItem *HashFind(HashTable *ht, ulong key) -{ - LOAD_AND_RETURN(ht, Find(ht, KEY_TRUNC(ht, key), NULL)); -} - -HTItem *HashFindLast(HashTable *ht) -{ - LOAD_AND_RETURN(ht, ht->posLastFind); -} - -/*************************************************************************\ -| HashFindOrInsert() | -| HashFindOrInsertItem() | -| HashInsert() | -| HashInsertItem() | -| HashDelete() | -| HashDeleteLast() | -| Pretty obvious what these guys do. Some take buckets (items), | -| some take keys and data separately. All things RETURN the bucket | -| (a pointer into the hashtable) if appropriate. | -\*************************************************************************/ - -HTItem *HashFindOrInsert(HashTable *ht, ulong key, ulong dataInsert) -{ - /* This is equivalent to Insert without samekey-overwrite */ - return Insert(ht, KEY_TRUNC(ht, key), dataInsert, 0); -} - -HTItem *HashFindOrInsertItem(HashTable *ht, HTItem *pItem) -{ - return HashFindOrInsert(ht, pItem->key, pItem->data); -} - -HTItem *HashInsert(HashTable *ht, ulong key, ulong data) -{ - return Insert(ht, KEY_TRUNC(ht, key), data, SAMEKEY_OVERWRITE); -} - -HTItem *HashInsertItem(HashTable *ht, HTItem *pItem) -{ - return HashInsert(ht, pItem->key, pItem->data); -} - -int HashDelete(HashTable *ht, ulong key) -{ - return Delete(ht, KEY_TRUNC(ht, key), !FAST_DELETE, 0); -} - -int HashDeleteLast(HashTable *ht) -{ - if ( !ht->posLastFind ) /* last find failed */ - return 0; - return Delete(ht, 0, !FAST_DELETE, 1); /* no need to specify a key */ -} - -/*************************************************************************\ -| HashFirstBucket() | -| HashNextBucket() | -| Iterates through the items in the hashtable by iterating through | -| the table. Since we know about deleted buckets and loading data | -| off disk, and the table doesn't, our job is to take care of these | -| things. RETURNS a bucket, or NULL after the last bucket. | -\*************************************************************************/ - -HTItem *HashFirstBucket(HashTable *ht) -{ - HTItem *retval; - - for ( retval = Table(FirstBucket)(ht->iter, ht->table, ht->cBuckets); - retval; retval = Table(NextBucket)(ht->iter) ) - if ( !IS_BCK_DELETED(retval) ) - LOAD_AND_RETURN(ht, retval); - return NULL; -} - -HTItem *HashNextBucket(HashTable *ht) -{ - HTItem *retval; - - while ( (retval=Table(NextBucket)(ht->iter)) ) - if ( !IS_BCK_DELETED(retval) ) - LOAD_AND_RETURN(ht, retval); - return NULL; -} - -/*************************************************************************\ -| HashSetDeltaGoalSize() | -| If we're going to insert 100 items, set the delta goal size to | -| 100 and we take that into account when inserting. Likewise, if | -| we're going to delete 10 items, set it to -100 and we won't | -| rehash until all 100 have been done. It's ok to be wrong, but | -| it's efficient to be right. Returns the delta value. | -\*************************************************************************/ - -int HashSetDeltaGoalSize(HashTable *ht, int delta) -{ - ht->cDeltaGoalSize = delta; -#if FAST_DELETE == 1 || defined INSERT_ONLY - if ( ht->cDeltaGoalSize < 0 ) /* for fast delete, we never */ - ht->cDeltaGoalSize = 0; /* ...rehash after deletion */ -#endif - return ht->cDeltaGoalSize; -} - - -/*************************************************************************\ -| HashSave() | -| HashLoad() | -| HashLoadKeys() | -| Routines for saving and loading the hashtable from disk. We can | -| then use the hashtable in two ways: loading it back into memory | -| (HashLoad()) or loading only the keys into memory, in which case | -| the data for a given key is loaded off disk when the key is | -| retrieved. The data is freed when something new is retrieved in | -| its place, so this is not a "lazy-load" scheme. | -| The key is saved automatically and restored upon load, but the | -| user needs to specify a routine for reading and writing the data. | -| fSaveKeys is of course set to 1 when you read in a hashtable. | -| HashLoad RETURNS a newly allocated hashtable. | -| DATA_WRITE() takes an fp and a char * (representing the data | -| field), and must perform two separate tasks. If fp is NULL, | -| return the number of bytes written. If not, writes the data to | -| disk at the place the fp points to. | -| DATA_READ() takes an fp and the number of bytes in the data | -| field, and returns a char * which points to wherever you've | -| written the data. Thus, you must allocate memory for the data. | -| Both dataRead and dataWrite may be NULL if you just wish to | -| store the data field directly, as an integer. | -\*************************************************************************/ - -void HashSave(FILE *fp, HashTable *ht, int (*dataWrite)(FILE *, char *)) -{ - long cchData, posStart; - HTItem *bck; - - /* File format: magic number (4 bytes) - : cchKey (one word) - : cItems (one word) - : cDeletedItems (one word) - : table info (buckets and a bitmap) - : cchAllKeys (one word) - Then the keys, in a block. If cchKey is NULL_TERMINATED, the keys - are null-terminated too, otherwise this takes up cchKey*cItems bytes. - Note that keys are not written for DELETED buckets. - Then the data: - : EITHER DELETED (one word) to indicate it's a deleted bucket, - : OR number of bytes for this (non-empty) bucket's data - (one word). This is not stored if dataWrite == NULL - since the size is known to be sizeof(ul). Plus: - : the data for this bucket (variable length) - All words are in network byte order. */ - - fprintf(fp, "%s", MAGIC_KEY); - WRITE_UL(fp, ht->cchKey); /* WRITE_UL, READ_UL, etc in fks-hash.h */ - WRITE_UL(fp, ht->cItems); - WRITE_UL(fp, ht->cDeletedItems); - Table(Write)(fp, ht->table, ht->cBuckets); /* writes cBuckets too */ - - WRITE_UL(fp, 0); /* to be replaced with sizeof(key block) */ - posStart = ftell(fp); - for ( bck = HashFirstBucket(ht); bck; bck = HashNextBucket(ht) ) - fwrite(KEY_PTR(ht, bck->key), 1, - (ht->cchKey == NULL_TERMINATED ? - strlen(KEY_PTR(ht, bck->key))+1 : ht->cchKey), fp); - cchData = ftell(fp) - posStart; - fseek(fp, posStart - sizeof(unsigned long), SEEK_SET); - WRITE_UL(fp, cchData); - fseek(fp, 0, SEEK_END); /* done with our sojourn at the header */ - - /* Unlike HashFirstBucket, TableFirstBucket iters through deleted bcks */ - for ( bck = Table(FirstBucket)(ht->iter, ht->table, ht->cBuckets); - bck; bck = Table(NextBucket)(ht->iter) ) - if ( dataWrite == NULL || IS_BCK_DELETED(bck) ) - WRITE_UL(fp, bck->data); - else /* write cchData followed by the data */ - { - WRITE_UL(fp, (*dataWrite)(NULL, (char *)bck->data)); - (*dataWrite)(fp, (char *)bck->data); - } -} - -static HashTable *HashDoLoad(FILE *fp, char * (*dataRead)(FILE *, int), - HashTable *ht) -{ - ulong cchKey; - char szMagicKey[4], *rgchKeys; - HTItem *bck; - - fread(szMagicKey, 1, 4, fp); - if ( strncmp(szMagicKey, MAGIC_KEY, 4) ) - { - fprintf(stderr, "ERROR: not a hash table (magic key is %4.4s, not %s)\n", - szMagicKey, MAGIC_KEY); - exit(3); - } - Table(Free)(ht->table, ht->cBuckets); /* allocated in AllocateHashTable */ - - READ_UL(fp, ht->cchKey); - READ_UL(fp, ht->cItems); - READ_UL(fp, ht->cDeletedItems); - ht->cBuckets = Table(Read)(fp, &ht->table); /* next is the table info */ - - READ_UL(fp, cchKey); - rgchKeys = (char *) HTsmalloc( cchKey ); /* stores all the keys */ - fread(rgchKeys, 1, cchKey, fp); - /* We use the table iterator so we don't try to LOAD_AND_RETURN */ - for ( bck = Table(FirstBucket)(ht->iter, ht->table, ht->cBuckets); - bck; bck = Table(NextBucket)(ht->iter) ) - { - READ_UL(fp, bck->data); /* all we need if dataRead is NULL */ - if ( IS_BCK_DELETED(bck) ) /* always 0 if defined(INSERT_ONLY) */ - continue; /* this is why we read the data first */ - if ( dataRead != NULL ) /* if it's null, we're done */ - if ( !ht->fpData ) /* load data into memory */ - bck->data = (ulong)dataRead(fp, bck->data); - else /* store location of data on disk */ - { - fseek(fp, bck->data, SEEK_CUR); /* bck->data held size of data */ - bck->data = ftell(fp) - bck->data - sizeof(unsigned long); - } - - if ( ht->cchKey == NULL_TERMINATED ) /* now read the key */ - { - bck->key = (ulong) rgchKeys; - rgchKeys = strchr(rgchKeys, '\0') + 1; /* read past the string */ - } - else - { - if ( STORES_PTR(ht) ) /* small keys stored directly */ - bck->key = (ulong) rgchKeys; - else - memcpy(&bck->key, rgchKeys, ht->cchKey); - rgchKeys += ht->cchKey; - } - } - if ( !STORES_PTR(ht) ) /* keys are stored directly */ - HTfree(rgchKeys - cchKey, cchKey); /* we've advanced rgchK to end */ - return ht; -} - -HashTable *HashLoad(FILE *fp, char * (*dataRead)(FILE *, int)) -{ - HashTable *ht; - ht = AllocateHashTable(0, 2); /* cchKey set later, fSaveKey should be 2! */ - return HashDoLoad(fp, dataRead, ht); -} - -HashTable *HashLoadKeys(FILE *fp, char * (*dataRead)(FILE *, int)) -{ - HashTable *ht; - - if ( dataRead == NULL ) - return HashLoad(fp, NULL); /* no reason not to load the data here */ - ht = AllocateHashTable(0, 2); /* cchKey set later, fSaveKey should be 2! */ - ht->fpData = fp; /* tells HashDoLoad() to only load keys */ - ht->dataRead = dataRead; - return HashDoLoad(fp, dataRead, ht); -} - -/*************************************************************************\ -| PrintHashTable() | -| A debugging tool. Prints the entire contents of the hash table, | -| like so: : key of the contents. Returns number of bytes | -| allocated. If time is not -1, we print it as the time required | -| for the hash. If iForm is 0, we just print the stats. If it's | -| 1, we print the keys and data too, but the keys are printed as | -| ulongs. If it's 2, we print the keys correctly (as long numbers | -| or as strings). | -\*************************************************************************/ - -ulong PrintHashTable(HashTable *ht, double time, int iForm) -{ - ulong cbData = 0, cbBin = 0, cItems = 0, cOccupied = 0; - HTItem *item; - - printf("HASH TABLE.\n"); - if ( time > -1.0 ) - { - printf("----------\n"); - printf("Time: %27.2f\n", time); - } - - for ( item = Table(FirstBucket)(ht->iter, ht->table, ht->cBuckets); - item; item = Table(NextBucket)(ht->iter) ) - { - cOccupied++; /* this includes deleted buckets */ - if ( IS_BCK_DELETED(item) ) /* we don't need you for anything else */ - continue; - cItems++; /* this is for a sanity check */ - if ( STORES_PTR(ht) ) - cbData += ht->cchKey == NULL_TERMINATED ? - WORD_ROUND(strlen((char *)item->key)+1) : ht->cchKey; - else - cbBin -= sizeof(item->key), cbData += sizeof(item->key); - cbBin -= sizeof(item->data), cbData += sizeof(item->data); - if ( iForm != 0 ) /* we want the actual contents */ - { - if ( iForm == 2 && ht->cchKey == NULL_TERMINATED ) - printf("%s/%lu\n", (char *)item->key, item->data); - else if ( iForm == 2 && STORES_PTR(ht) ) - printf("%.*s/%lu\n", - (int)ht->cchKey, (char *)item->key, item->data); - else /* either key actually is a ulong, or iForm == 1 */ - printf("%lu/%lu\n", item->key, item->data); - } - } - assert( cItems == ht->cItems ); /* sanity check */ - cbBin = Table(Memory)(ht->cBuckets, cOccupied); - - printf("----------\n"); - printf("%lu buckets (%lu bytes). %lu empty. %lu hold deleted items.\n" - "%lu items (%lu bytes).\n" - "%lu bytes total. %lu bytes (%2.1f%%) of this is ht overhead.\n", - ht->cBuckets, cbBin, ht->cBuckets - cOccupied, cOccupied - ht->cItems, - ht->cItems, cbData, - cbData + cbBin, cbBin, cbBin*100.0/(cbBin+cbData)); - - return cbData + cbBin; -} diff --git a/tommyds/benchmark/lib/google/libchash.h b/tommyds/benchmark/lib/google/libchash.h deleted file mode 100644 index 0c0f70a..0000000 --- a/tommyds/benchmark/lib/google/libchash.h +++ /dev/null @@ -1,252 +0,0 @@ -/* Copyright (c) 1998 - 2005, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * 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. - * - * --- - * Author: Craig Silverstein - * - * This library is intended to be used for in-memory hash tables, - * though it provides rudimentary permanent-storage capabilities. - * It attempts to be fast, portable, and small. The best algorithm - * to fulfill these goals is an internal probing hashing algorithm, - * as in Knuth, _Art of Computer Programming_, vol III. Unlike - * chained (open) hashing, it doesn't require a pointer for every - * item, yet it is still constant time lookup in practice. - * - * Also to save space, we let the contents (both data and key) that - * you insert be a union: if the key/data is small, we store it - * directly in the hashtable, otherwise we store a pointer to it. - * To keep you from having to figure out which, use KEY_PTR and - * PTR_KEY to convert between the arguments to these functions and - * a pointer to the real data. For instance: - * char key[] = "ab", *key2; - * HTItem *bck; HashTable *ht; - * HashInsert(ht, PTR_KEY(ht, key), 0); - * bck = HashFind(ht, PTR_KEY(ht, "ab")); - * key2 = KEY_PTR(ht, bck->key); - * - * There are a rich set of operations supported: - * AllocateHashTable() -- Allocates a hashtable structure and - * returns it. - * cchKey: if it's a positive number, then each key is a - * fixed-length record of that length. If it's 0, - * the key is assumed to be a \0-terminated string. - * fSaveKey: normally, you are responsible for allocating - * space for the key. If this is 1, we make a - * copy of the key for you. - * ClearHashTable() -- Removes everything from a hashtable - * FreeHashTable() -- Frees memory used by a hashtable - * - * HashFind() -- takes a key (use PTR_KEY) and returns the - * HTItem containing that key, or NULL if the - * key is not in the hashtable. - * HashFindLast() -- returns the item found by last HashFind() - * HashFindOrInsert() -- inserts the key/data pair if the key - * is not already in the hashtable, or - * returns the appropraite HTItem if it is. - * HashFindOrInsertItem() -- takes key/data as an HTItem. - * HashInsert() -- adds a key/data pair to the hashtable. What - * it does if the key is already in the table - * depends on the value of SAMEKEY_OVERWRITE. - * HashInsertItem() -- takes key/data as an HTItem. - * HashDelete() -- removes a key/data pair from the hashtable, - * if it's there. RETURNS 1 if it was there, - * 0 else. - * If you use sparse tables and never delete, the full data - * space is available. Otherwise we steal -2 (maybe -3), - * so you can't have data fields with those values. - * HashDeleteLast() -- deletes the item returned by the last Find(). - * - * HashFirstBucket() -- used to iterate over the buckets in a - * hashtable. DON'T INSERT OR DELETE WHILE - * ITERATING! You can't nest iterations. - * HashNextBucket() -- RETURNS NULL at the end of iterating. - * - * HashSetDeltaGoalSize() -- if you're going to insert 1000 items - * at once, call this fn with arg 1000. - * It grows the table more intelligently. - * - * HashSave() -- saves the hashtable to a file. It saves keys ok, - * but it doesn't know how to interpret the data field, - * so if the data field is a pointer to some complex - * structure, you must send a function that takes a - * file pointer and a pointer to the structure, and - * write whatever you want to write. It should return - * the number of bytes written. If the file is NULL, - * it should just return the number of bytes it would - * write, without writing anything. - * If your data field is just an integer, not a - * pointer, just send NULL for the function. - * HashLoad() -- loads a hashtable. It needs a function that takes - * a file and the size of the structure, and expects - * you to read in the structure and return a pointer - * to it. You must do memory allocation, etc. If - * the data is just a number, send NULL. - * HashLoadKeys() -- unlike HashLoad(), doesn't load the data off disk - * until needed. This saves memory, but if you look - * up the same key a lot, it does a disk access each - * time. - * You can't do Insert() or Delete() on hashtables that were loaded - * from disk. - */ - -#include /* includes definition of "ulong", we hope */ -#define ulong u_long - -#define MAGIC_KEY "CHsh" /* when we save the file */ - -#ifndef LOG_WORD_SIZE /* 5 for 32 bit words, 6 for 64 */ -#if defined (__LP64__) || defined (_LP64) -#define LOG_WORD_SIZE 6 /* log_2(sizeof(ulong)) [in bits] */ -#else -#define LOG_WORD_SIZE 5 /* log_2(sizeof(ulong)) [in bits] */ -#endif -#endif - - /* The following gives a speed/time tradeoff: how many buckets are * - * in each bin. 0 gives 32 buckets/bin, which is a good number. */ -#ifndef LOG_BM_WORDS -#define LOG_BM_WORDS 0 /* each group has 2^L_B_W * 32 buckets */ -#endif - - /* The following are all parameters that affect performance. */ -#ifndef JUMP -#define JUMP(key, offset) ( ++(offset) ) /* ( 1 ) for linear hashing */ -#endif -#ifndef Table -#define Table(x) Sparse##x /* Dense##x for dense tables */ -#endif -#ifndef FAST_DELETE -#define FAST_DELETE 0 /* if it's 1, we never shrink the ht */ -#endif -#ifndef SAMEKEY_OVERWRITE -#define SAMEKEY_OVERWRITE 1 /* overwrite item with our key on insert? */ -#endif -#ifndef OCCUPANCY_PCT -#define OCCUPANCY_PCT 0.5 /* large PCT means smaller and slower */ -#endif -#ifndef MIN_HASH_SIZE -#define MIN_HASH_SIZE 512 /* ht size when first created */ -#endif - /* When deleting a bucket, we can't just empty it (future hashes * - * may fail); instead we set the data field to DELETED. Thus you * - * should set DELETED to a data value you never use. Better yet, * - * if you don't need to delete, define INSERT_ONLY. */ -#ifndef INSERT_ONLY -#define DELETED -2UL -#define IS_BCK_DELETED(bck) ( (bck) && (bck)->data == DELETED ) -#define SET_BCK_DELETED(ht, bck) do { (bck)->data = DELETED; \ - FREE_KEY(ht, (bck)->key); } while ( 0 ) -#else -#define IS_BCK_DELETED(bck) 0 -#define SET_BCK_DELETED(ht, bck) \ - do { fprintf(stderr, "Deletion not supported for insert-only hashtable\n");\ - exit(2); } while ( 0 ) -#endif - - /* We need the following only for dense buckets (Dense##x above). * - * If you need to, set this to a value you'll never use for data. */ -#define EMPTY -3UL /* steal more of the bck->data space */ - - - /* This is what an item is. Either can be cast to a pointer. */ -typedef struct { - ulong data; /* 4 bytes for data: either a pointer or an integer */ - ulong key; /* 4 bytes for the key: either a pointer or an int */ -} HTItem; - -struct Table(Bin); /* defined in chash.c, I hope */ -struct Table(Iterator); -typedef struct Table(Bin) Table; /* Expands to SparseBin, etc */ -typedef struct Table(Iterator) TableIterator; - - /* for STORES_PTR to work ok, cchKey MUST BE DEFINED 1st, cItems 2nd! */ -typedef struct HashTable { - ulong cchKey; /* the length of the key, or if it's \0 terminated */ - ulong cItems; /* number of items currently in the hashtable */ - ulong cDeletedItems; /* # of buckets holding DELETE in the hashtable */ - ulong cBuckets; /* size of the table */ - Table *table; /* The actual contents of the hashtable */ - int fSaveKeys; /* 1 if we copy keys locally; 2 if keys in one block */ - int cDeltaGoalSize; /* # of coming inserts (or deletes, if <0) we expect */ - HTItem *posLastFind; /* position of last Find() command */ - TableIterator *iter; /* used in First/NextBucket */ - - FILE *fpData; /* if non-NULL, what item->data points into */ - char * (*dataRead)(FILE *, int); /* how to load data from disk */ - HTItem bckData; /* holds data after being loaded from disk */ -} HashTable; - - /* Small keys are stored and passed directly, but large keys are - * stored and passed as pointers. To make it easier to remember - * what to pass, we provide two functions: - * PTR_KEY: give it a pointer to your data, and it returns - * something appropriate to send to Hash() functions or - * be stored in a data field. - * KEY_PTR: give it something returned by a Hash() routine, and - * it returns a (char *) pointer to the actual data. - */ -#define HashKeySize(ht) ( ((ulong *)(ht))[0] ) /* this is how we inline */ -#define HashSize(ht) ( ((ulong *)(ht))[1] ) /* ...a la C++ :-) */ - -#define STORES_PTR(ht) ( HashKeySize(ht) == 0 || \ - HashKeySize(ht) > sizeof(ulong) ) -#define KEY_PTR(ht, key) ( STORES_PTR(ht) ? (char *)(key) : (char *)&(key) ) -#ifdef DONT_HAVE_TO_WORRY_ABOUT_BUS_ERRORS -#define PTR_KEY(ht, ptr) ( STORES_PTR(ht) ? (ulong)(ptr) : *(ulong *)(ptr) ) -#else -#define PTR_KEY(ht, ptr) ( STORES_PTR(ht) ? (ulong)(ptr) : HTcopy((char *)ptr)) -#endif - - - /* Function prototypes */ -unsigned long HTcopy(char *pul); /* for PTR_KEY, not for users */ - -struct HashTable *AllocateHashTable(int cchKey, int fSaveKeys); -void ClearHashTable(struct HashTable *ht); -void FreeHashTable(struct HashTable *ht); - -HTItem *HashFind(struct HashTable *ht, ulong key); -HTItem *HashFindLast(struct HashTable *ht); -HTItem *HashFindOrInsert(struct HashTable *ht, ulong key, ulong dataInsert); -HTItem *HashFindOrInsertItem(struct HashTable *ht, HTItem *pItem); - -HTItem *HashInsert(struct HashTable *ht, ulong key, ulong data); -HTItem *HashInsertItem(struct HashTable *ht, HTItem *pItem); - -int HashDelete(struct HashTable *ht, ulong key); -int HashDeleteLast(struct HashTable *ht); - -HTItem *HashFirstBucket(struct HashTable *ht); -HTItem *HashNextBucket(struct HashTable *ht); - -int HashSetDeltaGoalSize(struct HashTable *ht, int delta); - -void HashSave(FILE *fp, struct HashTable *ht, int (*write)(FILE *, char *)); -struct HashTable *HashLoad(FILE *fp, char * (*read)(FILE *, int)); -struct HashTable *HashLoadKeys(FILE *fp, char * (*read)(FILE *, int)); diff --git a/tommyds/benchmark/lib/google/sparse_hash_map b/tommyds/benchmark/lib/google/sparse_hash_map deleted file mode 100644 index 2a002f6..0000000 --- a/tommyds/benchmark/lib/google/sparse_hash_map +++ /dev/null @@ -1,309 +0,0 @@ -// Copyright (c) 2005, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// 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. - -// --- -// Author: Craig Silverstein -// -// This is just a very thin wrapper over sparsehashtable.h, just -// like sgi stl's stl_hash_map is a very thin wrapper over -// stl_hashtable. The major thing we define is operator[], because -// we have a concept of a data_type which stl_hashtable doesn't -// (it only has a key and a value). -// -// We adhere mostly to the STL semantics for hash-map. One important -// exception is that insert() may invalidate iterators entirely -- STL -// semantics are that insert() may reorder iterators, but they all -// still refer to something valid in the hashtable. Not so for us. -// Likewise, insert() may invalidate pointers into the hashtable. -// (Whether insert invalidates iterators and pointers depends on -// whether it results in a hashtable resize). On the plus side, -// delete() doesn't invalidate iterators or pointers at all, or even -// change the ordering of elements. -// -// Here are a few "power user" tips: -// -// 1) set_deleted_key(): -// Unlike STL's hash_map, if you want to use erase() you -// *must* call set_deleted_key() after construction. -// -// 2) resize(0): -// When an item is deleted, its memory isn't freed right -// away. This is what allows you to iterate over a hashtable -// and call erase() without invalidating the iterator. -// To force the memory to be freed, call resize(0). -// For tr1 compatibility, this can also be called as rehash(0). -// -// 3) min_load_factor(0.0) -// Setting the minimum load factor to 0.0 guarantees that -// the hash table will never shrink. -// -// Roughly speaking: -// (1) dense_hash_map: fastest, uses the most memory unless entries are small -// (2) sparse_hash_map: slowest, uses the least memory -// (3) hash_map / unordered_map (STL): in the middle -// -// Typically I use sparse_hash_map when I care about space and/or when -// I need to save the hashtable on disk. I use hash_map otherwise. I -// don't personally use dense_hash_map ever; some people use it for -// small maps with lots of lookups. -// -// - dense_hash_map has, typically, about 78% memory overhead (if your -// data takes up X bytes, the hash_map uses .78X more bytes in overhead). -// - sparse_hash_map has about 4 bits overhead per entry. -// - sparse_hash_map can be 3-7 times slower than the others for lookup and, -// especially, inserts. See time_hash_map.cc for details. -// -// See /usr/(local/)?doc/sparsehash-*/sparse_hash_map.html -// for information about how to use this class. - -#ifndef _SPARSE_HASH_MAP_H_ -#define _SPARSE_HASH_MAP_H_ - -#include -#include // for FILE * in read()/write() -#include // for the default template args -#include // for equal_to -#include // for alloc<> -#include // for pair<> -#include HASH_FUN_H // defined in config.h -#include -#include - - -_START_GOOGLE_NAMESPACE_ - -using STL_NAMESPACE::pair; - -template , // defined in sparseconfig.h - class EqualKey = STL_NAMESPACE::equal_to, - class Alloc = libc_allocator_with_realloc > > -class sparse_hash_map { - private: - // Apparently select1st is not stl-standard, so we define our own - struct SelectKey { - const Key& operator()(const pair& p) const { - return p.first; - } - }; - struct SetKey { - void operator()(pair* value, const Key& new_key) const { - *const_cast(&value->first) = new_key; - // It would be nice to clear the rest of value here as well, in - // case it's taking up a lot of memory. We do this by clearing - // the value. This assumes T has a zero-arg constructor! - value->second = T(); - } - }; - // For operator[]. - struct DefaultValue { - STL_NAMESPACE::pair operator()(const Key& key) { - return STL_NAMESPACE::make_pair(key, T()); - } - }; - - // The actual data - typedef sparse_hashtable, Key, HashFcn, SelectKey, - SetKey, EqualKey, Alloc> ht; - ht rep; - - public: - typedef typename ht::key_type key_type; - typedef T data_type; - typedef T mapped_type; - typedef typename ht::value_type value_type; - typedef typename ht::hasher hasher; - typedef typename ht::key_equal key_equal; - typedef Alloc allocator_type; - - typedef typename ht::size_type size_type; - typedef typename ht::difference_type difference_type; - typedef typename ht::pointer pointer; - typedef typename ht::const_pointer const_pointer; - typedef typename ht::reference reference; - typedef typename ht::const_reference const_reference; - - typedef typename ht::iterator iterator; - typedef typename ht::const_iterator const_iterator; - typedef typename ht::local_iterator local_iterator; - typedef typename ht::const_local_iterator const_local_iterator; - - // Iterator functions - iterator begin() { return rep.begin(); } - iterator end() { return rep.end(); } - const_iterator begin() const { return rep.begin(); } - const_iterator end() const { return rep.end(); } - - // These come from tr1's unordered_map. For us, a bucket has 0 or 1 elements. - local_iterator begin(size_type i) { return rep.begin(i); } - local_iterator end(size_type i) { return rep.end(i); } - const_local_iterator begin(size_type i) const { return rep.begin(i); } - const_local_iterator end(size_type i) const { return rep.end(i); } - - // Accessor functions - allocator_type get_allocator() const { return rep.get_allocator(); } - hasher hash_funct() const { return rep.hash_funct(); } - hasher hash_function() const { return hash_funct(); } - key_equal key_eq() const { return rep.key_eq(); } - - - // Constructors - explicit sparse_hash_map(size_type expected_max_items_in_table = 0, - const hasher& hf = hasher(), - const key_equal& eql = key_equal(), - const allocator_type& alloc = allocator_type()) - : rep(expected_max_items_in_table, hf, eql, SelectKey(), SetKey(), alloc) { - } - - template - sparse_hash_map(InputIterator f, InputIterator l, - size_type expected_max_items_in_table = 0, - const hasher& hf = hasher(), - const key_equal& eql = key_equal(), - const allocator_type& alloc = allocator_type()) - : rep(expected_max_items_in_table, hf, eql, SelectKey(), SetKey(), alloc) { - rep.insert(f, l); - } - // We use the default copy constructor - // We use the default operator=() - // We use the default destructor - - void clear() { rep.clear(); } - void swap(sparse_hash_map& hs) { rep.swap(hs.rep); } - - - // Functions concerning size - size_type size() const { return rep.size(); } - size_type max_size() const { return rep.max_size(); } - bool empty() const { return rep.empty(); } - size_type bucket_count() const { return rep.bucket_count(); } - size_type max_bucket_count() const { return rep.max_bucket_count(); } - - // These are tr1 methods. bucket() is the bucket the key is or would be in. - size_type bucket_size(size_type i) const { return rep.bucket_size(i); } - size_type bucket(const key_type& key) const { return rep.bucket(key); } - float load_factor() const { - return size() * 1.0f / bucket_count(); - } - float max_load_factor() const { - float shrink, grow; - rep.get_resizing_parameters(&shrink, &grow); - return grow; - } - void max_load_factor(float new_grow) { - float shrink, grow; - rep.get_resizing_parameters(&shrink, &grow); - rep.set_resizing_parameters(shrink, new_grow); - } - // These aren't tr1 methods but perhaps ought to be. - float min_load_factor() const { - float shrink, grow; - rep.get_resizing_parameters(&shrink, &grow); - return shrink; - } - void min_load_factor(float new_shrink) { - float shrink, grow; - rep.get_resizing_parameters(&shrink, &grow); - rep.set_resizing_parameters(new_shrink, grow); - } - // Deprecated; use min_load_factor() or max_load_factor() instead. - void set_resizing_parameters(float shrink, float grow) { - rep.set_resizing_parameters(shrink, grow); - } - - void resize(size_type hint) { rep.resize(hint); } - void rehash(size_type hint) { resize(hint); } // the tr1 name - - // Lookup routines - iterator find(const key_type& key) { return rep.find(key); } - const_iterator find(const key_type& key) const { return rep.find(key); } - - data_type& operator[](const key_type& key) { // This is our value-add! - // If key is in the hashtable, returns find(key)->second, - // otherwise returns insert(value_type(key, T()).first->second. - // Note it does not create an empty T unless the find fails. - return rep.template find_or_insert(key).second; - } - - size_type count(const key_type& key) const { return rep.count(key); } - - pair equal_range(const key_type& key) { - return rep.equal_range(key); - } - pair equal_range(const key_type& key) const { - return rep.equal_range(key); - } - - // Insertion routines - pair insert(const value_type& obj) { return rep.insert(obj); } - template - void insert(InputIterator f, InputIterator l) { rep.insert(f, l); } - void insert(const_iterator f, const_iterator l) { rep.insert(f, l); } - // required for std::insert_iterator; the passed-in iterator is ignored - iterator insert(iterator, const value_type& obj) { return insert(obj).first; } - - - // Deletion routines - // THESE ARE NON-STANDARD! I make you specify an "impossible" key - // value to identify deleted buckets. You can change the key as - // time goes on, or get rid of it entirely to be insert-only. - void set_deleted_key(const key_type& key) { - rep.set_deleted_key(key); - } - void clear_deleted_key() { rep.clear_deleted_key(); } - key_type deleted_key() const { return rep.deleted_key(); } - - // These are standard - size_type erase(const key_type& key) { return rep.erase(key); } - void erase(iterator it) { rep.erase(it); } - void erase(iterator f, iterator l) { rep.erase(f, l); } - - - // Comparison - bool operator==(const sparse_hash_map& hs) const { return rep == hs.rep; } - bool operator!=(const sparse_hash_map& hs) const { return rep != hs.rep; } - - - // I/O -- this is an add-on for writing metainformation to disk - bool write_metadata(FILE *fp) { return rep.write_metadata(fp); } - bool read_metadata(FILE *fp) { return rep.read_metadata(fp); } - bool write_nopointer_data(FILE *fp) { return rep.write_nopointer_data(fp); } - bool read_nopointer_data(FILE *fp) { return rep.read_nopointer_data(fp); } -}; - -// We need a global swap as well -template -inline void swap(sparse_hash_map& hm1, - sparse_hash_map& hm2) { - hm1.swap(hm2); -} - -_END_GOOGLE_NAMESPACE_ - -#endif /* _SPARSE_HASH_MAP_H_ */ diff --git a/tommyds/benchmark/lib/google/sparse_hash_set b/tommyds/benchmark/lib/google/sparse_hash_set deleted file mode 100644 index 7f34577..0000000 --- a/tommyds/benchmark/lib/google/sparse_hash_set +++ /dev/null @@ -1,285 +0,0 @@ -// Copyright (c) 2005, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// 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. - -// --- -// Author: Craig Silverstein -// -// This is just a very thin wrapper over sparsehashtable.h, just -// like sgi stl's stl_hash_set is a very thin wrapper over -// stl_hashtable. The major thing we define is operator[], because -// we have a concept of a data_type which stl_hashtable doesn't -// (it only has a key and a value). -// -// This is more different from sparse_hash_map than you might think, -// because all iterators for sets are const (you obviously can't -// change the key, and for sets there is no value). -// -// We adhere mostly to the STL semantics for hash-map. One important -// exception is that insert() may invalidate iterators entirely -- STL -// semantics are that insert() may reorder iterators, but they all -// still refer to something valid in the hashtable. Not so for us. -// Likewise, insert() may invalidate pointers into the hashtable. -// (Whether insert invalidates iterators and pointers depends on -// whether it results in a hashtable resize). On the plus side, -// delete() doesn't invalidate iterators or pointers at all, or even -// change the ordering of elements. -// -// Here are a few "power user" tips: -// -// 1) set_deleted_key(): -// Unlike STL's hash_map, if you want to use erase() you -// *must* call set_deleted_key() after construction. -// -// 2) resize(0): -// When an item is deleted, its memory isn't freed right -// away. This allows you to iterate over a hashtable, -// and call erase(), without invalidating the iterator. -// To force the memory to be freed, call resize(0). -// For tr1 compatibility, this can also be called as rehash(0). -// -// 3) min_load_factor(0.0) -// Setting the minimum load factor to 0.0 guarantees that -// the hash table will never shrink. -// -// Roughly speaking: -// (1) dense_hash_set: fastest, uses the most memory unless entries are small -// (2) sparse_hash_set: slowest, uses the least memory -// (3) hash_set / unordered_set (STL): in the middle -// -// Typically I use sparse_hash_set when I care about space and/or when -// I need to save the hashtable on disk. I use hash_set otherwise. I -// don't personally use dense_hash_set ever; some people use it for -// small sets with lots of lookups. -// -// - dense_hash_set has, typically, about 78% memory overhead (if your -// data takes up X bytes, the hash_set uses .78X more bytes in overhead). -// - sparse_hash_set has about 4 bits overhead per entry. -// - sparse_hash_set can be 3-7 times slower than the others for lookup and, -// especially, inserts. See time_hash_map.cc for details. -// -// See /usr/(local/)?doc/sparsehash-*/sparse_hash_set.html -// for information about how to use this class. - -#ifndef _SPARSE_HASH_SET_H_ -#define _SPARSE_HASH_SET_H_ - -#include -#include // for FILE * in read()/write() -#include // for the default template args -#include // for equal_to -#include // for alloc<> -#include // for pair<> -#include HASH_FUN_H // defined in config.h -#include -#include - -_START_GOOGLE_NAMESPACE_ - -using STL_NAMESPACE::pair; - -template , // defined in sparseconfig.h - class EqualKey = STL_NAMESPACE::equal_to, - class Alloc = libc_allocator_with_realloc > -class sparse_hash_set { - private: - // Apparently identity is not stl-standard, so we define our own - struct Identity { - Value& operator()(Value& v) const { return v; } - const Value& operator()(const Value& v) const { return v; } - }; - struct SetKey { - void operator()(Value* value, const Value& new_key) const { - *value = new_key; - } - }; - - typedef sparse_hashtable ht; - ht rep; - - public: - typedef typename ht::key_type key_type; - typedef typename ht::value_type value_type; - typedef typename ht::hasher hasher; - typedef typename ht::key_equal key_equal; - typedef Alloc allocator_type; - - typedef typename ht::size_type size_type; - typedef typename ht::difference_type difference_type; - typedef typename ht::const_pointer pointer; - typedef typename ht::const_pointer const_pointer; - typedef typename ht::const_reference reference; - typedef typename ht::const_reference const_reference; - - typedef typename ht::const_iterator iterator; - typedef typename ht::const_iterator const_iterator; - typedef typename ht::const_local_iterator local_iterator; - typedef typename ht::const_local_iterator const_local_iterator; - - - // Iterator functions -- recall all iterators are const - iterator begin() const { return rep.begin(); } - iterator end() const { return rep.end(); } - - // These come from tr1's unordered_set. For us, a bucket has 0 or 1 elements. - local_iterator begin(size_type i) const { return rep.begin(i); } - local_iterator end(size_type i) const { return rep.end(i); } - - - // Accessor functions - allocator_type get_allocator() const { return rep.get_allocator(); } - hasher hash_funct() const { return rep.hash_funct(); } - hasher hash_function() const { return hash_funct(); } // tr1 name - key_equal key_eq() const { return rep.key_eq(); } - - - // Constructors - explicit sparse_hash_set(size_type expected_max_items_in_table = 0, - const hasher& hf = hasher(), - const key_equal& eql = key_equal(), - const allocator_type& alloc = allocator_type()) - : rep(expected_max_items_in_table, hf, eql, Identity(), SetKey(), alloc) { - } - - template - sparse_hash_set(InputIterator f, InputIterator l, - size_type expected_max_items_in_table = 0, - const hasher& hf = hasher(), - const key_equal& eql = key_equal(), - const allocator_type& alloc = allocator_type()) - : rep(expected_max_items_in_table, hf, eql, Identity(), SetKey(), alloc) { - rep.insert(f, l); - } - // We use the default copy constructor - // We use the default operator=() - // We use the default destructor - - void clear() { rep.clear(); } - void swap(sparse_hash_set& hs) { rep.swap(hs.rep); } - - - // Functions concerning size - size_type size() const { return rep.size(); } - size_type max_size() const { return rep.max_size(); } - bool empty() const { return rep.empty(); } - size_type bucket_count() const { return rep.bucket_count(); } - size_type max_bucket_count() const { return rep.max_bucket_count(); } - - // These are tr1 methods. bucket() is the bucket the key is or would be in. - size_type bucket_size(size_type i) const { return rep.bucket_size(i); } - size_type bucket(const key_type& key) const { return rep.bucket(key); } - float load_factor() const { - return size() * 1.0f / bucket_count(); - } - float max_load_factor() const { - float shrink, grow; - rep.get_resizing_parameters(&shrink, &grow); - return grow; - } - void max_load_factor(float new_grow) { - float shrink, grow; - rep.get_resizing_parameters(&shrink, &grow); - rep.set_resizing_parameters(shrink, new_grow); - } - // These aren't tr1 methods but perhaps ought to be. - float min_load_factor() const { - float shrink, grow; - rep.get_resizing_parameters(&shrink, &grow); - return shrink; - } - void min_load_factor(float new_shrink) { - float shrink, grow; - rep.get_resizing_parameters(&shrink, &grow); - rep.set_resizing_parameters(new_shrink, grow); - } - // Deprecated; use min_load_factor() or max_load_factor() instead. - void set_resizing_parameters(float shrink, float grow) { - rep.set_resizing_parameters(shrink, grow); - } - - void resize(size_type hint) { rep.resize(hint); } - void rehash(size_type hint) { resize(hint); } // the tr1 name - - // Lookup routines - iterator find(const key_type& key) const { return rep.find(key); } - - size_type count(const key_type& key) const { return rep.count(key); } - - pair equal_range(const key_type& key) const { - return rep.equal_range(key); - } - - // Insertion routines - pair insert(const value_type& obj) { - pair p = rep.insert(obj); - return pair(p.first, p.second); // const to non-const - } - template - void insert(InputIterator f, InputIterator l) { rep.insert(f, l); } - void insert(const_iterator f, const_iterator l) { rep.insert(f, l); } - // required for std::insert_iterator; the passed-in iterator is ignored - iterator insert(iterator, const value_type& obj) { return insert(obj).first; } - - - // Deletion routines - // THESE ARE NON-STANDARD! I make you specify an "impossible" key - // value to identify deleted buckets. You can change the key as - // time goes on, or get rid of it entirely to be insert-only. - void set_deleted_key(const key_type& key) { rep.set_deleted_key(key); } - void clear_deleted_key() { rep.clear_deleted_key(); } - key_type deleted_key() const { return rep.deleted_key(); } - - // These are standard - size_type erase(const key_type& key) { return rep.erase(key); } - void erase(iterator it) { rep.erase(it); } - void erase(iterator f, iterator l) { rep.erase(f, l); } - - - // Comparison - bool operator==(const sparse_hash_set& hs) const { return rep == hs.rep; } - bool operator!=(const sparse_hash_set& hs) const { return rep != hs.rep; } - - - // I/O -- this is an add-on for writing metainformation to disk - bool write_metadata(FILE *fp) { return rep.write_metadata(fp); } - bool read_metadata(FILE *fp) { return rep.read_metadata(fp); } - bool write_nopointer_data(FILE *fp) { return rep.write_nopointer_data(fp); } - bool read_nopointer_data(FILE *fp) { return rep.read_nopointer_data(fp); } -}; - -template -inline void swap(sparse_hash_set& hs1, - sparse_hash_set& hs2) { - hs1.swap(hs2); -} - -_END_GOOGLE_NAMESPACE_ - -#endif /* _SPARSE_HASH_SET_H_ */ diff --git a/tommyds/benchmark/lib/google/sparsehash/densehashtable.h b/tommyds/benchmark/lib/google/sparsehash/densehashtable.h deleted file mode 100644 index 35f0a47..0000000 --- a/tommyds/benchmark/lib/google/sparsehash/densehashtable.h +++ /dev/null @@ -1,1264 +0,0 @@ -// Copyright (c) 2005, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// 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. - -// --- -// Author: Craig Silverstein -// -// A dense hashtable is a particular implementation of -// a hashtable: one that is meant to minimize memory allocation. -// It does this by using an array to store all the data. We -// steal a value from the key space to indicate "empty" array -// elements (ie indices where no item lives) and another to indicate -// "deleted" elements. -// -// (Note it is possible to change the value of the delete key -// on the fly; you can even remove it, though after that point -// the hashtable is insert_only until you set it again. The empty -// value however can't be changed.) -// -// To minimize allocation and pointer overhead, we use internal -// probing, in which the hashtable is a single table, and collisions -// are resolved by trying to insert again in another bucket. The -// most cache-efficient internal probing schemes are linear probing -// (which suffers, alas, from clumping) and quadratic probing, which -// is what we implement by default. -// -// Type requirements: value_type is required to be Copy Constructible -// and Default Constructible. It is not required to be (and commonly -// isn't) Assignable. -// -// You probably shouldn't use this code directly. Use -// or instead. - -// You can change the following below: -// HT_OCCUPANCY_PCT -- how full before we double size -// HT_EMPTY_PCT -- how empty before we halve size -// HT_MIN_BUCKETS -- default smallest bucket size -// -// You can also change enlarge_factor (which defaults to -// HT_OCCUPANCY_PCT), and shrink_factor (which defaults to -// HT_EMPTY_PCT) with set_resizing_parameters(). -// -// How to decide what values to use? -// shrink_factor's default of .4 * OCCUPANCY_PCT, is probably good. -// HT_MIN_BUCKETS is probably unnecessary since you can specify -// (indirectly) the starting number of buckets at construct-time. -// For enlarge_factor, you can use this chart to try to trade-off -// expected lookup time to the space taken up. By default, this -// code uses quadratic probing, though you can change it to linear -// via _JUMP below if you really want to. -// -// From http://www.augustana.ca/~mohrj/courses/1999.fall/csc210/lecture_notes/hashing.html -// NUMBER OF PROBES / LOOKUP Successful Unsuccessful -// Quadratic collision resolution 1 - ln(1-L) - L/2 1/(1-L) - L - ln(1-L) -// Linear collision resolution [1+1/(1-L)]/2 [1+1/(1-L)2]/2 -// -// -- enlarge_factor -- 0.10 0.50 0.60 0.75 0.80 0.90 0.99 -// QUADRATIC COLLISION RES. -// probes/successful lookup 1.05 1.44 1.62 2.01 2.21 2.85 5.11 -// probes/unsuccessful lookup 1.11 2.19 2.82 4.64 5.81 11.4 103.6 -// LINEAR COLLISION RES. -// probes/successful lookup 1.06 1.5 1.75 2.5 3.0 5.5 50.5 -// probes/unsuccessful lookup 1.12 2.5 3.6 8.5 13.0 50.0 5000.0 - -#ifndef _DENSEHASHTABLE_H_ -#define _DENSEHASHTABLE_H_ - -// The probing method -// Linear probing -// #define JUMP_(key, num_probes) ( 1 ) -// Quadratic probing -#define JUMP_(key, num_probes) ( num_probes ) - - -#include -#include -#include -#include // for abort() -#include // For swap(), eg -#include // For length_error -#include // For cerr -#include // For uninitialized_fill, uninitialized_copy -#include // for pair<> -#include // for facts about iterator tags -#include // for numeric_limits<> -#include -#include -#include // for true_type, integral_constant, etc. - -_START_GOOGLE_NAMESPACE_ - -using STL_NAMESPACE::pair; - -// Hashtable class, used to implement the hashed associative containers -// hash_set and hash_map. - -// Value: what is stored in the table (each bucket is a Value). -// Key: something in a 1-to-1 correspondence to a Value, that can be used -// to search for a Value in the table (find() takes a Key). -// HashFcn: Takes a Key and returns an integer, the more unique the better. -// ExtractKey: given a Value, returns the unique Key associated with it. -// SetKey: given a Value* and a Key, modifies the value such that -// ExtractKey(value) == key. We guarantee this is only called -// with key == deleted_key or key == empty_key. -// EqualKey: Given two Keys, says whether they are the same (that is, -// if they are both associated with the same Value). -// Alloc: STL allocator to use to allocate memory. - -template -class dense_hashtable; - -template -struct dense_hashtable_iterator; - -template -struct dense_hashtable_const_iterator; - -// We're just an array, but we need to skip over empty and deleted elements -template -struct dense_hashtable_iterator { - private: - typedef typename A::template rebind::other value_alloc_type; - - public: - typedef dense_hashtable_iterator iterator; - typedef dense_hashtable_const_iterator const_iterator; - - typedef STL_NAMESPACE::forward_iterator_tag iterator_category; - typedef V value_type; - typedef typename value_alloc_type::difference_type difference_type; - typedef typename value_alloc_type::size_type size_type; - typedef typename value_alloc_type::reference reference; - typedef typename value_alloc_type::pointer pointer; - - // "Real" constructor and default constructor - dense_hashtable_iterator(const dense_hashtable *h, - pointer it, pointer it_end, bool advance) - : ht(h), pos(it), end(it_end) { - if (advance) advance_past_empty_and_deleted(); - } - dense_hashtable_iterator() { } - // The default destructor is fine; we don't define one - // The default operator= is fine; we don't define one - - // Happy dereferencer - reference operator*() const { return *pos; } - pointer operator->() const { return &(operator*()); } - - // Arithmetic. The only hard part is making sure that - // we're not on an empty or marked-deleted array element - void advance_past_empty_and_deleted() { - while ( pos != end && (ht->test_empty(*this) || ht->test_deleted(*this)) ) - ++pos; - } - iterator& operator++() { - assert(pos != end); ++pos; advance_past_empty_and_deleted(); return *this; - } - iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; } - - // Comparison. - bool operator==(const iterator& it) const { return pos == it.pos; } - bool operator!=(const iterator& it) const { return pos != it.pos; } - - - // The actual data - const dense_hashtable *ht; - pointer pos, end; -}; - - -// Now do it all again, but with const-ness! -template -struct dense_hashtable_const_iterator { - private: - typedef typename A::template rebind::other value_alloc_type; - - public: - typedef dense_hashtable_iterator iterator; - typedef dense_hashtable_const_iterator const_iterator; - - typedef STL_NAMESPACE::forward_iterator_tag iterator_category; - typedef V value_type; - typedef typename value_alloc_type::difference_type difference_type; - typedef typename value_alloc_type::size_type size_type; - typedef typename value_alloc_type::const_reference reference; - typedef typename value_alloc_type::const_pointer pointer; - - // "Real" constructor and default constructor - dense_hashtable_const_iterator( - const dense_hashtable *h, - pointer it, pointer it_end, bool advance) - : ht(h), pos(it), end(it_end) { - if (advance) advance_past_empty_and_deleted(); - } - dense_hashtable_const_iterator() - : ht(NULL), pos(pointer()), end(pointer()) { } - // This lets us convert regular iterators to const iterators - dense_hashtable_const_iterator(const iterator &it) - : ht(it.ht), pos(it.pos), end(it.end) { } - // The default destructor is fine; we don't define one - // The default operator= is fine; we don't define one - - // Happy dereferencer - reference operator*() const { return *pos; } - pointer operator->() const { return &(operator*()); } - - // Arithmetic. The only hard part is making sure that - // we're not on an empty or marked-deleted array element - void advance_past_empty_and_deleted() { - while ( pos != end && (ht->test_empty(*this) || ht->test_deleted(*this)) ) - ++pos; - } - const_iterator& operator++() { - assert(pos != end); ++pos; advance_past_empty_and_deleted(); return *this; - } - const_iterator operator++(int) { const_iterator tmp(*this); ++*this; return tmp; } - - // Comparison. - bool operator==(const const_iterator& it) const { return pos == it.pos; } - bool operator!=(const const_iterator& it) const { return pos != it.pos; } - - - // The actual data - const dense_hashtable *ht; - pointer pos, end; -}; - -template -class dense_hashtable { - private: - typedef typename Alloc::template rebind::other value_alloc_type; - - public: - typedef Key key_type; - typedef Value value_type; - typedef HashFcn hasher; - typedef EqualKey key_equal; - typedef Alloc allocator_type; - - typedef typename value_alloc_type::size_type size_type; - typedef typename value_alloc_type::difference_type difference_type; - typedef typename value_alloc_type::reference reference; - typedef typename value_alloc_type::const_reference const_reference; - typedef typename value_alloc_type::pointer pointer; - typedef typename value_alloc_type::const_pointer const_pointer; - typedef dense_hashtable_iterator - iterator; - - typedef dense_hashtable_const_iterator - const_iterator; - - // These come from tr1. For us they're the same as regular iterators. - typedef iterator local_iterator; - typedef const_iterator const_local_iterator; - - // How full we let the table get before we resize, by default. - // Knuth says .8 is good -- higher causes us to probe too much, - // though it saves memory. - static const int HT_OCCUPANCY_PCT; // = 50 (out of 100) - - // How empty we let the table get before we resize lower, by default. - // (0.0 means never resize lower.) - // It should be less than OCCUPANCY_PCT / 2 or we thrash resizing - static const int HT_EMPTY_PCT; // = 0.4 * HT_OCCUPANCY_PCT; - - // Minimum size we're willing to let hashtables be. - // Must be a power of two, and at least 4. - // Note, however, that for a given hashtable, the initial size is a - // function of the first constructor arg, and may be >HT_MIN_BUCKETS. - static const size_type HT_MIN_BUCKETS = 4; - - // By default, if you don't specify a hashtable size at - // construction-time, we use this size. Must be a power of two, and - // at least HT_MIN_BUCKETS. - static const size_type HT_DEFAULT_STARTING_BUCKETS = 32; - - // ITERATOR FUNCTIONS - iterator begin() { return iterator(this, table, - table + num_buckets, true); } - iterator end() { return iterator(this, table + num_buckets, - table + num_buckets, true); } - const_iterator begin() const { return const_iterator(this, table, - table+num_buckets,true);} - const_iterator end() const { return const_iterator(this, table + num_buckets, - table+num_buckets,true);} - - // These come from tr1 unordered_map. They iterate over 'bucket' n. - // We'll just consider bucket n to be the n-th element of the table. - local_iterator begin(size_type i) { - return local_iterator(this, table + i, table + i+1, false); - } - local_iterator end(size_type i) { - local_iterator it = begin(i); - if (!test_empty(i) && !test_deleted(i)) - ++it; - return it; - } - const_local_iterator begin(size_type i) const { - return const_local_iterator(this, table + i, table + i+1, false); - } - const_local_iterator end(size_type i) const { - const_local_iterator it = begin(i); - if (!test_empty(i) && !test_deleted(i)) - ++it; - return it; - } - - // ACCESSOR FUNCTIONS for the things we templatize on, basically - hasher hash_funct() const { return settings; } - key_equal key_eq() const { return key_info; } - allocator_type get_allocator() const { - return allocator_type(val_info); - } - - // Accessor function for statistics gathering. - int num_table_copies() const { return settings.num_ht_copies(); } - - private: - // Annoyingly, we can't copy values around, because they might have - // const components (they're probably pair). We use - // explicit destructor invocation and placement new to get around - // this. Arg. - void set_value(pointer dst, const_reference src) { - dst->~value_type(); // delete the old value, if any - new(dst) value_type(src); - } - - void destroy_buckets(size_type first, size_type last) { - for ( ; first != last; ++first) - table[first].~value_type(); - } - - // DELETE HELPER FUNCTIONS - // This lets the user describe a key that will indicate deleted - // table entries. This key should be an "impossible" entry -- - // if you try to insert it for real, you won't be able to retrieve it! - // (NB: while you pass in an entire value, only the key part is looked - // at. This is just because I don't know how to assign just a key.) - private: - void squash_deleted() { // gets rid of any deleted entries we have - if ( num_deleted ) { // get rid of deleted before writing - dense_hashtable tmp(*this); // copying will get rid of deleted - swap(tmp); // now we are tmp - } - assert(num_deleted == 0); - } - - bool test_deleted_key(const key_type& key) const { - // The num_deleted test is crucial for read(): after read(), the ht values - // are garbage, and we don't want to think some of them are deleted. - // Invariant: !use_deleted implies num_deleted is 0. - assert(settings.use_deleted() || num_deleted == 0); - return num_deleted > 0 && equals(key_info.delkey, key); - } - - public: - void set_deleted_key(const key_type &key) { - // the empty indicator (if specified) and the deleted indicator - // must be different - assert((!settings.use_empty() || !equals(key, get_key(val_info.emptyval))) - && "Passed the empty-key to set_deleted_key"); - // It's only safe to change what "deleted" means if we purge deleted guys - squash_deleted(); - settings.set_use_deleted(true); - key_info.delkey = key; - } - void clear_deleted_key() { - squash_deleted(); - settings.set_use_deleted(false); - } - key_type deleted_key() const { - assert(settings.use_deleted() - && "Must set deleted key before calling deleted_key"); - return key_info.delkey; - } - - // These are public so the iterators can use them - // True if the item at position bucknum is "deleted" marker - bool test_deleted(size_type bucknum) const { - return test_deleted_key(get_key(table[bucknum])); - } - bool test_deleted(const iterator &it) const { - return test_deleted_key(get_key(*it)); - } - bool test_deleted(const const_iterator &it) const { - return test_deleted_key(get_key(*it)); - } - - private: - // Set it so test_deleted is true. true if object didn't used to be deleted. - bool set_deleted(iterator &it) { - assert(settings.use_deleted()); - bool retval = !test_deleted(it); - // &* converts from iterator to value-type. - set_key(&(*it), key_info.delkey); - return retval; - } - // Set it so test_deleted is false. true if object used to be deleted. - bool clear_deleted(iterator &it) { - assert(settings.use_deleted()); - // Happens automatically when we assign something else in its place. - return test_deleted(it); - } - - // We also allow to set/clear the deleted bit on a const iterator. - // We allow a const_iterator for the same reason you can delete a - // const pointer: it's convenient, and semantically you can't use - // 'it' after it's been deleted anyway, so its const-ness doesn't - // really matter. - bool set_deleted(const_iterator &it) { - assert(settings.use_deleted()); - bool retval = !test_deleted(it); - set_key(const_cast(&(*it)), key_info.delkey); - return retval; - } - // Set it so test_deleted is false. true if object used to be deleted. - bool clear_deleted(const_iterator &it) { - assert(settings.use_deleted()); - return test_deleted(it); - } - - // EMPTY HELPER FUNCTIONS - // This lets the user describe a key that will indicate empty (unused) - // table entries. This key should be an "impossible" entry -- - // if you try to insert it for real, you won't be able to retrieve it! - // (NB: while you pass in an entire value, only the key part is looked - // at. This is just because I don't know how to assign just a key.) - public: - // These are public so the iterators can use them - // True if the item at position bucknum is "empty" marker - bool test_empty(size_type bucknum) const { - assert(settings.use_empty()); // we always need to know what's empty! - return equals(get_key(val_info.emptyval), get_key(table[bucknum])); - } - bool test_empty(const iterator &it) const { - assert(settings.use_empty()); // we always need to know what's empty! - return equals(get_key(val_info.emptyval), get_key(*it)); - } - bool test_empty(const const_iterator &it) const { - assert(settings.use_empty()); // we always need to know what's empty! - return equals(get_key(val_info.emptyval), get_key(*it)); - } - - private: - void fill_range_with_empty(pointer table_start, pointer table_end) { - STL_NAMESPACE::uninitialized_fill(table_start, table_end, val_info.emptyval); - } - - public: - // TODO(csilvers): change all callers of this to pass in a key instead, - // and take a const key_type instead of const value_type. - void set_empty_key(const_reference val) { - // Once you set the empty key, you can't change it - assert(!settings.use_empty() && "Calling set_empty_key multiple times"); - // The deleted indicator (if specified) and the empty indicator - // must be different. - assert((!settings.use_deleted() || !equals(get_key(val), key_info.delkey)) - && "Setting the empty key the same as the deleted key"); - settings.set_use_empty(true); - set_value(&val_info.emptyval, val); - - assert(!table); // must set before first use - // num_buckets was set in constructor even though table was NULL - table = val_info.allocate(num_buckets); - assert(table); - fill_range_with_empty(table, table + num_buckets); - } - // TODO(sjackman): return a key_type rather than a value_type - value_type empty_key() const { - assert(settings.use_empty()); - return val_info.emptyval; - } - - // FUNCTIONS CONCERNING SIZE - public: - size_type size() const { return num_elements - num_deleted; } - size_type max_size() const { return val_info.max_size(); } - bool empty() const { return size() == 0; } - size_type bucket_count() const { return num_buckets; } - size_type max_bucket_count() const { return max_size(); } - size_type nonempty_bucket_count() const { return num_elements; } - // These are tr1 methods. Their idea of 'bucket' doesn't map well to - // what we do. We just say every bucket has 0 or 1 items in it. - size_type bucket_size(size_type i) const { - return begin(i) == end(i) ? 0 : 1; - } - - private: - // Because of the above, size_type(-1) is never legal; use it for errors - static const size_type ILLEGAL_BUCKET = size_type(-1); - - // Used after a string of deletes. Returns true if we actually shrunk. - // TODO(csilvers): take a delta so we can take into account inserts - // done after shrinking. Maybe make part of the Settings class? - bool maybe_shrink() { - assert(num_elements >= num_deleted); - assert((bucket_count() & (bucket_count()-1)) == 0); // is a power of two - assert(bucket_count() >= HT_MIN_BUCKETS); - bool retval = false; - - // If you construct a hashtable with < HT_DEFAULT_STARTING_BUCKETS, - // we'll never shrink until you get relatively big, and we'll never - // shrink below HT_DEFAULT_STARTING_BUCKETS. Otherwise, something - // like "dense_hash_set x; x.insert(4); x.erase(4);" will - // shrink us down to HT_MIN_BUCKETS buckets, which is too small. - const size_type num_remain = num_elements - num_deleted; - const size_type shrink_threshold = settings.shrink_threshold(); - if (shrink_threshold > 0 && num_remain < shrink_threshold && - bucket_count() > HT_DEFAULT_STARTING_BUCKETS) { - const float shrink_factor = settings.shrink_factor(); - size_type sz = bucket_count() / 2; // find how much we should shrink - while (sz > HT_DEFAULT_STARTING_BUCKETS && - num_remain < sz * shrink_factor) { - sz /= 2; // stay a power of 2 - } - dense_hashtable tmp(*this, sz); // Do the actual resizing - swap(tmp); // now we are tmp - retval = true; - } - settings.set_consider_shrink(false); // because we just considered it - return retval; - } - - // We'll let you resize a hashtable -- though this makes us copy all! - // When you resize, you say, "make it big enough for this many more elements" - // Returns true if we actually resized, false if size was already ok. - bool resize_delta(size_type delta) { - bool did_resize = false; - if ( settings.consider_shrink() ) { // see if lots of deletes happened - if ( maybe_shrink() ) - did_resize = true; - } - if (num_elements >= (STL_NAMESPACE::numeric_limits::max)() - delta) - throw std::length_error("resize overflow"); - if ( bucket_count() >= HT_MIN_BUCKETS && - (num_elements + delta) <= settings.enlarge_threshold() ) - return did_resize; // we're ok as we are - - // Sometimes, we need to resize just to get rid of all the - // "deleted" buckets that are clogging up the hashtable. So when - // deciding whether to resize, count the deleted buckets (which - // are currently taking up room). But later, when we decide what - // size to resize to, *don't* count deleted buckets, since they - // get discarded during the resize. - const size_type needed_size = settings.min_buckets(num_elements + delta, 0); - if ( needed_size <= bucket_count() ) // we have enough buckets - return did_resize; - - size_type resize_to = - settings.min_buckets(num_elements - num_deleted + delta, bucket_count()); - - if (resize_to < needed_size && // may double resize_to - resize_to < (STL_NAMESPACE::numeric_limits::max)() / 2) { - // This situation means that we have enough deleted elements, - // that once we purge them, we won't actually have needed to - // grow. But we may want to grow anyway: if we just purge one - // element, say, we'll have to grow anyway next time we - // insert. Might as well grow now, since we're already going - // through the trouble of copying (in order to purge the - // deleted elements). - const size_type target = - static_cast(settings.shrink_size(resize_to*2)); - if (num_elements - num_deleted + delta >= target) { - // Good, we won't be below the shrink threshhold even if we double. - resize_to *= 2; - } - } - dense_hashtable tmp(*this, resize_to); - swap(tmp); // now we are tmp - return true; - } - - // We require table be not-NULL and empty before calling this. - void resize_table(size_type /*old_size*/, size_type new_size, - true_type) { - table = val_info.realloc_or_die(table, new_size); - } - - void resize_table(size_type old_size, size_type new_size, false_type) { - val_info.deallocate(table, old_size); - table = val_info.allocate(new_size); - } - - // Used to actually do the rehashing when we grow/shrink a hashtable - void copy_from(const dense_hashtable &ht, size_type min_buckets_wanted) { - clear_to_size(settings.min_buckets(ht.size(), min_buckets_wanted)); - - // We use a normal iterator to get non-deleted bcks from ht - // We could use insert() here, but since we know there are - // no duplicates and no deleted items, we can be more efficient - assert((bucket_count() & (bucket_count()-1)) == 0); // a power of two - for ( const_iterator it = ht.begin(); it != ht.end(); ++it ) { - size_type num_probes = 0; // how many times we've probed - size_type bucknum; - const size_type bucket_count_minus_one = bucket_count() - 1; - for (bucknum = hash(get_key(*it)) & bucket_count_minus_one; - !test_empty(bucknum); // not empty - bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one) { - ++num_probes; - assert(num_probes < bucket_count() - && "Hashtable is full: an error in key_equal<> or hash<>"); - } - set_value(&table[bucknum], *it); // copies the value to here - num_elements++; - } - settings.inc_num_ht_copies(); - } - - // Required by the spec for hashed associative container - public: - // Though the docs say this should be num_buckets, I think it's much - // more useful as num_elements. As a special feature, calling with - // req_elements==0 will cause us to shrink if we can, saving space. - void resize(size_type req_elements) { // resize to this or larger - if ( settings.consider_shrink() || req_elements == 0 ) - maybe_shrink(); - if ( req_elements > num_elements ) - resize_delta(req_elements - num_elements); - } - - // Get and change the value of shrink_factor and enlarge_factor. The - // description at the beginning of this file explains how to choose - // the values. Setting the shrink parameter to 0.0 ensures that the - // table never shrinks. - void get_resizing_parameters(float* shrink, float* grow) const { - *shrink = settings.shrink_factor(); - *grow = settings.enlarge_factor(); - } - void set_resizing_parameters(float shrink, float grow) { - settings.set_resizing_parameters(shrink, grow); - settings.reset_thresholds(bucket_count()); - } - - // CONSTRUCTORS -- as required by the specs, we take a size, - // but also let you specify a hashfunction, key comparator, - // and key extractor. We also define a copy constructor and =. - // DESTRUCTOR -- needs to free the table - explicit dense_hashtable(size_type expected_max_items_in_table = 0, - const HashFcn& hf = HashFcn(), - const EqualKey& eql = EqualKey(), - const ExtractKey& ext = ExtractKey(), - const SetKey& set = SetKey(), - const Alloc& alloc = Alloc()) - : settings(hf), - key_info(ext, set, eql), - num_deleted(0), - num_elements(0), - num_buckets(expected_max_items_in_table == 0 - ? HT_DEFAULT_STARTING_BUCKETS - : settings.min_buckets(expected_max_items_in_table, 0)), - val_info(alloc_impl(alloc)), - table(NULL) { - // table is NULL until emptyval is set. However, we set num_buckets - // here so we know how much space to allocate once emptyval is set - settings.reset_thresholds(bucket_count()); - } - - // As a convenience for resize(), we allow an optional second argument - // which lets you make this new hashtable a different size than ht - dense_hashtable(const dense_hashtable& ht, - size_type min_buckets_wanted = HT_DEFAULT_STARTING_BUCKETS) - : settings(ht.settings), - key_info(ht.key_info), - num_deleted(0), - num_elements(0), - num_buckets(0), - val_info(ht.val_info), - table(NULL) { - if (!ht.settings.use_empty()) { - // If use_empty isn't set, copy_from will crash, so we do our own copying. - assert(ht.empty()); - num_buckets = settings.min_buckets(ht.size(), min_buckets_wanted); - settings.reset_thresholds(bucket_count()); - return; - } - settings.reset_thresholds(bucket_count()); - copy_from(ht, min_buckets_wanted); // copy_from() ignores deleted entries - } - - dense_hashtable& operator= (const dense_hashtable& ht) { - if (&ht == this) return *this; // don't copy onto ourselves - if (!ht.settings.use_empty()) { - assert(ht.empty()); - dense_hashtable empty_table(ht); // empty table with ht's thresholds - this->swap(empty_table); - return *this; - } - settings = ht.settings; - key_info = ht.key_info; - set_value(&val_info.emptyval, ht.val_info.emptyval); - // copy_from() calls clear and sets num_deleted to 0 too - copy_from(ht, HT_MIN_BUCKETS); - // we purposefully don't copy the allocator, which may not be copyable - return *this; - } - - ~dense_hashtable() { - if (table) { - destroy_buckets(0, num_buckets); - val_info.deallocate(table, num_buckets); - } - } - - // Many STL algorithms use swap instead of copy constructors - void swap(dense_hashtable& ht) { - STL_NAMESPACE::swap(settings, ht.settings); - STL_NAMESPACE::swap(key_info, ht.key_info); - STL_NAMESPACE::swap(num_deleted, ht.num_deleted); - STL_NAMESPACE::swap(num_elements, ht.num_elements); - STL_NAMESPACE::swap(num_buckets, ht.num_buckets); - { value_type tmp; // for annoying reasons, swap() doesn't work - set_value(&tmp, val_info.emptyval); - set_value(&val_info.emptyval, ht.val_info.emptyval); - set_value(&ht.val_info.emptyval, tmp); - } - STL_NAMESPACE::swap(table, ht.table); - settings.reset_thresholds(bucket_count()); // this also resets consider_shrink - ht.settings.reset_thresholds(bucket_count()); - // we purposefully don't swap the allocator, which may not be swap-able - } - - private: - void clear_to_size(size_type new_num_buckets) { - if (!table) { - table = val_info.allocate(new_num_buckets); - } else { - destroy_buckets(0, num_buckets); - if (new_num_buckets != num_buckets) { // resize, if necessary - typedef integral_constant >::value> - realloc_ok; - resize_table(num_buckets, new_num_buckets, realloc_ok()); - } - } - assert(table); - fill_range_with_empty(table, table + new_num_buckets); - num_elements = 0; - num_deleted = 0; - num_buckets = new_num_buckets; // our new size - settings.reset_thresholds(bucket_count()); - } - - public: - // It's always nice to be able to clear a table without deallocating it - void clear() { - // If the table is already empty, and the number of buckets is - // already as we desire, there's nothing to do. - const size_type new_num_buckets = settings.min_buckets(0, 0); - if (num_elements == 0 && new_num_buckets == num_buckets) { - return; - } - clear_to_size(new_num_buckets); - } - - // Clear the table without resizing it. - // Mimicks the stl_hashtable's behaviour when clear()-ing in that it - // does not modify the bucket count - void clear_no_resize() { - if (num_elements > 0) { - assert(table); - destroy_buckets(0, num_buckets); - fill_range_with_empty(table, table + num_buckets); - } - // don't consider to shrink before another erase() - settings.reset_thresholds(bucket_count()); - num_elements = 0; - num_deleted = 0; - } - - // LOOKUP ROUTINES - private: - // Returns a pair of positions: 1st where the object is, 2nd where - // it would go if you wanted to insert it. 1st is ILLEGAL_BUCKET - // if object is not found; 2nd is ILLEGAL_BUCKET if it is. - // Note: because of deletions where-to-insert is not trivial: it's the - // first deleted bucket we see, as long as we don't find the key later - pair find_position(const key_type &key) const { - size_type num_probes = 0; // how many times we've probed - const size_type bucket_count_minus_one = bucket_count() - 1; - size_type bucknum = hash(key) & bucket_count_minus_one; - size_type insert_pos = ILLEGAL_BUCKET; // where we would insert - while ( 1 ) { // probe until something happens - if ( test_empty(bucknum) ) { // bucket is empty - if ( insert_pos == ILLEGAL_BUCKET ) // found no prior place to insert - return pair(ILLEGAL_BUCKET, bucknum); - else - return pair(ILLEGAL_BUCKET, insert_pos); - - } else if ( test_deleted(bucknum) ) {// keep searching, but mark to insert - if ( insert_pos == ILLEGAL_BUCKET ) - insert_pos = bucknum; - - } else if ( equals(key, get_key(table[bucknum])) ) { - return pair(bucknum, ILLEGAL_BUCKET); - } - ++num_probes; // we're doing another probe - bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one; - assert(num_probes < bucket_count() - && "Hashtable is full: an error in key_equal<> or hash<>"); - } - } - - public: - iterator find(const key_type& key) { - if ( size() == 0 ) return end(); - pair pos = find_position(key); - if ( pos.first == ILLEGAL_BUCKET ) // alas, not there - return end(); - else - return iterator(this, table + pos.first, table + num_buckets, false); - } - - const_iterator find(const key_type& key) const { - if ( size() == 0 ) return end(); - pair pos = find_position(key); - if ( pos.first == ILLEGAL_BUCKET ) // alas, not there - return end(); - else - return const_iterator(this, table + pos.first, table+num_buckets, false); - } - - // This is a tr1 method: the bucket a given key is in, or what bucket - // it would be put in, if it were to be inserted. Shrug. - size_type bucket(const key_type& key) const { - pair pos = find_position(key); - return pos.first == ILLEGAL_BUCKET ? pos.second : pos.first; - } - - // Counts how many elements have key key. For maps, it's either 0 or 1. - size_type count(const key_type &key) const { - pair pos = find_position(key); - return pos.first == ILLEGAL_BUCKET ? 0 : 1; - } - - // Likewise, equal_range doesn't really make sense for us. Oh well. - pair equal_range(const key_type& key) { - iterator pos = find(key); // either an iterator or end - if (pos == end()) { - return pair(pos, pos); - } else { - const iterator startpos = pos++; - return pair(startpos, pos); - } - } - pair equal_range(const key_type& key) const { - const_iterator pos = find(key); // either an iterator or end - if (pos == end()) { - return pair(pos, pos); - } else { - const const_iterator startpos = pos++; - return pair(startpos, pos); - } - } - - - // INSERTION ROUTINES - private: - // Private method used by insert_noresize and find_or_insert. - iterator insert_at(const_reference obj, size_type pos) { - if (size() >= max_size()) - throw std::length_error("insert overflow"); - if ( test_deleted(pos) ) { // just replace if it's been del. - // shrug: shouldn't need to be const. - const_iterator delpos(this, table + pos, table + num_buckets, false); - clear_deleted(delpos); - assert( num_deleted > 0); - --num_deleted; // used to be, now it isn't - } else { - ++num_elements; // replacing an empty bucket - } - set_value(&table[pos], obj); - return iterator(this, table + pos, table + num_buckets, false); - } - - // If you know *this is big enough to hold obj, use this routine - pair insert_noresize(const_reference obj) { - // First, double-check we're not inserting delkey or emptyval - assert((!settings.use_empty() || !equals(get_key(obj), - get_key(val_info.emptyval))) - && "Inserting the empty key"); - assert((!settings.use_deleted() || !equals(get_key(obj), key_info.delkey)) - && "Inserting the deleted key"); - const pair pos = find_position(get_key(obj)); - if ( pos.first != ILLEGAL_BUCKET) { // object was already there - return pair(iterator(this, table + pos.first, - table + num_buckets, false), - false); // false: we didn't insert - } else { // pos.second says where to put it - return pair(insert_at(obj, pos.second), true); - } - } - - // Specializations of insert(it, it) depending on the power of the iterator: - // (1) Iterator supports operator-, resize before inserting - template - void insert(ForwardIterator f, ForwardIterator l, STL_NAMESPACE::forward_iterator_tag) { - size_t dist = STL_NAMESPACE::distance(f, l); - if (dist >= (std::numeric_limits::max)()) - throw std::length_error("insert-range overflow"); - resize_delta(static_cast(dist)); - for ( ; dist > 0; --dist, ++f) { - insert_noresize(*f); - } - } - - // (2) Arbitrary iterator, can't tell how much to resize - template - void insert(InputIterator f, InputIterator l, STL_NAMESPACE::input_iterator_tag) { - for ( ; f != l; ++f) - insert(*f); - } - - public: - // This is the normal insert routine, used by the outside world - pair insert(const_reference obj) { - resize_delta(1); // adding an object, grow if need be - return insert_noresize(obj); - } - - // When inserting a lot at a time, we specialize on the type of iterator - template - void insert(InputIterator f, InputIterator l) { - // specializes on iterator type - insert(f, l, typename STL_NAMESPACE::iterator_traits::iterator_category()); - } - - // DefaultValue is a functor that takes a key and returns a value_type - // representing the default value to be inserted if none is found. - template - value_type& find_or_insert(const key_type& key) { - // First, double-check we're not inserting emptykey or delkey - assert((!settings.use_empty() || !equals(key, get_key(val_info.emptyval))) - && "Inserting the empty key"); - assert((!settings.use_deleted() || !equals(key, key_info.delkey)) - && "Inserting the deleted key"); - const pair pos = find_position(key); - DefaultValue default_value; - if ( pos.first != ILLEGAL_BUCKET) { // object was already there - return table[pos.first]; - } else if (resize_delta(1)) { // needed to rehash to make room - // Since we resized, we can't use pos, so recalculate where to insert. - return *insert_noresize(default_value(key)).first; - } else { // no need to rehash, insert right here - return *insert_at(default_value(key), pos.second); - } - } - - // DELETION ROUTINES - size_type erase(const key_type& key) { - // First, double-check we're not trying to erase delkey or emptyval. - assert((!settings.use_empty() || !equals(key, get_key(val_info.emptyval))) - && "Erasing the empty key"); - assert((!settings.use_deleted() || !equals(key, key_info.delkey)) - && "Erasing the deleted key"); - const_iterator pos = find(key); // shrug: shouldn't need to be const - if ( pos != end() ) { - assert(!test_deleted(pos)); // or find() shouldn't have returned it - set_deleted(pos); - ++num_deleted; - settings.set_consider_shrink(true); // will think about shrink after next insert - return 1; // because we deleted one thing - } else { - return 0; // because we deleted nothing - } - } - - // We return the iterator past the deleted item. - void erase(iterator pos) { - if ( pos == end() ) return; // sanity check - if ( set_deleted(pos) ) { // true if object has been newly deleted - ++num_deleted; - settings.set_consider_shrink(true); // will think about shrink after next insert - } - } - - void erase(iterator f, iterator l) { - for ( ; f != l; ++f) { - if ( set_deleted(f) ) // should always be true - ++num_deleted; - } - settings.set_consider_shrink(true); // will think about shrink after next insert - } - - // We allow you to erase a const_iterator just like we allow you to - // erase an iterator. This is in parallel to 'delete': you can delete - // a const pointer just like a non-const pointer. The logic is that - // you can't use the object after it's erased anyway, so it doesn't matter - // if it's const or not. - void erase(const_iterator pos) { - if ( pos == end() ) return; // sanity check - if ( set_deleted(pos) ) { // true if object has been newly deleted - ++num_deleted; - settings.set_consider_shrink(true); // will think about shrink after next insert - } - } - void erase(const_iterator f, const_iterator l) { - for ( ; f != l; ++f) { - if ( set_deleted(f) ) // should always be true - ++num_deleted; - } - settings.set_consider_shrink(true); // will think about shrink after next insert - } - - - // COMPARISON - bool operator==(const dense_hashtable& ht) const { - if (size() != ht.size()) { - return false; - } else if (this == &ht) { - return true; - } else { - // Iterate through the elements in "this" and see if the - // corresponding element is in ht - for ( const_iterator it = begin(); it != end(); ++it ) { - const_iterator it2 = ht.find(get_key(*it)); - if ((it2 == ht.end()) || (*it != *it2)) { - return false; - } - } - return true; - } - } - bool operator!=(const dense_hashtable& ht) const { - return !(*this == ht); - } - - - // I/O - // We support reading and writing hashtables to disk. Alas, since - // I don't know how to write a hasher or key_equal, you have to make - // sure everything but the table is the same. We compact before writing - // - // NOTE: These functions are currently TODO. They've not been implemented. - bool write_metadata(FILE * /*fp*/) { - squash_deleted(); // so we don't have to worry about delkey - return false; // TODO - } - - bool read_metadata(FILE* /*fp*/) { - num_deleted = 0; // since we got rid before writing - assert(settings.use_empty() && "empty_key not set for read_metadata"); - if (table) val_info.deallocate(table, num_buckets); // we'll make our own - // TODO: read magic number - // TODO: read num_buckets - settings.reset_thresholds(bucket_count()); - table = val_info.allocate(num_buckets); - assert(table); - fill_range_with_empty(table, table + num_buckets); - // TODO: read num_elements - for ( size_type i = 0; i < num_elements; ++i ) { - // TODO: read bucket_num - // TODO: set with non-empty, non-deleted value - } - return false; // TODO - } - - // If your keys and values are simple enough, we can write them to - // disk for you. "simple enough" means value_type is a POD type - // that contains no pointers. However, we don't try to normalize - // endianness - bool write_nopointer_data(FILE *fp) const { - for ( const_iterator it = begin(); it != end(); ++it ) { - // TODO: skip empty/deleted values - if ( !fwrite(&*it, sizeof(*it), 1, fp) ) return false; - } - return false; - } - - // When reading, we have to override the potential const-ness of *it - bool read_nopointer_data(FILE *fp) { - for ( iterator it = begin(); it != end(); ++it ) { - // TODO: skip empty/deleted values - if ( !fread(reinterpret_cast(&(*it)), sizeof(*it), 1, fp) ) - return false; - } - return false; - } - - private: - template - class alloc_impl : public A { - public: - typedef typename A::pointer pointer; - typedef typename A::size_type size_type; - - // Convert a normal allocator to one that has realloc_or_die() - alloc_impl(const A& a) : A(a) { } - - // realloc_or_die should only be used when using the default - // allocator (libc_allocator_with_realloc). - pointer realloc_or_die(pointer /*ptr*/, size_type /*n*/) { - fprintf(stderr, "realloc_or_die is only supported for " - "libc_allocator_with_realloc"); - exit(1); - return NULL; - } - }; - - // A template specialization of alloc_impl for - // libc_allocator_with_realloc that can handle realloc_or_die. - template - class alloc_impl > - : public libc_allocator_with_realloc { - public: - typedef typename libc_allocator_with_realloc::pointer pointer; - typedef typename libc_allocator_with_realloc::size_type size_type; - - alloc_impl(const libc_allocator_with_realloc& a) - : libc_allocator_with_realloc(a) { } - - pointer realloc_or_die(pointer ptr, size_type n) { - pointer retval = this->reallocate(ptr, n); - if (retval == NULL) { - // We really should use PRIuS here, but I don't want to have to add - // a whole new configure option, with concomitant macro namespace - // pollution, just to print this (unlikely) error message. So I cast. - fprintf(stderr, "sparsehash: FATAL ERROR: failed to reallocate " - "%lu elements for ptr %p", - static_cast(n), ptr); - exit(1); - } - return retval; - } - }; - - // Package allocator with emptyval to eliminate memory needed for - // the zero-size allocator. - // If new fields are added to this class, we should add them to - // operator= and swap. - class ValInfo : public alloc_impl { - public: - typedef typename alloc_impl::value_type value_type; - - ValInfo(const alloc_impl& a) - : alloc_impl(a), emptyval() { } - ValInfo(const ValInfo& v) - : alloc_impl(v), emptyval(v.emptyval) { } - - value_type emptyval; // which key marks unused entries - }; - - - // Package functors with another class to eliminate memory needed for - // zero-size functors. Since ExtractKey and hasher's operator() might - // have the same function signature, they must be packaged in - // different classes. - struct Settings : - sh_hashtable_settings { - explicit Settings(const hasher& hf) - : sh_hashtable_settings( - hf, HT_OCCUPANCY_PCT / 100.0f, HT_EMPTY_PCT / 100.0f) {} - }; - - // Packages ExtractKey and SetKey functors. - class KeyInfo : public ExtractKey, public SetKey, public key_equal { - public: - KeyInfo(const ExtractKey& ek, const SetKey& sk, const key_equal& eq) - : ExtractKey(ek), - SetKey(sk), - key_equal(eq) { - } - const key_type get_key(const_reference v) const { - return ExtractKey::operator()(v); - } - void set_key(pointer v, const key_type& k) const { - SetKey::operator()(v, k); - } - bool equals(const key_type& a, const key_type& b) const { - return key_equal::operator()(a, b); - } - - // Which key marks deleted entries. - // TODO(csilvers): make a pointer, and get rid of use_deleted (benchmark!) - typename remove_const::type delkey; - }; - - // Utility functions to access the templated operators - size_type hash(const key_type& v) const { - return settings.hash(v); - } - bool equals(const key_type& a, const key_type& b) const { - return key_info.equals(a, b); - } - const key_type get_key(const_reference v) const { - return key_info.get_key(v); - } - void set_key(pointer v, const key_type& k) const { - key_info.set_key(v, k); - } - - private: - // Actual data - Settings settings; - KeyInfo key_info; - - size_type num_deleted; // how many occupied buckets are marked deleted - size_type num_elements; - size_type num_buckets; - ValInfo val_info; // holds emptyval, and also the allocator - pointer table; -}; - - -// We need a global swap as well -template -inline void swap(dense_hashtable &x, - dense_hashtable &y) { - x.swap(y); -} - -#undef JUMP_ - -template -const typename dense_hashtable::size_type - dense_hashtable::ILLEGAL_BUCKET; - -// How full we let the table get before we resize. Knuth says .8 is -// good -- higher causes us to probe too much, though saves memory. -// However, we go with .5, getting better performance at the cost of -// more space (a trade-off densehashtable explicitly chooses to make). -// Feel free to play around with different values, though. -template -const int dense_hashtable::HT_OCCUPANCY_PCT = 50; - -// How empty we let the table get before we resize lower. -// It should be less than OCCUPANCY_PCT / 2 or we thrash resizing -template -const int dense_hashtable::HT_EMPTY_PCT - = static_cast(0.4 * - dense_hashtable::HT_OCCUPANCY_PCT); - -_END_GOOGLE_NAMESPACE_ - -#endif /* _DENSEHASHTABLE_H_ */ diff --git a/tommyds/benchmark/lib/google/sparsehash/hashtable-common.h b/tommyds/benchmark/lib/google/sparsehash/hashtable-common.h deleted file mode 100644 index e823b12..0000000 --- a/tommyds/benchmark/lib/google/sparsehash/hashtable-common.h +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright (c) 2005, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// 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. - -// --- -// Author: Giao Nguyen - -#ifndef UTIL_GTL_HASHTABLE_COMMON_H_ -#define UTIL_GTL_HASHTABLE_COMMON_H_ - -#include - -// Settings contains parameters for growing and shrinking the table. -// It also packages zero-size functor (ie. hasher). - -template -class sh_hashtable_settings : public HashFunc { - public: - typedef Key key_type; - typedef HashFunc hasher; - typedef SizeType size_type; - - public: - sh_hashtable_settings(const hasher& hf, - const float ht_occupancy_flt, - const float ht_empty_flt) - : hasher(hf), - enlarge_threshold_(0), - shrink_threshold_(0), - consider_shrink_(false), - use_empty_(false), - use_deleted_(false), - num_ht_copies_(0) { - set_enlarge_factor(ht_occupancy_flt); - set_shrink_factor(ht_empty_flt); - } - - size_type hash(const key_type& v) const { - return hasher::operator()(v); - } - - float enlarge_factor() const { - return enlarge_factor_; - } - void set_enlarge_factor(float f) { - enlarge_factor_ = f; - } - float shrink_factor() const { - return shrink_factor_; - } - void set_shrink_factor(float f) { - shrink_factor_ = f; - } - - size_type enlarge_threshold() const { - return enlarge_threshold_; - } - void set_enlarge_threshold(size_type t) { - enlarge_threshold_ = t; - } - size_type shrink_threshold() const { - return shrink_threshold_; - } - void set_shrink_threshold(size_type t) { - shrink_threshold_ = t; - } - - size_type enlarge_size(size_type x) const { - return static_cast(x * enlarge_factor_); - } - size_type shrink_size(size_type x) const { - return static_cast(x * shrink_factor_); - } - - bool consider_shrink() const { - return consider_shrink_; - } - void set_consider_shrink(bool t) { - consider_shrink_ = t; - } - - bool use_empty() const { - return use_empty_; - } - void set_use_empty(bool t) { - use_empty_ = t; - } - - bool use_deleted() const { - return use_deleted_; - } - void set_use_deleted(bool t) { - use_deleted_ = t; - } - - size_type num_ht_copies() const { - return static_cast(num_ht_copies_); - } - void inc_num_ht_copies() { - ++num_ht_copies_; - } - - // Reset the enlarge and shrink thresholds - void reset_thresholds(size_type num_buckets) { - set_enlarge_threshold(enlarge_size(num_buckets)); - set_shrink_threshold(shrink_size(num_buckets)); - // whatever caused us to reset already considered - set_consider_shrink(false); - } - - // Caller is resposible for calling reset_threshold right after - // set_resizing_parameters. - void set_resizing_parameters(float shrink, float grow) { - assert(shrink >= 0.0); - assert(grow <= 1.0); - if (shrink > grow/2.0f) - shrink = grow / 2.0f; // otherwise we thrash hashtable size - set_shrink_factor(shrink); - set_enlarge_factor(grow); - } - - // This is the smallest size a hashtable can be without being too crowded - // If you like, you can give a min #buckets as well as a min #elts - size_type min_buckets(size_type num_elts, size_type min_buckets_wanted) { - float enlarge = enlarge_factor(); - size_type sz = HT_MIN_BUCKETS; // min buckets allowed - while ( sz < min_buckets_wanted || - num_elts >= static_cast(sz * enlarge) ) { - // This just prevents overflowing size_type, since sz can exceed - // max_size() here. - if (static_cast(sz * 2) < sz) { - throw std::length_error("resize overflow"); // protect against overflow - } - sz *= 2; - } - return sz; - } - - private: - size_type enlarge_threshold_; // table.size() * enlarge_factor - size_type shrink_threshold_; // table.size() * shrink_factor - float enlarge_factor_; // how full before resize - float shrink_factor_; // how empty before resize - // consider_shrink=true if we should try to shrink before next insert - bool consider_shrink_; - bool use_empty_; // used only by densehashtable, not sparsehashtable - bool use_deleted_; // false until delkey has been set - // num_ht_copies is a counter incremented every Copy/Move - unsigned int num_ht_copies_; -}; - -#endif // UTIL_GTL_HASHTABLE_COMMON_H_ diff --git a/tommyds/benchmark/lib/google/sparsehash/libc_allocator_with_realloc.h b/tommyds/benchmark/lib/google/sparsehash/libc_allocator_with_realloc.h deleted file mode 100644 index 4ba1db4..0000000 --- a/tommyds/benchmark/lib/google/sparsehash/libc_allocator_with_realloc.h +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) 2010, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// 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. - -// --- -// Author: Guilin Chen - -#ifndef UTIL_GTL_LIBC_ALLOCATOR_WITH_REALLOC_H_ -#define UTIL_GTL_LIBC_ALLOCATOR_WITH_REALLOC_H_ - -#include - -#include // for malloc/realloc/free -#include // for ptrdiff_t - - -_START_GOOGLE_NAMESPACE_ - -template -class libc_allocator_with_realloc { - public: - typedef T value_type; - typedef size_t size_type; - typedef ptrdiff_t difference_type; - - typedef T* pointer; - typedef const T* const_pointer; - typedef T& reference; - typedef const T& const_reference; - - libc_allocator_with_realloc() {} - libc_allocator_with_realloc(const libc_allocator_with_realloc&) {} - ~libc_allocator_with_realloc() {} - - pointer address(reference r) const { return &r; } - const_pointer address(const_reference r) const { return &r; } - - pointer allocate(size_type n, const_pointer = 0) { - return static_cast(malloc(n * sizeof(value_type))); - } - void deallocate(pointer p, size_type) { - free(p); - } - pointer reallocate(pointer p, size_type n) { - return static_cast(realloc(p, n * sizeof(value_type))); - } - - size_type max_size() const { - return static_cast(-1) / sizeof(value_type); - } - - void construct(pointer p, const value_type& val) { - new(p) value_type(val); - } - void destroy(pointer p) { p->~value_type(); } - - template - libc_allocator_with_realloc(const libc_allocator_with_realloc&) {} - - template - struct rebind { - typedef libc_allocator_with_realloc other; - }; -}; - -// libc_allocator_with_realloc specialization. -template<> -class libc_allocator_with_realloc { - public: - typedef void value_type; - typedef size_t size_type; - typedef ptrdiff_t difference_type; - typedef void* pointer; - typedef const void* const_pointer; - - template - struct rebind { - typedef libc_allocator_with_realloc other; - }; -}; - -template -inline bool operator==(const libc_allocator_with_realloc&, - const libc_allocator_with_realloc&) { - return true; -} - -template -inline bool operator!=(const libc_allocator_with_realloc&, - const libc_allocator_with_realloc&) { - return false; -} - -_END_GOOGLE_NAMESPACE_ - -#endif // UTIL_GTL_LIBC_ALLOCATOR_WITH_REALLOC_H_ diff --git a/tommyds/benchmark/lib/google/sparsehash/sparseconfig.h b/tommyds/benchmark/lib/google/sparsehash/sparseconfig.h deleted file mode 100644 index 4a932ac..0000000 --- a/tommyds/benchmark/lib/google/sparsehash/sparseconfig.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef SPARSEHASH_WINDOWS_SPARSECONFIG_H_ -#define SPARSEHASH_WINDOWS_SPARSECONFIG_H_ - -/*** - *** These are #defines that autoheader puts in config.h.in that we - *** want to show up in sparseconfig.h, the minimal config.h file - *** #included by all our .h files. The reason we don't take - *** everything that autoheader emits is that we have to include a - *** config.h in installed header files, and we want to minimize the - *** number of #defines we make so as to not pollute the namespace. - ***/ -/* NOTE: these are for internal use only. Don't use these - * #defines in your own programs! - */ -#define GOOGLE_NAMESPACE ::google -#define HASH_NAMESPACE stdext -#define HASH_FUN_H -#define SPARSEHASH_HASH HASH_NAMESPACE::hash_compare -#undef HAVE_UINT16_T -#undef HAVE_U_INT16_T -#define HAVE___UINT16 1 -#define HAVE_LONG_LONG 1 -#define HAVE_SYS_TYPES_H 1 -#undef HAVE_STDINT_H -#undef HAVE_INTTYPES_H -#define HAVE_MEMCPY 1 -#define STL_NAMESPACE std -#define _END_GOOGLE_NAMESPACE_ } -#define _START_GOOGLE_NAMESPACE_ namespace google { - - -#endif /* SPARSEHASH_WINDOWS_SPARSECONFIG_H_ */ diff --git a/tommyds/benchmark/lib/google/sparsehash/sparsehashtable.h b/tommyds/benchmark/lib/google/sparsehash/sparsehashtable.h deleted file mode 100644 index b868894..0000000 --- a/tommyds/benchmark/lib/google/sparsehash/sparsehashtable.h +++ /dev/null @@ -1,1187 +0,0 @@ -// Copyright (c) 2005, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// 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. - -// --- -// Author: Craig Silverstein -// -// A sparse hashtable is a particular implementation of -// a hashtable: one that is meant to minimize memory use. -// It does this by using a *sparse table* (cf sparsetable.h), -// which uses between 1 and 2 bits to store empty buckets -// (we may need another bit for hashtables that support deletion). -// -// When empty buckets are so cheap, an appealing hashtable -// implementation is internal probing, in which the hashtable -// is a single table, and collisions are resolved by trying -// to insert again in another bucket. The most cache-efficient -// internal probing schemes are linear probing (which suffers, -// alas, from clumping) and quadratic probing, which is what -// we implement by default. -// -// Deleted buckets are a bit of a pain. We have to somehow mark -// deleted buckets (the probing must distinguish them from empty -// buckets). The most principled way is to have another bitmap, -// but that's annoying and takes up space. Instead we let the -// user specify an "impossible" key. We set deleted buckets -// to have the impossible key. -// -// Note it is possible to change the value of the delete key -// on the fly; you can even remove it, though after that point -// the hashtable is insert_only until you set it again. -// -// You probably shouldn't use this code directly. Use -// or instead. -// -// You can modify the following, below: -// HT_OCCUPANCY_PCT -- how full before we double size -// HT_EMPTY_PCT -- how empty before we halve size -// HT_MIN_BUCKETS -- smallest bucket size -// HT_DEFAULT_STARTING_BUCKETS -- default bucket size at construct-time -// -// You can also change enlarge_factor (which defaults to -// HT_OCCUPANCY_PCT), and shrink_factor (which defaults to -// HT_EMPTY_PCT) with set_resizing_parameters(). -// -// How to decide what values to use? -// shrink_factor's default of .4 * OCCUPANCY_PCT, is probably good. -// HT_MIN_BUCKETS is probably unnecessary since you can specify -// (indirectly) the starting number of buckets at construct-time. -// For enlarge_factor, you can use this chart to try to trade-off -// expected lookup time to the space taken up. By default, this -// code uses quadratic probing, though you can change it to linear -// via _JUMP below if you really want to. -// -// From http://www.augustana.ca/~mohrj/courses/1999.fall/csc210/lecture_notes/hashing.html -// NUMBER OF PROBES / LOOKUP Successful Unsuccessful -// Quadratic collision resolution 1 - ln(1-L) - L/2 1/(1-L) - L - ln(1-L) -// Linear collision resolution [1+1/(1-L)]/2 [1+1/(1-L)2]/2 -// -// -- enlarge_factor -- 0.10 0.50 0.60 0.75 0.80 0.90 0.99 -// QUADRATIC COLLISION RES. -// probes/successful lookup 1.05 1.44 1.62 2.01 2.21 2.85 5.11 -// probes/unsuccessful lookup 1.11 2.19 2.82 4.64 5.81 11.4 103.6 -// LINEAR COLLISION RES. -// probes/successful lookup 1.06 1.5 1.75 2.5 3.0 5.5 50.5 -// probes/unsuccessful lookup 1.12 2.5 3.6 8.5 13.0 50.0 5000.0 -// -// The value type is required to be copy constructible and default -// constructible, but it need not be (and commonly isn't) assignable. - -#ifndef _SPARSEHASHTABLE_H_ -#define _SPARSEHASHTABLE_H_ - -#ifndef SPARSEHASH_STAT_UPDATE -#define SPARSEHASH_STAT_UPDATE(x) ((void) 0) -#endif - -// The probing method -// Linear probing -// #define JUMP_(key, num_probes) ( 1 ) -// Quadratic probing -#define JUMP_(key, num_probes) ( num_probes ) - -#include -#include -#include // For swap(), eg -#include // For length_error -#include // for facts about iterator tags -#include // for numeric_limits<> -#include // for pair<> -#include -#include // Since that's basically what we are - -_START_GOOGLE_NAMESPACE_ - -using STL_NAMESPACE::pair; - -// The smaller this is, the faster lookup is (because the group bitmap is -// smaller) and the faster insert is, because there's less to move. -// On the other hand, there are more groups. Since group::size_type is -// a short, this number should be of the form 32*x + 16 to avoid waste. -static const u_int16_t DEFAULT_GROUP_SIZE = 48; // fits in 1.5 words - -// Hashtable class, used to implement the hashed associative containers -// hash_set and hash_map. -// -// Value: what is stored in the table (each bucket is a Value). -// Key: something in a 1-to-1 correspondence to a Value, that can be used -// to search for a Value in the table (find() takes a Key). -// HashFcn: Takes a Key and returns an integer, the more unique the better. -// ExtractKey: given a Value, returns the unique Key associated with it. -// SetKey: given a Value* and a Key, modifies the value such that -// ExtractKey(value) == key. We guarantee this is only called -// with key == deleted_key. -// EqualKey: Given two Keys, says whether they are the same (that is, -// if they are both associated with the same Value). -// Alloc: STL allocator to use to allocate memory. - -template -class sparse_hashtable; - -template -struct sparse_hashtable_iterator; - -template -struct sparse_hashtable_const_iterator; - -// As far as iterating, we're basically just a sparsetable -// that skips over deleted elements. -template -struct sparse_hashtable_iterator { - private: - typedef typename A::template rebind::other value_alloc_type; - - public: - typedef sparse_hashtable_iterator iterator; - typedef sparse_hashtable_const_iterator const_iterator; - typedef typename sparsetable::nonempty_iterator - st_iterator; - - typedef STL_NAMESPACE::forward_iterator_tag iterator_category; - typedef V value_type; - typedef typename value_alloc_type::difference_type difference_type; - typedef typename value_alloc_type::size_type size_type; - typedef typename value_alloc_type::reference reference; - typedef typename value_alloc_type::pointer pointer; - - // "Real" constructor and default constructor - sparse_hashtable_iterator(const sparse_hashtable *h, - st_iterator it, st_iterator it_end) - : ht(h), pos(it), end(it_end) { advance_past_deleted(); } - sparse_hashtable_iterator() { } // not ever used internally - // The default destructor is fine; we don't define one - // The default operator= is fine; we don't define one - - // Happy dereferencer - reference operator*() const { return *pos; } - pointer operator->() const { return &(operator*()); } - - // Arithmetic. The only hard part is making sure that - // we're not on a marked-deleted array element - void advance_past_deleted() { - while ( pos != end && ht->test_deleted(*this) ) - ++pos; - } - iterator& operator++() { - assert(pos != end); ++pos; advance_past_deleted(); return *this; - } - iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; } - - // Comparison. - bool operator==(const iterator& it) const { return pos == it.pos; } - bool operator!=(const iterator& it) const { return pos != it.pos; } - - - // The actual data - const sparse_hashtable *ht; - st_iterator pos, end; -}; - -// Now do it all again, but with const-ness! -template -struct sparse_hashtable_const_iterator { - private: - typedef typename A::template rebind::other value_alloc_type; - - public: - typedef sparse_hashtable_iterator iterator; - typedef sparse_hashtable_const_iterator const_iterator; - typedef typename sparsetable::const_nonempty_iterator - st_iterator; - - typedef STL_NAMESPACE::forward_iterator_tag iterator_category; - typedef V value_type; - typedef typename value_alloc_type::difference_type difference_type; - typedef typename value_alloc_type::size_type size_type; - typedef typename value_alloc_type::const_reference reference; - typedef typename value_alloc_type::const_pointer pointer; - - // "Real" constructor and default constructor - sparse_hashtable_const_iterator(const sparse_hashtable *h, - st_iterator it, st_iterator it_end) - : ht(h), pos(it), end(it_end) { advance_past_deleted(); } - // This lets us convert regular iterators to const iterators - sparse_hashtable_const_iterator() { } // never used internally - sparse_hashtable_const_iterator(const iterator &it) - : ht(it.ht), pos(it.pos), end(it.end) { } - // The default destructor is fine; we don't define one - // The default operator= is fine; we don't define one - - // Happy dereferencer - reference operator*() const { return *pos; } - pointer operator->() const { return &(operator*()); } - - // Arithmetic. The only hard part is making sure that - // we're not on a marked-deleted array element - void advance_past_deleted() { - while ( pos != end && ht->test_deleted(*this) ) - ++pos; - } - const_iterator& operator++() { - assert(pos != end); ++pos; advance_past_deleted(); return *this; - } - const_iterator operator++(int) { const_iterator tmp(*this); ++*this; return tmp; } - - // Comparison. - bool operator==(const const_iterator& it) const { return pos == it.pos; } - bool operator!=(const const_iterator& it) const { return pos != it.pos; } - - - // The actual data - const sparse_hashtable *ht; - st_iterator pos, end; -}; - -// And once again, but this time freeing up memory as we iterate -template -struct sparse_hashtable_destructive_iterator { - private: - typedef typename A::template rebind::other value_alloc_type; - - public: - typedef sparse_hashtable_destructive_iterator iterator; - typedef typename sparsetable::destructive_iterator - st_iterator; - - typedef STL_NAMESPACE::forward_iterator_tag iterator_category; - typedef V value_type; - typedef typename value_alloc_type::difference_type difference_type; - typedef typename value_alloc_type::size_type size_type; - typedef typename value_alloc_type::reference reference; - typedef typename value_alloc_type::pointer pointer; - - // "Real" constructor and default constructor - sparse_hashtable_destructive_iterator(const - sparse_hashtable *h, - st_iterator it, st_iterator it_end) - : ht(h), pos(it), end(it_end) { advance_past_deleted(); } - sparse_hashtable_destructive_iterator() { } // never used internally - // The default destructor is fine; we don't define one - // The default operator= is fine; we don't define one - - // Happy dereferencer - reference operator*() const { return *pos; } - pointer operator->() const { return &(operator*()); } - - // Arithmetic. The only hard part is making sure that - // we're not on a marked-deleted array element - void advance_past_deleted() { - while ( pos != end && ht->test_deleted(*this) ) - ++pos; - } - iterator& operator++() { - assert(pos != end); ++pos; advance_past_deleted(); return *this; - } - iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; } - - // Comparison. - bool operator==(const iterator& it) const { return pos == it.pos; } - bool operator!=(const iterator& it) const { return pos != it.pos; } - - - // The actual data - const sparse_hashtable *ht; - st_iterator pos, end; -}; - - -template -class sparse_hashtable { - private: - typedef typename Alloc::template rebind::other value_alloc_type; - - public: - typedef Key key_type; - typedef Value value_type; - typedef HashFcn hasher; - typedef EqualKey key_equal; - typedef Alloc allocator_type; - - typedef typename value_alloc_type::size_type size_type; - typedef typename value_alloc_type::difference_type difference_type; - typedef typename value_alloc_type::reference reference; - typedef typename value_alloc_type::const_reference const_reference; - typedef typename value_alloc_type::pointer pointer; - typedef typename value_alloc_type::const_pointer const_pointer; - typedef sparse_hashtable_iterator - iterator; - - typedef sparse_hashtable_const_iterator - const_iterator; - - typedef sparse_hashtable_destructive_iterator - destructive_iterator; - - // These come from tr1. For us they're the same as regular iterators. - typedef iterator local_iterator; - typedef const_iterator const_local_iterator; - - // How full we let the table get before we resize, by default. - // Knuth says .8 is good -- higher causes us to probe too much, - // though it saves memory. - static const int HT_OCCUPANCY_PCT; // = 80 (out of 100); - - // How empty we let the table get before we resize lower, by default. - // (0.0 means never resize lower.) - // It should be less than OCCUPANCY_PCT / 2 or we thrash resizing - static const int HT_EMPTY_PCT; // = 0.4 * HT_OCCUPANCY_PCT; - - // Minimum size we're willing to let hashtables be. - // Must be a power of two, and at least 4. - // Note, however, that for a given hashtable, the initial size is a - // function of the first constructor arg, and may be >HT_MIN_BUCKETS. - static const size_type HT_MIN_BUCKETS = 4; - - // By default, if you don't specify a hashtable size at - // construction-time, we use this size. Must be a power of two, and - // at least HT_MIN_BUCKETS. - static const size_type HT_DEFAULT_STARTING_BUCKETS = 32; - - // ITERATOR FUNCTIONS - iterator begin() { return iterator(this, table.nonempty_begin(), - table.nonempty_end()); } - iterator end() { return iterator(this, table.nonempty_end(), - table.nonempty_end()); } - const_iterator begin() const { return const_iterator(this, - table.nonempty_begin(), - table.nonempty_end()); } - const_iterator end() const { return const_iterator(this, - table.nonempty_end(), - table.nonempty_end()); } - - // These come from tr1 unordered_map. They iterate over 'bucket' n. - // For sparsehashtable, we could consider each 'group' to be a bucket, - // I guess, but I don't really see the point. We'll just consider - // bucket n to be the n-th element of the sparsetable, if it's occupied, - // or some empty element, otherwise. - local_iterator begin(size_type i) { - if (table.test(i)) - return local_iterator(this, table.get_iter(i), table.nonempty_end()); - else - return local_iterator(this, table.nonempty_end(), table.nonempty_end()); - } - local_iterator end(size_type i) { - local_iterator it = begin(i); - if (table.test(i) && !test_deleted(i)) - ++it; - return it; - } - const_local_iterator begin(size_type i) const { - if (table.test(i)) - return const_local_iterator(this, table.get_iter(i), - table.nonempty_end()); - else - return const_local_iterator(this, table.nonempty_end(), - table.nonempty_end()); - } - const_local_iterator end(size_type i) const { - const_local_iterator it = begin(i); - if (table.test(i) && !test_deleted(i)) - ++it; - return it; - } - - // This is used when resizing - destructive_iterator destructive_begin() { - return destructive_iterator(this, table.destructive_begin(), - table.destructive_end()); - } - destructive_iterator destructive_end() { - return destructive_iterator(this, table.destructive_end(), - table.destructive_end()); - } - - - // ACCESSOR FUNCTIONS for the things we templatize on, basically - hasher hash_funct() const { return settings; } - key_equal key_eq() const { return key_info; } - allocator_type get_allocator() const { return table.get_allocator(); } - - // Accessor function for statistics gathering. - int num_table_copies() const { return settings.num_ht_copies(); } - - private: - // We need to copy values when we set the special marker for deleted - // elements, but, annoyingly, we can't just use the copy assignment - // operator because value_type might not be assignable (it's often - // pair). We use explicit destructor invocation and - // placement new to get around this. Arg. - void set_value(pointer dst, const_reference src) { - dst->~value_type(); // delete the old value, if any - new(dst) value_type(src); - } - - // This is used as a tag for the copy constructor, saying to destroy its - // arg We have two ways of destructively copying: with potentially growing - // the hashtable as we copy, and without. To make sure the outside world - // can't do a destructive copy, we make the typename private. - enum MoveDontCopyT {MoveDontCopy, MoveDontGrow}; - - // DELETE HELPER FUNCTIONS - // This lets the user describe a key that will indicate deleted - // table entries. This key should be an "impossible" entry -- - // if you try to insert it for real, you won't be able to retrieve it! - // (NB: while you pass in an entire value, only the key part is looked - // at. This is just because I don't know how to assign just a key.) - private: - void squash_deleted() { // gets rid of any deleted entries we have - if ( num_deleted ) { // get rid of deleted before writing - sparse_hashtable tmp(MoveDontGrow, *this); - swap(tmp); // now we are tmp - } - assert(num_deleted == 0); - } - - bool test_deleted_key(const key_type& key) const { - // The num_deleted test is crucial for read(): after read(), the ht values - // are garbage, and we don't want to think some of them are deleted. - // Invariant: !use_deleted implies num_deleted is 0. - assert(settings.use_deleted() || num_deleted == 0); - return num_deleted > 0 && equals(key_info.delkey, key); - } - - public: - void set_deleted_key(const key_type &key) { - // It's only safe to change what "deleted" means if we purge deleted guys - squash_deleted(); - settings.set_use_deleted(true); - key_info.delkey = key; - } - void clear_deleted_key() { - squash_deleted(); - settings.set_use_deleted(false); - } - key_type deleted_key() const { - assert(settings.use_deleted() - && "Must set deleted key before calling deleted_key"); - return key_info.delkey; - } - - // These are public so the iterators can use them - // True if the item at position bucknum is "deleted" marker - bool test_deleted(size_type bucknum) const { - if (num_deleted == 0 || !table.test(bucknum)) return false; - return test_deleted_key(get_key(table.unsafe_get(bucknum))); - } - bool test_deleted(const iterator &it) const { - if (!settings.use_deleted()) return false; - return test_deleted_key(get_key(*it)); - } - bool test_deleted(const const_iterator &it) const { - if (!settings.use_deleted()) return false; - return test_deleted_key(get_key(*it)); - } - bool test_deleted(const destructive_iterator &it) const { - if (!settings.use_deleted()) return false; - return test_deleted_key(get_key(*it)); - } - - private: - // Set it so test_deleted is true. true if object didn't used to be deleted. - // TODO(csilvers): make these private (also in densehashtable.h) - bool set_deleted(iterator &it) { - assert(settings.use_deleted()); - bool retval = !test_deleted(it); - // &* converts from iterator to value-type. - set_key(&(*it), key_info.delkey); - return retval; - } - // Set it so test_deleted is false. true if object used to be deleted. - bool clear_deleted(iterator &it) { - assert(settings.use_deleted()); - // Happens automatically when we assign something else in its place. - return test_deleted(it); - } - - // We also allow to set/clear the deleted bit on a const iterator. - // We allow a const_iterator for the same reason you can delete a - // const pointer: it's convenient, and semantically you can't use - // 'it' after it's been deleted anyway, so its const-ness doesn't - // really matter. - bool set_deleted(const_iterator &it) { - assert(settings.use_deleted()); // bad if set_deleted_key() wasn't called - bool retval = !test_deleted(it); - set_key(const_cast(&(*it)), key_info.delkey); - return retval; - } - // Set it so test_deleted is false. true if object used to be deleted. - bool clear_deleted(const_iterator &it) { - assert(settings.use_deleted()); // bad if set_deleted_key() wasn't called - return test_deleted(it); - } - - // FUNCTIONS CONCERNING SIZE - public: - size_type size() const { return table.num_nonempty() - num_deleted; } - size_type max_size() const { return table.max_size(); } - bool empty() const { return size() == 0; } - size_type bucket_count() const { return table.size(); } - size_type max_bucket_count() const { return max_size(); } - // These are tr1 methods. Their idea of 'bucket' doesn't map well to - // what we do. We just say every bucket has 0 or 1 items in it. - size_type bucket_size(size_type i) const { - return begin(i) == end(i) ? 0 : 1; - } - - private: - // Because of the above, size_type(-1) is never legal; use it for errors - static const size_type ILLEGAL_BUCKET = size_type(-1); - - // Used after a string of deletes. Returns true if we actually shrunk. - // TODO(csilvers): take a delta so we can take into account inserts - // done after shrinking. Maybe make part of the Settings class? - bool maybe_shrink() { - assert(table.num_nonempty() >= num_deleted); - assert((bucket_count() & (bucket_count()-1)) == 0); // is a power of two - assert(bucket_count() >= HT_MIN_BUCKETS); - bool retval = false; - - // If you construct a hashtable with < HT_DEFAULT_STARTING_BUCKETS, - // we'll never shrink until you get relatively big, and we'll never - // shrink below HT_DEFAULT_STARTING_BUCKETS. Otherwise, something - // like "dense_hash_set x; x.insert(4); x.erase(4);" will - // shrink us down to HT_MIN_BUCKETS buckets, which is too small. - const size_type num_remain = table.num_nonempty() - num_deleted; - const size_type shrink_threshold = settings.shrink_threshold(); - if (shrink_threshold > 0 && num_remain < shrink_threshold && - bucket_count() > HT_DEFAULT_STARTING_BUCKETS) { - const float shrink_factor = settings.shrink_factor(); - size_type sz = bucket_count() / 2; // find how much we should shrink - while (sz > HT_DEFAULT_STARTING_BUCKETS && - num_remain < static_cast(sz * shrink_factor)) { - sz /= 2; // stay a power of 2 - } - sparse_hashtable tmp(MoveDontCopy, *this, sz); - swap(tmp); // now we are tmp - retval = true; - } - settings.set_consider_shrink(false); // because we just considered it - return retval; - } - - // We'll let you resize a hashtable -- though this makes us copy all! - // When you resize, you say, "make it big enough for this many more elements" - // Returns true if we actually resized, false if size was already ok. - bool resize_delta(size_type delta) { - bool did_resize = false; - if ( settings.consider_shrink() ) { // see if lots of deletes happened - if ( maybe_shrink() ) - did_resize = true; - } - if (table.num_nonempty() >= - (STL_NAMESPACE::numeric_limits::max)() - delta) - throw std::length_error("resize overflow"); - if ( bucket_count() >= HT_MIN_BUCKETS && - (table.num_nonempty() + delta) <= settings.enlarge_threshold() ) - return did_resize; // we're ok as we are - - // Sometimes, we need to resize just to get rid of all the - // "deleted" buckets that are clogging up the hashtable. So when - // deciding whether to resize, count the deleted buckets (which - // are currently taking up room). But later, when we decide what - // size to resize to, *don't* count deleted buckets, since they - // get discarded during the resize. - const size_type needed_size = - settings.min_buckets(table.num_nonempty() + delta, 0); - if ( needed_size <= bucket_count() ) // we have enough buckets - return did_resize; - - size_type resize_to = - settings.min_buckets(table.num_nonempty() - num_deleted + delta, - bucket_count()); - if (resize_to < needed_size && // may double resize_to - resize_to < (STL_NAMESPACE::numeric_limits::max)() / 2) { - // This situation means that we have enough deleted elements, - // that once we purge them, we won't actually have needed to - // grow. But we may want to grow anyway: if we just purge one - // element, say, we'll have to grow anyway next time we - // insert. Might as well grow now, since we're already going - // through the trouble of copying (in order to purge the - // deleted elements). - const size_type target = - static_cast(settings.shrink_size(resize_to*2)); - if (table.num_nonempty() - num_deleted + delta >= target) { - // Good, we won't be below the shrink threshhold even if we double. - resize_to *= 2; - } - } - - sparse_hashtable tmp(MoveDontCopy, *this, resize_to); - swap(tmp); // now we are tmp - return true; - } - - // Used to actually do the rehashing when we grow/shrink a hashtable - void copy_from(const sparse_hashtable &ht, size_type min_buckets_wanted) { - clear(); // clear table, set num_deleted to 0 - - // If we need to change the size of our table, do it now - const size_type resize_to = - settings.min_buckets(ht.size(), min_buckets_wanted); - if ( resize_to > bucket_count() ) { // we don't have enough buckets - table.resize(resize_to); // sets the number of buckets - settings.reset_thresholds(bucket_count()); - } - - // We use a normal iterator to get non-deleted bcks from ht - // We could use insert() here, but since we know there are - // no duplicates and no deleted items, we can be more efficient - assert((bucket_count() & (bucket_count()-1)) == 0); // a power of two - for ( const_iterator it = ht.begin(); it != ht.end(); ++it ) { - size_type num_probes = 0; // how many times we've probed - size_type bucknum; - const size_type bucket_count_minus_one = bucket_count() - 1; - for (bucknum = hash(get_key(*it)) & bucket_count_minus_one; - table.test(bucknum); // not empty - bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one) { - ++num_probes; - assert(num_probes < bucket_count() - && "Hashtable is full: an error in key_equal<> or hash<>"); - } - table.set(bucknum, *it); // copies the value to here - } - settings.inc_num_ht_copies(); - } - - // Implementation is like copy_from, but it destroys the table of the - // "from" guy by freeing sparsetable memory as we iterate. This is - // useful in resizing, since we're throwing away the "from" guy anyway. - void move_from(MoveDontCopyT mover, sparse_hashtable &ht, - size_type min_buckets_wanted) { - clear(); // clear table, set num_deleted to 0 - - // If we need to change the size of our table, do it now - size_type resize_to; - if ( mover == MoveDontGrow ) - resize_to = ht.bucket_count(); // keep same size as old ht - else // MoveDontCopy - resize_to = settings.min_buckets(ht.size(), min_buckets_wanted); - if ( resize_to > bucket_count() ) { // we don't have enough buckets - table.resize(resize_to); // sets the number of buckets - settings.reset_thresholds(bucket_count()); - } - - // We use a normal iterator to get non-deleted bcks from ht - // We could use insert() here, but since we know there are - // no duplicates and no deleted items, we can be more efficient - assert( (bucket_count() & (bucket_count()-1)) == 0); // a power of two - // THIS IS THE MAJOR LINE THAT DIFFERS FROM COPY_FROM(): - for ( destructive_iterator it = ht.destructive_begin(); - it != ht.destructive_end(); ++it ) { - size_type num_probes = 0; // how many times we've probed - size_type bucknum; - for ( bucknum = hash(get_key(*it)) & (bucket_count()-1); // h % buck_cnt - table.test(bucknum); // not empty - bucknum = (bucknum + JUMP_(key, num_probes)) & (bucket_count()-1) ) { - ++num_probes; - assert(num_probes < bucket_count() - && "Hashtable is full: an error in key_equal<> or hash<>"); - } - table.set(bucknum, *it); // copies the value to here - } - settings.inc_num_ht_copies(); - } - - - // Required by the spec for hashed associative container - public: - // Though the docs say this should be num_buckets, I think it's much - // more useful as num_elements. As a special feature, calling with - // req_elements==0 will cause us to shrink if we can, saving space. - void resize(size_type req_elements) { // resize to this or larger - if ( settings.consider_shrink() || req_elements == 0 ) - maybe_shrink(); - if ( req_elements > table.num_nonempty() ) // we only grow - resize_delta(req_elements - table.num_nonempty()); - } - - // Get and change the value of shrink_factor and enlarge_factor. The - // description at the beginning of this file explains how to choose - // the values. Setting the shrink parameter to 0.0 ensures that the - // table never shrinks. - void get_resizing_parameters(float* shrink, float* grow) const { - *shrink = settings.shrink_factor(); - *grow = settings.enlarge_factor(); - } - void set_resizing_parameters(float shrink, float grow) { - settings.set_resizing_parameters(shrink, grow); - settings.reset_thresholds(bucket_count()); - } - - // CONSTRUCTORS -- as required by the specs, we take a size, - // but also let you specify a hashfunction, key comparator, - // and key extractor. We also define a copy constructor and =. - // DESTRUCTOR -- the default is fine, surprisingly. - explicit sparse_hashtable(size_type expected_max_items_in_table = 0, - const HashFcn& hf = HashFcn(), - const EqualKey& eql = EqualKey(), - const ExtractKey& ext = ExtractKey(), - const SetKey& set = SetKey(), - const Alloc& alloc = Alloc()) - : settings(hf), - key_info(ext, set, eql), - num_deleted(0), - table((expected_max_items_in_table == 0 - ? HT_DEFAULT_STARTING_BUCKETS - : settings.min_buckets(expected_max_items_in_table, 0)), - alloc) { - settings.reset_thresholds(bucket_count()); - } - - // As a convenience for resize(), we allow an optional second argument - // which lets you make this new hashtable a different size than ht. - // We also provide a mechanism of saying you want to "move" the ht argument - // into us instead of copying. - sparse_hashtable(const sparse_hashtable& ht, - size_type min_buckets_wanted = HT_DEFAULT_STARTING_BUCKETS) - : settings(ht.settings), - key_info(ht.key_info), - num_deleted(0), - table(0, ht.get_allocator()) { - settings.reset_thresholds(bucket_count()); - copy_from(ht, min_buckets_wanted); // copy_from() ignores deleted entries - } - sparse_hashtable(MoveDontCopyT mover, sparse_hashtable& ht, - size_type min_buckets_wanted = HT_DEFAULT_STARTING_BUCKETS) - : settings(ht.settings), - key_info(ht.key_info), - num_deleted(0), - table(0, ht.get_allocator()) { - settings.reset_thresholds(bucket_count()); - move_from(mover, ht, min_buckets_wanted); // ignores deleted entries - } - - sparse_hashtable& operator= (const sparse_hashtable& ht) { - if (&ht == this) return *this; // don't copy onto ourselves - settings = ht.settings; - key_info = ht.key_info; - num_deleted = ht.num_deleted; - // copy_from() calls clear and sets num_deleted to 0 too - copy_from(ht, HT_MIN_BUCKETS); - // we purposefully don't copy the allocator, which may not be copyable - return *this; - } - - // Many STL algorithms use swap instead of copy constructors - void swap(sparse_hashtable& ht) { - STL_NAMESPACE::swap(settings, ht.settings); - STL_NAMESPACE::swap(key_info, ht.key_info); - STL_NAMESPACE::swap(num_deleted, ht.num_deleted); - table.swap(ht.table); - } - - // It's always nice to be able to clear a table without deallocating it - void clear() { - if (!empty() || (num_deleted != 0)) { - table.clear(); - } - settings.reset_thresholds(bucket_count()); - num_deleted = 0; - } - - // LOOKUP ROUTINES - private: - // Returns a pair of positions: 1st where the object is, 2nd where - // it would go if you wanted to insert it. 1st is ILLEGAL_BUCKET - // if object is not found; 2nd is ILLEGAL_BUCKET if it is. - // Note: because of deletions where-to-insert is not trivial: it's the - // first deleted bucket we see, as long as we don't find the key later - pair find_position(const key_type &key) const { - size_type num_probes = 0; // how many times we've probed - const size_type bucket_count_minus_one = bucket_count() - 1; - size_type bucknum = hash(key) & bucket_count_minus_one; - size_type insert_pos = ILLEGAL_BUCKET; // where we would insert - SPARSEHASH_STAT_UPDATE(total_lookups += 1); - while ( 1 ) { // probe until something happens - if ( !table.test(bucknum) ) { // bucket is empty - SPARSEHASH_STAT_UPDATE(total_probes += num_probes); - if ( insert_pos == ILLEGAL_BUCKET ) // found no prior place to insert - return pair(ILLEGAL_BUCKET, bucknum); - else - return pair(ILLEGAL_BUCKET, insert_pos); - - } else if ( test_deleted(bucknum) ) {// keep searching, but mark to insert - if ( insert_pos == ILLEGAL_BUCKET ) - insert_pos = bucknum; - - } else if ( equals(key, get_key(table.unsafe_get(bucknum))) ) { - SPARSEHASH_STAT_UPDATE(total_probes += num_probes); - return pair(bucknum, ILLEGAL_BUCKET); - } - ++num_probes; // we're doing another probe - bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one; - assert(num_probes < bucket_count() - && "Hashtable is full: an error in key_equal<> or hash<>"); - } - } - - public: - iterator find(const key_type& key) { - if ( size() == 0 ) return end(); - pair pos = find_position(key); - if ( pos.first == ILLEGAL_BUCKET ) // alas, not there - return end(); - else - return iterator(this, table.get_iter(pos.first), table.nonempty_end()); - } - - const_iterator find(const key_type& key) const { - if ( size() == 0 ) return end(); - pair pos = find_position(key); - if ( pos.first == ILLEGAL_BUCKET ) // alas, not there - return end(); - else - return const_iterator(this, - table.get_iter(pos.first), table.nonempty_end()); - } - - // This is a tr1 method: the bucket a given key is in, or what bucket - // it would be put in, if it were to be inserted. Shrug. - size_type bucket(const key_type& key) const { - pair pos = find_position(key); - return pos.first == ILLEGAL_BUCKET ? pos.second : pos.first; - } - - // Counts how many elements have key key. For maps, it's either 0 or 1. - size_type count(const key_type &key) const { - pair pos = find_position(key); - return pos.first == ILLEGAL_BUCKET ? 0 : 1; - } - - // Likewise, equal_range doesn't really make sense for us. Oh well. - pair equal_range(const key_type& key) { - iterator pos = find(key); // either an iterator or end - if (pos == end()) { - return pair(pos, pos); - } else { - const iterator startpos = pos++; - return pair(startpos, pos); - } - } - pair equal_range(const key_type& key) const { - const_iterator pos = find(key); // either an iterator or end - if (pos == end()) { - return pair(pos, pos); - } else { - const const_iterator startpos = pos++; - return pair(startpos, pos); - } - } - - - // INSERTION ROUTINES - private: - // Private method used by insert_noresize and find_or_insert. - iterator insert_at(const_reference obj, size_type pos) { - if (size() >= max_size()) - throw std::length_error("insert overflow"); - if ( test_deleted(pos) ) { // just replace if it's been deleted - // The set() below will undelete this object. We just worry about stats - assert(num_deleted > 0); - --num_deleted; // used to be, now it isn't - } - table.set(pos, obj); - return iterator(this, table.get_iter(pos), table.nonempty_end()); - } - - // If you know *this is big enough to hold obj, use this routine - pair insert_noresize(const_reference obj) { - // First, double-check we're not inserting delkey - assert((!settings.use_deleted() || !equals(get_key(obj), key_info.delkey)) - && "Inserting the deleted key"); - const pair pos = find_position(get_key(obj)); - if ( pos.first != ILLEGAL_BUCKET) { // object was already there - return pair(iterator(this, table.get_iter(pos.first), - table.nonempty_end()), - false); // false: we didn't insert - } else { // pos.second says where to put it - return pair(insert_at(obj, pos.second), true); - } - } - - // Specializations of insert(it, it) depending on the power of the iterator: - // (1) Iterator supports operator-, resize before inserting - template - void insert(ForwardIterator f, ForwardIterator l, STL_NAMESPACE::forward_iterator_tag) { - size_t dist = STL_NAMESPACE::distance(f, l); - if (dist >= (std::numeric_limits::max)()) - throw std::length_error("insert-range overflow"); - resize_delta(static_cast(dist)); - for ( ; dist > 0; --dist, ++f) { - insert_noresize(*f); - } - } - - // (2) Arbitrary iterator, can't tell how much to resize - template - void insert(InputIterator f, InputIterator l, STL_NAMESPACE::input_iterator_tag) { - for ( ; f != l; ++f) - insert(*f); - } - - public: - // This is the normal insert routine, used by the outside world - pair insert(const_reference obj) { - resize_delta(1); // adding an object, grow if need be - return insert_noresize(obj); - } - - // When inserting a lot at a time, we specialize on the type of iterator - template - void insert(InputIterator f, InputIterator l) { - // specializes on iterator type - insert(f, l, typename STL_NAMESPACE::iterator_traits::iterator_category()); - } - - // DefaultValue is a functor that takes a key and returns a value_type - // representing the default value to be inserted if none is found. - template - value_type& find_or_insert(const key_type& key) { - // First, double-check we're not inserting delkey - assert((!settings.use_deleted() || !equals(key, key_info.delkey)) - && "Inserting the deleted key"); - const pair pos = find_position(key); - DefaultValue default_value; - if ( pos.first != ILLEGAL_BUCKET) { // object was already there - return *table.get_iter(pos.first); - } else if (resize_delta(1)) { // needed to rehash to make room - // Since we resized, we can't use pos, so recalculate where to insert. - return *insert_noresize(default_value(key)).first; - } else { // no need to rehash, insert right here - return *insert_at(default_value(key), pos.second); - } - } - - // DELETION ROUTINES - size_type erase(const key_type& key) { - // First, double-check we're not erasing delkey. - assert((!settings.use_deleted() || !equals(key, key_info.delkey)) - && "Erasing the deleted key"); - assert(!settings.use_deleted() || !equals(key, key_info.delkey)); - const_iterator pos = find(key); // shrug: shouldn't need to be const - if ( pos != end() ) { - assert(!test_deleted(pos)); // or find() shouldn't have returned it - set_deleted(pos); - ++num_deleted; - // will think about shrink after next insert - settings.set_consider_shrink(true); - return 1; // because we deleted one thing - } else { - return 0; // because we deleted nothing - } - } - - // We return the iterator past the deleted item. - void erase(iterator pos) { - if ( pos == end() ) return; // sanity check - if ( set_deleted(pos) ) { // true if object has been newly deleted - ++num_deleted; - // will think about shrink after next insert - settings.set_consider_shrink(true); - } - } - - void erase(iterator f, iterator l) { - for ( ; f != l; ++f) { - if ( set_deleted(f) ) // should always be true - ++num_deleted; - } - // will think about shrink after next insert - settings.set_consider_shrink(true); - } - - // We allow you to erase a const_iterator just like we allow you to - // erase an iterator. This is in parallel to 'delete': you can delete - // a const pointer just like a non-const pointer. The logic is that - // you can't use the object after it's erased anyway, so it doesn't matter - // if it's const or not. - void erase(const_iterator pos) { - if ( pos == end() ) return; // sanity check - if ( set_deleted(pos) ) { // true if object has been newly deleted - ++num_deleted; - // will think about shrink after next insert - settings.set_consider_shrink(true); - } - } - void erase(const_iterator f, const_iterator l) { - for ( ; f != l; ++f) { - if ( set_deleted(f) ) // should always be true - ++num_deleted; - } - // will think about shrink after next insert - settings.set_consider_shrink(true); - } - - - // COMPARISON - bool operator==(const sparse_hashtable& ht) const { - if (size() != ht.size()) { - return false; - } else if (this == &ht) { - return true; - } else { - // Iterate through the elements in "this" and see if the - // corresponding element is in ht - for ( const_iterator it = begin(); it != end(); ++it ) { - const_iterator it2 = ht.find(get_key(*it)); - if ((it2 == ht.end()) || (*it != *it2)) { - return false; - } - } - return true; - } - } - bool operator!=(const sparse_hashtable& ht) const { - return !(*this == ht); - } - - - // I/O - // We support reading and writing hashtables to disk. NOTE that - // this only stores the hashtable metadata, not the stuff you've - // actually put in the hashtable! Alas, since I don't know how to - // write a hasher or key_equal, you have to make sure everything - // but the table is the same. We compact before writing. - bool write_metadata(FILE *fp) { - squash_deleted(); // so we don't have to worry about delkey - return table.write_metadata(fp); - } - - bool read_metadata(FILE *fp) { - num_deleted = 0; // since we got rid before writing - bool result = table.read_metadata(fp); - settings.reset_thresholds(bucket_count()); - return result; - } - - // Only meaningful if value_type is a POD. - bool write_nopointer_data(FILE *fp) { - return table.write_nopointer_data(fp); - } - - // Only meaningful if value_type is a POD. - bool read_nopointer_data(FILE *fp) { - return table.read_nopointer_data(fp); - } - - private: - // Table is the main storage class. - typedef sparsetable Table; - - // Package templated functors with the other types to eliminate memory - // needed for storing these zero-size operators. Since ExtractKey and - // hasher's operator() might have the same function signature, they - // must be packaged in different classes. - struct Settings : - sh_hashtable_settings { - explicit Settings(const hasher& hf) - : sh_hashtable_settings( - hf, HT_OCCUPANCY_PCT / 100.0f, HT_EMPTY_PCT / 100.0f) {} - }; - - // KeyInfo stores delete key and packages zero-size functors: - // ExtractKey and SetKey. - class KeyInfo : public ExtractKey, public SetKey, public key_equal { - public: - KeyInfo(const ExtractKey& ek, const SetKey& sk, const key_equal& eq) - : ExtractKey(ek), - SetKey(sk), - key_equal(eq) { - } - const key_type get_key(const_reference v) const { - return ExtractKey::operator()(v); - } - void set_key(pointer v, const key_type& k) const { - SetKey::operator()(v, k); - } - bool equals(const key_type& a, const key_type& b) const { - return key_equal::operator()(a, b); - } - - // Which key marks deleted entries. - // TODO(csilvers): make a pointer, and get rid of use_deleted (benchmark!) - typename remove_const::type delkey; - }; - - // Utility functions to access the templated operators - size_type hash(const key_type& v) const { - return settings.hash(v); - } - bool equals(const key_type& a, const key_type& b) const { - return key_info.equals(a, b); - } - const key_type get_key(const_reference v) const { - return key_info.get_key(v); - } - void set_key(pointer v, const key_type& k) const { - key_info.set_key(v, k); - } - - private: - // Actual data - Settings settings; - KeyInfo key_info; - size_type num_deleted; // how many occupied buckets are marked deleted - Table table; // holds num_buckets and num_elements too -}; - - -// We need a global swap as well -template -inline void swap(sparse_hashtable &x, - sparse_hashtable &y) { - x.swap(y); -} - -#undef JUMP_ - -template -const typename sparse_hashtable::size_type - sparse_hashtable::ILLEGAL_BUCKET; - -// How full we let the table get before we resize. Knuth says .8 is -// good -- higher causes us to probe too much, though saves memory -template -const int sparse_hashtable::HT_OCCUPANCY_PCT = 80; - -// How empty we let the table get before we resize lower. -// It should be less than OCCUPANCY_PCT / 2 or we thrash resizing -template -const int sparse_hashtable::HT_EMPTY_PCT - = static_cast(0.4 * - sparse_hashtable::HT_OCCUPANCY_PCT); - -_END_GOOGLE_NAMESPACE_ - -#endif /* _SPARSEHASHTABLE_H_ */ diff --git a/tommyds/benchmark/lib/google/sparsetable b/tommyds/benchmark/lib/google/sparsetable deleted file mode 100644 index aa4ccab..0000000 --- a/tommyds/benchmark/lib/google/sparsetable +++ /dev/null @@ -1,1598 +0,0 @@ -// Copyright (c) 2005, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// 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. - -// --- -// Author: Craig Silverstein -// -// A sparsetable is a random container that implements a sparse array, -// that is, an array that uses very little memory to store unassigned -// indices (in this case, between 1-2 bits per unassigned index). For -// instance, if you allocate an array of size 5 and assign a[2] = , then a[2] will take up a lot of memory but a[0], a[1], -// a[3], and a[4] will not. Array elements that have a value are -// called "assigned". Array elements that have no value yet, or have -// had their value cleared using erase() or clear(), are called -// "unassigned". -// -// Unassigned values seem to have the default value of T (see below). -// Nevertheless, there is a difference between an unassigned index and -// one explicitly assigned the value of T(). The latter is considered -// assigned. -// -// Access to an array element is constant time, as is insertion and -// deletion. Insertion and deletion may be fairly slow, however: -// because of this container's memory economy, each insert and delete -// causes a memory reallocation. -// -// See doc/sparsetable.html for information about how to use this class. - -#ifndef _SPARSETABLE_H_ -#define _SPARSETABLE_H_ - -#include -#include // for malloc/free -#include // to read/write tables -#ifdef HAVE_STDINT_H -#include // the normal place uint16_t is defined -#endif -#ifdef HAVE_SYS_TYPES_H -#include // the normal place u_int16_t is defined -#endif -#ifdef HAVE_INTTYPES_H -#include // a third place for uint16_t or u_int16_t -#endif -#include // for bounds checking -#include // to define reverse_iterator for me -#include // equal, lexicographical_compare, swap,... -#include // uninitialized_copy -#include // a sparsetable is a vector of groups -#include -#include // for true_type, integral_constant, etc. - -#if STDC_HEADERS -#include // for memcpy -#else -#if !HAVE_MEMCPY -#define memcpy(d, s, n) bcopy ((s), (d), (n)) -#endif -#endif - -#ifndef HAVE_U_INT16_T -# if defined HAVE_UINT16_T - typedef uint16_t u_int16_t; // true on solaris, possibly other C99 libc's -# elif defined HAVE___UINT16 - typedef __int16 int16_t; // true on vc++7 - typedef unsigned __int16 u_int16_t; -# else - // Cannot find a 16-bit integer type. Hoping for the best with "short"... - typedef short int int16_t; - typedef unsigned short int u_int16_t; -# endif -#endif - -_START_GOOGLE_NAMESPACE_ - -using STL_NAMESPACE::vector; -using STL_NAMESPACE::uninitialized_copy; - -// The smaller this is, the faster lookup is (because the group bitmap is -// smaller) and the faster insert is, because there's less to move. -// On the other hand, there are more groups. Since group::size_type is -// a short, this number should be of the form 32*x + 16 to avoid waste. -static const u_int16_t DEFAULT_SPARSEGROUP_SIZE = 48; // fits in 1.5 words - - -// A NOTE ON ASSIGNING: -// A sparse table does not actually allocate memory for entries -// that are not filled. Because of this, it becomes complicated -// to have a non-const iterator: we don't know, if the iterator points -// to a not-filled bucket, whether you plan to fill it with something -// or whether you plan to read its value (in which case you'll get -// the default bucket value). Therefore, while we can define const -// operations in a pretty 'normal' way, for non-const operations, we -// define something that returns a helper object with operator= and -// operator& that allocate a bucket lazily. We use this for table[] -// and also for regular table iterators. - -template -class table_element_adaptor { - public: - typedef typename tabletype::value_type value_type; - typedef typename tabletype::size_type size_type; - typedef typename tabletype::reference reference; - typedef typename tabletype::pointer pointer; - - table_element_adaptor(tabletype *tbl, size_type p) - : table(tbl), pos(p) { } - table_element_adaptor& operator= (const value_type &val) { - table->set(pos, val); - return *this; - } - operator value_type() { return table->get(pos); } // we look like a value - pointer operator& () { return &table->mutating_get(pos); } - - private: - tabletype* table; - size_type pos; -}; - -// Our iterator as simple as iterators can be: basically it's just -// the index into our table. Dereference, the only complicated -// thing, we punt to the table class. This just goes to show how -// much machinery STL requires to do even the most trivial tasks. -// -// By templatizing over tabletype, we have one iterator type which -// we can use for both sparsetables and sparsebins. In fact it -// works on any class that allows size() and operator[] (eg vector), -// as long as it does the standard STL typedefs too (eg value_type). - -template -class table_iterator { - public: - typedef table_iterator iterator; - - typedef STL_NAMESPACE::random_access_iterator_tag iterator_category; - typedef typename tabletype::value_type value_type; - typedef typename tabletype::difference_type difference_type; - typedef typename tabletype::size_type size_type; - typedef table_element_adaptor reference; - typedef table_element_adaptor* pointer; - - // The "real" constructor - table_iterator(tabletype *tbl, size_type p) - : table(tbl), pos(p) { } - // The default constructor, used when I define vars of type table::iterator - table_iterator() : table(NULL), pos(0) { } - // The copy constructor, for when I say table::iterator foo = tbl.begin() - // The default destructor is fine; we don't define one - // The default operator= is fine; we don't define one - - // The main thing our iterator does is dereference. If the table entry - // we point to is empty, we return the default value type. - // This is the big different function from the const iterator. - reference operator*() { - return table_element_adaptor(table, pos); - } - pointer operator->() { return &(operator*()); } - - // Helper function to assert things are ok; eg pos is still in range - void check() const { - assert(table); - assert(pos <= table->size()); - } - - // Arithmetic: we just do arithmetic on pos. We don't even need to - // do bounds checking, since STL doesn't consider that its job. :-) - iterator& operator+=(size_type t) { pos += t; check(); return *this; } - iterator& operator-=(size_type t) { pos -= t; check(); return *this; } - iterator& operator++() { ++pos; check(); return *this; } - iterator& operator--() { --pos; check(); return *this; } - iterator operator++(int) { iterator tmp(*this); // for x++ - ++pos; check(); return tmp; } - iterator operator--(int) { iterator tmp(*this); // for x-- - --pos; check(); return tmp; } - iterator operator+(difference_type i) const { iterator tmp(*this); - tmp += i; return tmp; } - iterator operator-(difference_type i) const { iterator tmp(*this); - tmp -= i; return tmp; } - difference_type operator-(iterator it) const { // for "x = it2 - it" - assert(table == it.table); - return pos - it.pos; - } - reference operator[](difference_type n) const { - return *(*this + n); // simple though not totally efficient - } - - // Comparisons. - bool operator==(const iterator& it) const { - return table == it.table && pos == it.pos; - } - bool operator<(const iterator& it) const { - assert(table == it.table); // life is bad bad bad otherwise - return pos < it.pos; - } - bool operator!=(const iterator& it) const { return !(*this == it); } - bool operator<=(const iterator& it) const { return !(it < *this); } - bool operator>(const iterator& it) const { return it < *this; } - bool operator>=(const iterator& it) const { return !(*this < it); } - - // Here's the info we actually need to be an iterator - tabletype *table; // so we can dereference and bounds-check - size_type pos; // index into the table -}; - -// support for "3 + iterator" has to be defined outside the class, alas -template -table_iterator operator+(typename table_iterator::difference_type i, - table_iterator it) { - return it + i; // so people can say it2 = 3 + it -} - -template -class const_table_iterator { - public: - typedef table_iterator iterator; - typedef const_table_iterator const_iterator; - - typedef STL_NAMESPACE::random_access_iterator_tag iterator_category; - typedef typename tabletype::value_type value_type; - typedef typename tabletype::difference_type difference_type; - typedef typename tabletype::size_type size_type; - typedef typename tabletype::const_reference reference; // we're const-only - typedef typename tabletype::const_pointer pointer; - - // The "real" constructor - const_table_iterator(const tabletype *tbl, size_type p) - : table(tbl), pos(p) { } - // The default constructor, used when I define vars of type table::iterator - const_table_iterator() : table(NULL), pos(0) { } - // The copy constructor, for when I say table::iterator foo = tbl.begin() - // Also converts normal iterators to const iterators - const_table_iterator(const iterator &from) - : table(from.table), pos(from.pos) { } - // The default destructor is fine; we don't define one - // The default operator= is fine; we don't define one - - // The main thing our iterator does is dereference. If the table entry - // we point to is empty, we return the default value type. - reference operator*() const { return (*table)[pos]; } - pointer operator->() const { return &(operator*()); } - - // Helper function to assert things are ok; eg pos is still in range - void check() const { - assert(table); - assert(pos <= table->size()); - } - - // Arithmetic: we just do arithmetic on pos. We don't even need to - // do bounds checking, since STL doesn't consider that its job. :-) - const_iterator& operator+=(size_type t) { pos += t; check(); return *this; } - const_iterator& operator-=(size_type t) { pos -= t; check(); return *this; } - const_iterator& operator++() { ++pos; check(); return *this; } - const_iterator& operator--() { --pos; check(); return *this; } - const_iterator operator++(int) { const_iterator tmp(*this); // for x++ - ++pos; check(); return tmp; } - const_iterator operator--(int) { const_iterator tmp(*this); // for x-- - --pos; check(); return tmp; } - const_iterator operator+(difference_type i) const { const_iterator tmp(*this); - tmp += i; return tmp; } - const_iterator operator-(difference_type i) const { const_iterator tmp(*this); - tmp -= i; return tmp; } - difference_type operator-(const_iterator it) const { // for "x = it2 - it" - assert(table == it.table); - return pos - it.pos; - } - reference operator[](difference_type n) const { - return *(*this + n); // simple though not totally efficient - } - - // Comparisons. - bool operator==(const const_iterator& it) const { - return table == it.table && pos == it.pos; - } - bool operator<(const const_iterator& it) const { - assert(table == it.table); // life is bad bad bad otherwise - return pos < it.pos; - } - bool operator!=(const const_iterator& it) const { return !(*this == it); } - bool operator<=(const const_iterator& it) const { return !(it < *this); } - bool operator>(const const_iterator& it) const { return it < *this; } - bool operator>=(const const_iterator& it) const { return !(*this < it); } - - // Here's the info we actually need to be an iterator - const tabletype *table; // so we can dereference and bounds-check - size_type pos; // index into the table -}; - -// support for "3 + iterator" has to be defined outside the class, alas -template -const_table_iterator operator+(typename - const_table_iterator::difference_type i, - const_table_iterator it) { - return it + i; // so people can say it2 = 3 + it -} - - -// --------------------------------------------------------------------------- - - -/* -// This is a 2-D iterator. You specify a begin and end over a list -// of *containers*. We iterate over each container by iterating over -// it. It's actually simple: -// VECTOR.begin() VECTOR[0].begin() --------> VECTOR[0].end() ---, -// | ________________________________________________/ -// | \_> VECTOR[1].begin() --------> VECTOR[1].end() -, -// | ___________________________________________________/ -// v \_> ...... -// VECTOR.end() -// -// It's impossible to do random access on one of these things in constant -// time, so it's just a bidirectional iterator. -// -// Unfortunately, because we need to use this for a non-empty iterator, -// we use nonempty_begin() and nonempty_end() instead of begin() and end() -// (though only going across, not down). -*/ - -#define TWOD_BEGIN_ nonempty_begin -#define TWOD_END_ nonempty_end -#define TWOD_ITER_ nonempty_iterator -#define TWOD_CONST_ITER_ const_nonempty_iterator - -template -class two_d_iterator { - public: - typedef two_d_iterator iterator; - - typedef STL_NAMESPACE::bidirectional_iterator_tag iterator_category; - // apparently some versions of VC++ have trouble with two ::'s in a typename - typedef typename containertype::value_type _tmp_vt; - typedef typename _tmp_vt::value_type value_type; - typedef typename _tmp_vt::difference_type difference_type; - typedef typename _tmp_vt::reference reference; - typedef typename _tmp_vt::pointer pointer; - - // The "real" constructor. begin and end specify how many rows we have - // (in the diagram above); we always iterate over each row completely. - two_d_iterator(typename containertype::iterator begin, - typename containertype::iterator end, - typename containertype::iterator curr) - : row_begin(begin), row_end(end), row_current(curr), col_current() { - if ( row_current != row_end ) { - col_current = row_current->TWOD_BEGIN_(); - advance_past_end(); // in case cur->begin() == cur->end() - } - } - // If you want to start at an arbitrary place, you can, I guess - two_d_iterator(typename containertype::iterator begin, - typename containertype::iterator end, - typename containertype::iterator curr, - typename containertype::value_type::TWOD_ITER_ col) - : row_begin(begin), row_end(end), row_current(curr), col_current(col) { - advance_past_end(); // in case cur->begin() == cur->end() - } - // The default constructor, used when I define vars of type table::iterator - two_d_iterator() : row_begin(), row_end(), row_current(), col_current() { } - // The default destructor is fine; we don't define one - // The default operator= is fine; we don't define one - - // Happy dereferencer - reference operator*() const { return *col_current; } - pointer operator->() const { return &(operator*()); } - - // Arithmetic: we just do arithmetic on pos. We don't even need to - // do bounds checking, since STL doesn't consider that its job. :-) - // NOTE: this is not amortized constant time! What do we do about it? - void advance_past_end() { // used when col_current points to end() - while ( col_current == row_current->TWOD_END_() ) { // end of current row - ++row_current; // go to beginning of next - if ( row_current != row_end ) // col is irrelevant at end - col_current = row_current->TWOD_BEGIN_(); - else - break; // don't go past row_end - } - } - - iterator& operator++() { - assert(row_current != row_end); // how to ++ from there? - ++col_current; - advance_past_end(); // in case col_current is at end() - return *this; - } - iterator& operator--() { - while ( row_current == row_end || - col_current == row_current->TWOD_BEGIN_() ) { - assert(row_current != row_begin); - --row_current; - col_current = row_current->TWOD_END_(); // this is 1 too far - } - --col_current; - return *this; - } - iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; } - iterator operator--(int) { iterator tmp(*this); --*this; return tmp; } - - - // Comparisons. - bool operator==(const iterator& it) const { - return ( row_begin == it.row_begin && - row_end == it.row_end && - row_current == it.row_current && - (row_current == row_end || col_current == it.col_current) ); - } - bool operator!=(const iterator& it) const { return !(*this == it); } - - - // Here's the info we actually need to be an iterator - // These need to be public so we convert from iterator to const_iterator - typename containertype::iterator row_begin, row_end, row_current; - typename containertype::value_type::TWOD_ITER_ col_current; -}; - -// The same thing again, but this time const. :-( -template -class const_two_d_iterator { - public: - typedef const_two_d_iterator iterator; - - typedef STL_NAMESPACE::bidirectional_iterator_tag iterator_category; - // apparently some versions of VC++ have trouble with two ::'s in a typename - typedef typename containertype::value_type _tmp_vt; - typedef typename _tmp_vt::value_type value_type; - typedef typename _tmp_vt::difference_type difference_type; - typedef typename _tmp_vt::const_reference reference; - typedef typename _tmp_vt::const_pointer pointer; - - const_two_d_iterator(typename containertype::const_iterator begin, - typename containertype::const_iterator end, - typename containertype::const_iterator curr) - : row_begin(begin), row_end(end), row_current(curr), col_current() { - if ( curr != end ) { - col_current = curr->TWOD_BEGIN_(); - advance_past_end(); // in case cur->begin() == cur->end() - } - } - const_two_d_iterator(typename containertype::const_iterator begin, - typename containertype::const_iterator end, - typename containertype::const_iterator curr, - typename containertype::value_type::TWOD_CONST_ITER_ col) - : row_begin(begin), row_end(end), row_current(curr), col_current(col) { - advance_past_end(); // in case cur->begin() == cur->end() - } - const_two_d_iterator() - : row_begin(), row_end(), row_current(), col_current() { - } - // Need this explicitly so we can convert normal iterators to const iterators - const_two_d_iterator(const two_d_iterator& it) : - row_begin(it.row_begin), row_end(it.row_end), row_current(it.row_current), - col_current(it.col_current) { } - - typename containertype::const_iterator row_begin, row_end, row_current; - typename containertype::value_type::TWOD_CONST_ITER_ col_current; - - - // EVERYTHING FROM HERE DOWN IS THE SAME AS THE NON-CONST ITERATOR - reference operator*() const { return *col_current; } - pointer operator->() const { return &(operator*()); } - - void advance_past_end() { // used when col_current points to end() - while ( col_current == row_current->TWOD_END_() ) { // end of current row - ++row_current; // go to beginning of next - if ( row_current != row_end ) // col is irrelevant at end - col_current = row_current->TWOD_BEGIN_(); - else - break; // don't go past row_end - } - } - iterator& operator++() { - assert(row_current != row_end); // how to ++ from there? - ++col_current; - advance_past_end(); // in case col_current is at end() - return *this; - } - iterator& operator--() { - while ( row_current == row_end || - col_current == row_current->TWOD_BEGIN_() ) { - assert(row_current != row_begin); - --row_current; - col_current = row_current->TWOD_END_(); // this is 1 too far - } - --col_current; - return *this; - } - iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; } - iterator operator--(int) { iterator tmp(*this); --*this; return tmp; } - - bool operator==(const iterator& it) const { - return ( row_begin == it.row_begin && - row_end == it.row_end && - row_current == it.row_current && - (row_current == row_end || col_current == it.col_current) ); - } - bool operator!=(const iterator& it) const { return !(*this == it); } -}; - -// We provide yet another version, to be as frugal with memory as -// possible. This one frees each block of memory as it finishes -// iterating over it. By the end, the entire table is freed. -// For understandable reasons, you can only iterate over it once, -// which is why it's an input iterator -template -class destructive_two_d_iterator { - public: - typedef destructive_two_d_iterator iterator; - - typedef STL_NAMESPACE::input_iterator_tag iterator_category; - // apparently some versions of VC++ have trouble with two ::'s in a typename - typedef typename containertype::value_type _tmp_vt; - typedef typename _tmp_vt::value_type value_type; - typedef typename _tmp_vt::difference_type difference_type; - typedef typename _tmp_vt::reference reference; - typedef typename _tmp_vt::pointer pointer; - - destructive_two_d_iterator(typename containertype::iterator begin, - typename containertype::iterator end, - typename containertype::iterator curr) - : row_begin(begin), row_end(end), row_current(curr), col_current() { - if ( curr != end ) { - col_current = curr->TWOD_BEGIN_(); - advance_past_end(); // in case cur->begin() == cur->end() - } - } - destructive_two_d_iterator(typename containertype::iterator begin, - typename containertype::iterator end, - typename containertype::iterator curr, - typename containertype::value_type::TWOD_ITER_ col) - : row_begin(begin), row_end(end), row_current(curr), col_current(col) { - advance_past_end(); // in case cur->begin() == cur->end() - } - destructive_two_d_iterator() - : row_begin(), row_end(), row_current(), col_current() { - } - - typename containertype::iterator row_begin, row_end, row_current; - typename containertype::value_type::TWOD_ITER_ col_current; - - // This is the part that destroys - void advance_past_end() { // used when col_current points to end() - while ( col_current == row_current->TWOD_END_() ) { // end of current row - row_current->clear(); // the destructive part - // It would be nice if we could decrement sparsetable->num_buckets here - ++row_current; // go to beginning of next - if ( row_current != row_end ) // col is irrelevant at end - col_current = row_current->TWOD_BEGIN_(); - else - break; // don't go past row_end - } - } - - // EVERYTHING FROM HERE DOWN IS THE SAME AS THE REGULAR ITERATOR - reference operator*() const { return *col_current; } - pointer operator->() const { return &(operator*()); } - - iterator& operator++() { - assert(row_current != row_end); // how to ++ from there? - ++col_current; - advance_past_end(); // in case col_current is at end() - return *this; - } - iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; } - - bool operator==(const iterator& it) const { - return ( row_begin == it.row_begin && - row_end == it.row_end && - row_current == it.row_current && - (row_current == row_end || col_current == it.col_current) ); - } - bool operator!=(const iterator& it) const { return !(*this == it); } -}; - -#undef TWOD_BEGIN_ -#undef TWOD_END_ -#undef TWOD_ITER_ -#undef TWOD_CONST_ITER_ - - - - -// SPARSE-TABLE -// ------------ -// The idea is that a table with (logically) t buckets is divided -// into t/M *groups* of M buckets each. (M is a constant set in -// GROUP_SIZE for efficiency.) Each group is stored sparsely. -// Thus, inserting into the table causes some array to grow, which is -// slow but still constant time. Lookup involves doing a -// logical-position-to-sparse-position lookup, which is also slow but -// constant time. The larger M is, the slower these operations are -// but the less overhead (slightly). -// -// To store the sparse array, we store a bitmap B, where B[i] = 1 iff -// bucket i is non-empty. Then to look up bucket i we really look up -// array[# of 1s before i in B]. This is constant time for fixed M. -// -// Terminology: the position of an item in the overall table (from -// 1 .. t) is called its "location." The logical position in a group -// (from 1 .. M ) is called its "position." The actual location in -// the array (from 1 .. # of non-empty buckets in the group) is -// called its "offset." - -// The weird mod in the offset is entirely to quiet compiler warnings -// as is the cast to int after doing the "x mod 256" -#define PUT_(take_from, offset) do { \ - if (putc(static_cast(((take_from) >> ((offset) % (sizeof(take_from)*8)))\ - % 256), fp) \ - == EOF) \ - return false; \ -} while (0) - -#define GET_(add_to, offset) do { \ - if ((x=getc(fp)) == EOF) \ - return false; \ - else \ - add_to |= (static_cast(x) << ((offset) % (sizeof(add_to)*8))); \ -} while (0) - -template -class sparsegroup { - private: - typedef typename Alloc::template rebind::other value_alloc_type; - - public: - // Basic types - typedef T value_type; - typedef Alloc allocator_type; - typedef typename value_alloc_type::reference reference; - typedef typename value_alloc_type::const_reference const_reference; - typedef typename value_alloc_type::pointer pointer; - typedef typename value_alloc_type::const_pointer const_pointer; - - typedef table_iterator > iterator; - typedef const_table_iterator > - const_iterator; - typedef table_element_adaptor > - element_adaptor; - typedef u_int16_t size_type; // max # of buckets - typedef int16_t difference_type; - typedef STL_NAMESPACE::reverse_iterator const_reverse_iterator; - typedef STL_NAMESPACE::reverse_iterator reverse_iterator; - - // These are our special iterators, that go over non-empty buckets in a - // group. These aren't const-only because you can change non-empty bcks. - typedef pointer nonempty_iterator; - typedef const_pointer const_nonempty_iterator; - typedef STL_NAMESPACE::reverse_iterator reverse_nonempty_iterator; - typedef STL_NAMESPACE::reverse_iterator const_reverse_nonempty_iterator; - - // Iterator functions - iterator begin() { return iterator(this, 0); } - const_iterator begin() const { return const_iterator(this, 0); } - iterator end() { return iterator(this, size()); } - const_iterator end() const { return const_iterator(this, size()); } - reverse_iterator rbegin() { return reverse_iterator(end()); } - const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } - reverse_iterator rend() { return reverse_iterator(begin()); } - const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } - - // We'll have versions for our special non-empty iterator too - nonempty_iterator nonempty_begin() { return group; } - const_nonempty_iterator nonempty_begin() const { return group; } - nonempty_iterator nonempty_end() { - return group + settings.num_buckets; - } - const_nonempty_iterator nonempty_end() const { - return group + settings.num_buckets; - } - reverse_nonempty_iterator nonempty_rbegin() { - return reverse_nonempty_iterator(nonempty_end()); - } - const_reverse_nonempty_iterator nonempty_rbegin() const { - return const_reverse_nonempty_iterator(nonempty_end()); - } - reverse_nonempty_iterator nonempty_rend() { - return reverse_nonempty_iterator(nonempty_begin()); - } - const_reverse_nonempty_iterator nonempty_rend() const { - return const_reverse_nonempty_iterator(nonempty_begin()); - } - - - // This gives us the "default" value to return for an empty bucket. - // We just use the default constructor on T, the template type - const_reference default_value() const { - static value_type defaultval = value_type(); - return defaultval; - } - - - private: - // We need to do all this bit manipulation, of course. ick - static size_type charbit(size_type i) { return i >> 3; } - static size_type modbit(size_type i) { return 1 << (i&7); } - int bmtest(size_type i) const { return bitmap[charbit(i)] & modbit(i); } - void bmset(size_type i) { bitmap[charbit(i)] |= modbit(i); } - void bmclear(size_type i) { bitmap[charbit(i)] &= ~modbit(i); } - - pointer allocate_group(size_type n) { - pointer retval = settings.allocate(n); - if (retval == NULL) { - // We really should use PRIuS here, but I don't want to have to add - // a whole new configure option, with concomitant macro namespace - // pollution, just to print this (unlikely) error message. So I cast. - fprintf(stderr, "sparsehash: FATAL ERROR: " - "failed to allocate %lu groups\n", - static_cast(n)); - exit(1); - } - return retval; - } - - void free_group() { - if (!group) return; - pointer end_it = group + settings.num_buckets; - for (pointer p = group; p != end_it; ++p) - p->~value_type(); - settings.deallocate(group, settings.num_buckets); - group = NULL; - } - - public: // get_iter() in sparsetable needs it - // We need a small function that tells us how many set bits there are - // in positions 0..i-1 of the bitmap. It uses a big table. - // We make it static so templates don't allocate lots of these tables. - // There are lots of ways to do this calculation (called 'popcount'). - // The 8-bit table lookup is one of the fastest, though this - // implementation suffers from not doing any loop unrolling. See, eg, - // http://www.dalkescientific.com/writings/diary/archive/2008/07/03/hakmem_and_other_popcounts.html - // http://gurmeetsingh.wordpress.com/2008/08/05/fast-bit-counting-routines/ - static size_type pos_to_offset(const unsigned char *bm, size_type pos) { - // We could make these ints. The tradeoff is size (eg does it overwhelm - // the cache?) vs efficiency in referencing sub-word-sized array elements - static const char bits_in[256] = { // # of bits set in one char - 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, - }; - size_type retval = 0; - - // [Note: condition pos > 8 is an optimization; convince yourself we - // give exactly the same result as if we had pos >= 8 here instead.] - for ( ; pos > 8; pos -= 8 ) // bm[0..pos/8-1] - retval += bits_in[*bm++]; // chars we want *all* bits in - return retval + bits_in[*bm & ((1 << pos)-1)]; // the char that includes pos - } - - size_type pos_to_offset(size_type pos) const { // not static but still const - return pos_to_offset(bitmap, pos); - } - - - public: - // Constructors -- default and copy -- and destructor - sparsegroup(allocator_type& a) : - group(0), settings(alloc_impl(a)) { - memset(bitmap, 0, sizeof(bitmap)); - } - sparsegroup(const sparsegroup& x) : group(0), settings(x.settings) { - if ( settings.num_buckets ) { - group = allocate_group(x.settings.num_buckets); - uninitialized_copy(x.group, x.group + x.settings.num_buckets, group); - } - memcpy(bitmap, x.bitmap, sizeof(bitmap)); - } - ~sparsegroup() { free_group(); } - - // Operator= is just like the copy constructor, I guess - // TODO(austern): Make this exception safe. Handle exceptions in value_type's - // copy constructor. - sparsegroup &operator=(const sparsegroup& x) { - if ( &x == this ) return *this; // x = x - if ( x.settings.num_buckets == 0 ) { - free_group(); - } else { - pointer p = allocate_group(x.settings.num_buckets); - uninitialized_copy(x.group, x.group + x.settings.num_buckets, p); - free_group(); - group = p; - } - memcpy(bitmap, x.bitmap, sizeof(bitmap)); - settings.num_buckets = x.settings.num_buckets; - return *this; - } - - // Many STL algorithms use swap instead of copy constructors - void swap(sparsegroup& x) { - STL_NAMESPACE::swap(group, x.group); - for ( int i = 0; i < sizeof(bitmap) / sizeof(*bitmap); ++i ) - STL_NAMESPACE::swap(bitmap[i], x.bitmap[i]); // swap not defined on arrays - STL_NAMESPACE::swap(settings.num_buckets, x.settings.num_buckets); - // we purposefully don't swap the allocator, which may not be swap-able - } - - // It's always nice to be able to clear a table without deallocating it - void clear() { - free_group(); - memset(bitmap, 0, sizeof(bitmap)); - settings.num_buckets = 0; - } - - // Functions that tell you about size. Alas, these aren't so useful - // because our table is always fixed size. - size_type size() const { return GROUP_SIZE; } - size_type max_size() const { return GROUP_SIZE; } - bool empty() const { return false; } - // We also may want to know how many *used* buckets there are - size_type num_nonempty() const { return settings.num_buckets; } - - - // get()/set() are explicitly const/non-const. You can use [] if - // you want something that can be either (potentially more expensive). - const_reference get(size_type i) const { - if ( bmtest(i) ) // bucket i is occupied - return group[pos_to_offset(bitmap, i)]; - else - return default_value(); // return the default reference - } - - // TODO(csilvers): make protected + friend - // This is used by sparse_hashtable to get an element from the table - // when we know it exists. - const_reference unsafe_get(size_type i) const { - assert(bmtest(i)); - return group[pos_to_offset(bitmap, i)]; - } - - // TODO(csilvers): make protected + friend - reference mutating_get(size_type i) { // fills bucket i before getting - if ( !bmtest(i) ) - set(i, default_value()); - return group[pos_to_offset(bitmap, i)]; - } - - // Syntactic sugar. It's easy to return a const reference. To - // return a non-const reference, we need to use the assigner adaptor. - const_reference operator[](size_type i) const { - return get(i); - } - - element_adaptor operator[](size_type i) { - return element_adaptor(this, i); - } - - private: - // Create space at group[offset], assuming value_type has trivial - // copy constructor and destructor, and the allocator_type is - // the default libc_allocator_with_alloc. (Really, we want it to have - // "trivial move", because that's what realloc and memmove both do. - // But there's no way to capture that using type_traits, so we - // pretend that move(x, y) is equivalent to "x.~T(); new(x) T(y);" - // which is pretty much correct, if a bit conservative.) - void set_aux(size_type offset, true_type) { - group = settings.realloc_or_die(group, settings.num_buckets+1); - // This is equivalent to memmove(), but faster on my Intel P4, - // at least with gcc4.1 -O2 / glibc 2.3.6. - for (size_type i = settings.num_buckets; i > offset; --i) - memcpy(group + i, group + i-1, sizeof(*group)); - } - - // Create space at group[offset], without special assumptions about value_type - // and allocator_type. - void set_aux(size_type offset, false_type) { - // This is valid because 0 <= offset <= num_buckets - pointer p = allocate_group(settings.num_buckets + 1); - uninitialized_copy(group, group + offset, p); - uninitialized_copy(group + offset, group + settings.num_buckets, - p + offset + 1); - free_group(); - group = p; - } - - public: - // This returns a reference to the inserted item (which is a copy of val). - // TODO(austern): Make this exception safe: handle exceptions from - // value_type's copy constructor. - reference set(size_type i, const_reference val) { - size_type offset = pos_to_offset(bitmap, i); // where we'll find (or insert) - if ( bmtest(i) ) { - // Delete the old value, which we're replacing with the new one - group[offset].~value_type(); - } else { - typedef integral_constant::value && - has_trivial_destructor::value && - is_same >::value)> - realloc_and_memmove_ok; // we pretend mv(x,y) == "x.~T(); new(x) T(y)" - set_aux(offset, realloc_and_memmove_ok()); - ++settings.num_buckets; - bmset(i); - } - // This does the actual inserting. Since we made the array using - // malloc, we use "placement new" to just call the constructor. - new(&group[offset]) value_type(val); - return group[offset]; - } - - // We let you see if a bucket is non-empty without retrieving it - bool test(size_type i) const { - return bmtest(i) != 0; - } - bool test(iterator pos) const { - return bmtest(pos.pos) != 0; - } - - private: - // Shrink the array, assuming value_type has trivial copy - // constructor and destructor, and the allocator_type is the default - // libc_allocator_with_alloc. (Really, we want it to have "trivial - // move", because that's what realloc and memmove both do. But - // there's no way to capture that using type_traits, so we pretend - // that move(x, y) is equivalent to ""x.~T(); new(x) T(y);" - // which is pretty much correct, if a bit conservative.) - void erase_aux(size_type offset, true_type) { - // This isn't technically necessary, since we know we have a - // trivial destructor, but is a cheap way to get a bit more safety. - group[offset].~value_type(); - // This is equivalent to memmove(), but faster on my Intel P4, - // at lesat with gcc4.1 -O2 / glibc 2.3.6. - assert(settings.num_buckets > 0); - for (size_type i = offset; i < settings.num_buckets-1; ++i) - memcpy(group + i, group + i+1, sizeof(*group)); // hopefully inlined! - group = settings.realloc_or_die(group, settings.num_buckets-1); - } - - // Shrink the array, without any special assumptions about value_type and - // allocator_type. - void erase_aux(size_type offset, false_type) { - // This is valid because 0 <= offset < num_buckets. Note the inequality. - pointer p = allocate_group(settings.num_buckets - 1); - uninitialized_copy(group, group + offset, p); - uninitialized_copy(group + offset + 1, group + settings.num_buckets, - p + offset); - free_group(); - group = p; - } - - public: - // This takes the specified elements out of the group. This is - // "undefining", rather than "clearing". - // TODO(austern): Make this exception safe: handle exceptions from - // value_type's copy constructor. - void erase(size_type i) { - if ( bmtest(i) ) { // trivial to erase empty bucket - size_type offset = pos_to_offset(bitmap,i); // where we'll find (or insert) - if ( settings.num_buckets == 1 ) { - free_group(); - group = NULL; - } else { - typedef integral_constant::value && - has_trivial_destructor::value && - is_same< - allocator_type, - libc_allocator_with_realloc >::value)> - realloc_and_memmove_ok; // pretend mv(x,y) == "x.~T(); new(x) T(y)" - erase_aux(offset, realloc_and_memmove_ok()); - } - --settings.num_buckets; - bmclear(i); - } - } - - void erase(iterator pos) { - erase(pos.pos); - } - - void erase(iterator start_it, iterator end_it) { - // This could be more efficient, but to do so we'd need to make - // bmclear() clear a range of indices. Doesn't seem worth it. - for ( ; start_it != end_it; ++start_it ) - erase(start_it); - } - - - // I/O - // We support reading and writing groups to disk. We don't store - // the actual array contents (which we don't know how to store), - // just the bitmap and size. Meant to be used with table I/O. - // Returns true if all was ok - bool write_metadata(FILE *fp) const { - // we explicitly set to u_int16_t - assert(sizeof(settings.num_buckets) == 2); - PUT_(settings.num_buckets, 8); - PUT_(settings.num_buckets, 0); - if ( !fwrite(bitmap, sizeof(bitmap), 1, fp) ) - return false; - return true; - } - - // Reading destroys the old group contents! Returns true if all was ok - bool read_metadata(FILE *fp) { - clear(); - - int x; // the GET_ macro requires an 'int x' to be defined - GET_(settings.num_buckets, 8); - GET_(settings.num_buckets, 0); - - if ( !fread(bitmap, sizeof(bitmap), 1, fp) ) return false; - - // We'll allocate the space, but we won't fill it: it will be - // left as uninitialized raw memory. - group = allocate_group(settings.num_buckets); - return true; - } - - // If your keys and values are simple enough, we can write them - // to disk for you. "simple enough" means POD and no pointers. - // However, we don't try to normalize endianness - bool write_nopointer_data(FILE *fp) const { - for ( const_nonempty_iterator it = nonempty_begin(); - it != nonempty_end(); ++it ) { - if ( !fwrite(&*it, sizeof(*it), 1, fp) ) return false; - } - return true; - } - - // When reading, we have to override the potential const-ness of *it. - // Again, only meaningful if value_type is a POD. - bool read_nopointer_data(FILE *fp) { - for ( nonempty_iterator it = nonempty_begin(); - it != nonempty_end(); ++it ) { - if ( !fread(reinterpret_cast(&(*it)), sizeof(*it), 1, fp) ) - return false; - } - return true; - } - - // Comparisons. Note the comparisons are pretty arbitrary: we - // compare values of the first index that isn't equal (using default - // value for empty buckets). - bool operator==(const sparsegroup& x) const { - return ( settings.num_buckets == x.settings.num_buckets && - memcmp(bitmap, x.bitmap, sizeof(bitmap)) == 0 && - STL_NAMESPACE::equal(begin(), end(), x.begin()) ); // from algorithm - } - bool operator<(const sparsegroup& x) const { // also from algorithm - return STL_NAMESPACE::lexicographical_compare(begin(), end(), - x.begin(), x.end()); - } - bool operator!=(const sparsegroup& x) const { return !(*this == x); } - bool operator<=(const sparsegroup& x) const { return !(x < *this); } - bool operator>(const sparsegroup& x) const { return x < *this; } - bool operator>=(const sparsegroup& x) const { return !(*this < x); } - - private: - template - class alloc_impl : public A { - public: - typedef typename A::pointer pointer; - typedef typename A::size_type size_type; - - // Convert a normal allocator to one that has realloc_or_die() - alloc_impl(const A& a) : A(a) { } - - // realloc_or_die should only be used when using the default - // allocator (libc_allocator_with_realloc). - pointer realloc_or_die(pointer /*ptr*/, size_type /*n*/) { - fprintf(stderr, "realloc_or_die is only supported for " - "libc_allocator_with_realloc"); - exit(1); - return NULL; - } - }; - - // A template specialization of alloc_impl for - // libc_allocator_with_realloc that can handle realloc_or_die. - template - class alloc_impl > - : public libc_allocator_with_realloc { - public: - typedef typename libc_allocator_with_realloc::pointer pointer; - typedef typename libc_allocator_with_realloc::size_type size_type; - - alloc_impl(const libc_allocator_with_realloc& a) - : libc_allocator_with_realloc(a) { } - - pointer realloc_or_die(pointer ptr, size_type n) { - pointer retval = this->reallocate(ptr, n); - if (retval == NULL) { - // We really should use PRIuS here, but I don't want to have to add - // a whole new configure option, with concomitant macro namespace - // pollution, just to print this (unlikely) error message. So I cast. - fprintf(stderr, "sparsehash: FATAL ERROR: failed to reallocate " - "%lu elements for ptr %p", - static_cast(n), ptr); - exit(1); - } - return retval; - } - }; - - // Package allocator with num_buckets to eliminate memory needed for the - // zero-size allocator. - // If new fields are added to this class, we should add them to - // operator= and swap. - class Settings : public alloc_impl { - public: - Settings(const alloc_impl& a, u_int16_t n = 0) - : alloc_impl(a), num_buckets(n) { } - Settings(const Settings& s) - : alloc_impl(s), num_buckets(s.num_buckets) { } - - u_int16_t num_buckets; // limits GROUP_SIZE to 64K - }; - - // The actual data - pointer group; // (small) array of T's - Settings settings; // allocator and num_buckets - unsigned char bitmap[(GROUP_SIZE-1)/8 + 1]; // fancy math is so we round up -}; - -// We need a global swap as well -template -inline void swap(sparsegroup &x, - sparsegroup &y) { - x.swap(y); -} - -// --------------------------------------------------------------------------- - - -template > -class sparsetable { - private: - typedef typename Alloc::template rebind::other value_alloc_type; - typedef typename Alloc::template rebind< - sparsegroup >::other vector_alloc; - - public: - // Basic types - typedef T value_type; // stolen from stl_vector.h - typedef Alloc allocator_type; - typedef typename value_alloc_type::size_type size_type; - typedef typename value_alloc_type::difference_type difference_type; - typedef typename value_alloc_type::reference reference; - typedef typename value_alloc_type::const_reference const_reference; - typedef typename value_alloc_type::pointer pointer; - typedef typename value_alloc_type::const_pointer const_pointer; - typedef table_iterator > iterator; - typedef const_table_iterator > - const_iterator; - typedef table_element_adaptor > - element_adaptor; - typedef STL_NAMESPACE::reverse_iterator const_reverse_iterator; - typedef STL_NAMESPACE::reverse_iterator reverse_iterator; - - // These are our special iterators, that go over non-empty buckets in a - // table. These aren't const only because you can change non-empty bcks. - typedef two_d_iterator< vector< sparsegroup, - vector_alloc> > - nonempty_iterator; - typedef const_two_d_iterator< vector< sparsegroup, - vector_alloc> > - const_nonempty_iterator; - typedef STL_NAMESPACE::reverse_iterator reverse_nonempty_iterator; - typedef STL_NAMESPACE::reverse_iterator const_reverse_nonempty_iterator; - // Another special iterator: it frees memory as it iterates (used to resize) - typedef destructive_two_d_iterator< vector< sparsegroup, - vector_alloc> > - destructive_iterator; - - // Iterator functions - iterator begin() { return iterator(this, 0); } - const_iterator begin() const { return const_iterator(this, 0); } - iterator end() { return iterator(this, size()); } - const_iterator end() const { return const_iterator(this, size()); } - reverse_iterator rbegin() { return reverse_iterator(end()); } - const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } - reverse_iterator rend() { return reverse_iterator(begin()); } - const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } - - // Versions for our special non-empty iterator - nonempty_iterator nonempty_begin() { - return nonempty_iterator(groups.begin(), groups.end(), groups.begin()); - } - const_nonempty_iterator nonempty_begin() const { - return const_nonempty_iterator(groups.begin(),groups.end(), groups.begin()); - } - nonempty_iterator nonempty_end() { - return nonempty_iterator(groups.begin(), groups.end(), groups.end()); - } - const_nonempty_iterator nonempty_end() const { - return const_nonempty_iterator(groups.begin(), groups.end(), groups.end()); - } - reverse_nonempty_iterator nonempty_rbegin() { - return reverse_nonempty_iterator(nonempty_end()); - } - const_reverse_nonempty_iterator nonempty_rbegin() const { - return const_reverse_nonempty_iterator(nonempty_end()); - } - reverse_nonempty_iterator nonempty_rend() { - return reverse_nonempty_iterator(nonempty_begin()); - } - const_reverse_nonempty_iterator nonempty_rend() const { - return const_reverse_nonempty_iterator(nonempty_begin()); - } - destructive_iterator destructive_begin() { - return destructive_iterator(groups.begin(), groups.end(), groups.begin()); - } - destructive_iterator destructive_end() { - return destructive_iterator(groups.begin(), groups.end(), groups.end()); - } - - typedef sparsegroup group_type; - typedef vector group_vector_type; - - typedef typename group_vector_type::reference GroupsReference; - typedef typename group_vector_type::const_reference GroupsConstReference; - typedef typename group_vector_type::iterator GroupsIterator; - typedef typename group_vector_type::const_iterator GroupsConstIterator; - - // How to deal with the proper group - static size_type num_groups(size_type num) { // how many to hold num buckets - return num == 0 ? 0 : ((num-1) / GROUP_SIZE) + 1; - } - - u_int16_t pos_in_group(size_type i) const { - return static_cast(i % GROUP_SIZE); - } - size_type group_num(size_type i) const { - return i / GROUP_SIZE; - } - GroupsReference which_group(size_type i) { - return groups[group_num(i)]; - } - GroupsConstReference which_group(size_type i) const { - return groups[group_num(i)]; - } - - public: - // Constructors -- default, normal (when you specify size), and copy - sparsetable(size_type sz = 0, Alloc alloc = Alloc()) - : groups(vector_alloc(alloc)), settings(alloc, sz) { - groups.resize(num_groups(sz), group_type(settings)); - } - // We can get away with using the default copy constructor, - // and default destructor, and hence the default operator=. Huzzah! - - // Many STL algorithms use swap instead of copy constructors - void swap(sparsetable& x) { - STL_NAMESPACE::swap(groups, x.groups); - STL_NAMESPACE::swap(settings.table_size, x.settings.table_size); - STL_NAMESPACE::swap(settings.num_buckets, x.settings.num_buckets); - } - - // It's always nice to be able to clear a table without deallocating it - void clear() { - GroupsIterator group; - for ( group = groups.begin(); group != groups.end(); ++group ) { - group->clear(); - } - settings.num_buckets = 0; - } - - // ACCESSOR FUNCTIONS for the things we templatize on, basically - allocator_type get_allocator() const { - return allocator_type(settings); - } - - - // Functions that tell you about size. - // NOTE: empty() is non-intuitive! It does not tell you the number - // of not-empty buckets (use num_nonempty() for that). Instead - // it says whether you've allocated any buckets or not. - size_type size() const { return settings.table_size; } - size_type max_size() const { return settings.max_size(); } - bool empty() const { return settings.table_size == 0; } - // We also may want to know how many *used* buckets there are - size_type num_nonempty() const { return settings.num_buckets; } - - // OK, we'll let you resize one of these puppies - void resize(size_type new_size) { - groups.resize(num_groups(new_size), group_type(settings)); - if ( new_size < settings.table_size) { - // lower num_buckets, clear last group - if ( pos_in_group(new_size) > 0 ) // need to clear inside last group - groups.back().erase(groups.back().begin() + pos_in_group(new_size), - groups.back().end()); - settings.num_buckets = 0; // refigure # of used buckets - GroupsConstIterator group; - for ( group = groups.begin(); group != groups.end(); ++group ) - settings.num_buckets += group->num_nonempty(); - } - settings.table_size = new_size; - } - - - // We let you see if a bucket is non-empty without retrieving it - bool test(size_type i) const { - return which_group(i).test(pos_in_group(i)); - } - bool test(iterator pos) const { - return which_group(pos.pos).test(pos_in_group(pos.pos)); - } - bool test(const_iterator pos) const { - return which_group(pos.pos).test(pos_in_group(pos.pos)); - } - - // We only return const_references because it's really hard to - // return something settable for empty buckets. Use set() instead. - const_reference get(size_type i) const { - assert(i < settings.table_size); - return which_group(i).get(pos_in_group(i)); - } - - // TODO(csilvers): make protected + friend - // This is used by sparse_hashtable to get an element from the table - // when we know it exists (because the caller has called test(i)). - const_reference unsafe_get(size_type i) const { - assert(i < settings.table_size); - assert(test(i)); - return which_group(i).unsafe_get(pos_in_group(i)); - } - - // TODO(csilvers): make protected + friend element_adaptor - reference mutating_get(size_type i) { // fills bucket i before getting - assert(i < settings.table_size); - size_type old_numbuckets = which_group(i).num_nonempty(); - reference retval = which_group(i).mutating_get(pos_in_group(i)); - settings.num_buckets += which_group(i).num_nonempty() - old_numbuckets; - return retval; - } - - // Syntactic sugar. As in sparsegroup, the non-const version is harder - const_reference operator[](size_type i) const { - return get(i); - } - - element_adaptor operator[](size_type i) { - return element_adaptor(this, i); - } - - // Needed for hashtables, gets as a nonempty_iterator. Crashes for empty bcks - const_nonempty_iterator get_iter(size_type i) const { - assert(test(i)); // how can a nonempty_iterator point to an empty bucket? - return const_nonempty_iterator( - groups.begin(), groups.end(), - groups.begin() + group_num(i), - (groups[group_num(i)].nonempty_begin() + - groups[group_num(i)].pos_to_offset(pos_in_group(i)))); - } - // For nonempty we can return a non-const version - nonempty_iterator get_iter(size_type i) { - assert(test(i)); // how can a nonempty_iterator point to an empty bucket? - return nonempty_iterator( - groups.begin(), groups.end(), - groups.begin() + group_num(i), - (groups[group_num(i)].nonempty_begin() + - groups[group_num(i)].pos_to_offset(pos_in_group(i)))); - } - - - // This returns a reference to the inserted item (which is a copy of val) - // The trick is to figure out whether we're replacing or inserting anew - reference set(size_type i, const_reference val) { - assert(i < settings.table_size); - size_type old_numbuckets = which_group(i).num_nonempty(); - reference retval = which_group(i).set(pos_in_group(i), val); - settings.num_buckets += which_group(i).num_nonempty() - old_numbuckets; - return retval; - } - - // This takes the specified elements out of the table. This is - // "undefining", rather than "clearing". - void erase(size_type i) { - assert(i < settings.table_size); - size_type old_numbuckets = which_group(i).num_nonempty(); - which_group(i).erase(pos_in_group(i)); - settings.num_buckets += which_group(i).num_nonempty() - old_numbuckets; - } - - void erase(iterator pos) { - erase(pos.pos); - } - - void erase(iterator start_it, iterator end_it) { - // This could be more efficient, but then we'd need to figure - // out if we spanned groups or not. Doesn't seem worth it. - for ( ; start_it != end_it; ++start_it ) - erase(start_it); - } - - - // We support reading and writing tables to disk. We don't store - // the actual array contents (which we don't know how to store), - // just the groups and sizes. Returns true if all went ok. - - private: - // Every time the disk format changes, this should probably change too - static const unsigned long MAGIC_NUMBER = 0x24687531; - - // Old versions of this code write all data in 32 bits. We need to - // support these files as well as having support for 64-bit systems. - // So we use the following encoding scheme: for values < 2^32-1, we - // store in 4 bytes in big-endian order. For values > 2^32, we - // store 0xFFFFFFF followed by 8 bytes in big-endian order. This - // causes us to mis-read old-version code that stores exactly - // 0xFFFFFFF, but I don't think that is likely to have happened for - // these particular values. - static bool write_32_or_64(FILE* fp, size_type value) { - if ( value < 0xFFFFFFFFULL ) { // fits in 4 bytes - PUT_(value, 24); - PUT_(value, 16); - PUT_(value, 8); - PUT_(value, 0); - } else if ( value == 0xFFFFFFFFUL ) { // special case in 32bit systems - PUT_(0xFF, 0); PUT_(0xFF, 0); PUT_(0xFF, 0); PUT_(0xFF, 0); // marker - PUT_(0, 0); PUT_(0, 0); PUT_(0, 0); PUT_(0, 0); - PUT_(0xFF, 0); PUT_(0xFF, 0); PUT_(0xFF, 0); PUT_(0xFF, 0); - } else { - PUT_(0xFF, 0); PUT_(0xFF, 0); PUT_(0xFF, 0); PUT_(0xFF, 0); // marker - PUT_(value, 56); - PUT_(value, 48); - PUT_(value, 40); - PUT_(value, 32); - PUT_(value, 24); - PUT_(value, 16); - PUT_(value, 8); - PUT_(value, 0); - } - return true; - } - - static bool read_32_or_64(FILE* fp, size_type *value) { // reads into value - size_type first4 = 0; - int x; - GET_(first4, 24); - GET_(first4, 16); - GET_(first4, 8); - GET_(first4, 0); - if ( first4 < 0xFFFFFFFFULL ) { - *value = first4; - } else { - GET_(*value, 56); - GET_(*value, 48); - GET_(*value, 40); - GET_(*value, 32); - GET_(*value, 24); - GET_(*value, 16); - GET_(*value, 8); - GET_(*value, 0); - } - return true; - } - - public: - bool write_metadata(FILE *fp) const { - if ( !write_32_or_64(fp, MAGIC_NUMBER) ) return false; - if ( !write_32_or_64(fp, settings.table_size) ) return false; - if ( !write_32_or_64(fp, settings.num_buckets) ) return false; - - GroupsConstIterator group; - for ( group = groups.begin(); group != groups.end(); ++group ) - if ( group->write_metadata(fp) == false ) return false; - return true; - } - - // Reading destroys the old table contents! Returns true if read ok. - bool read_metadata(FILE *fp) { - size_type magic_read = 0; - if ( !read_32_or_64(fp, &magic_read) ) return false; - if ( magic_read != MAGIC_NUMBER ) { - clear(); // just to be consistent - return false; - } - - if ( !read_32_or_64(fp, &settings.table_size) ) return false; - if ( !read_32_or_64(fp, &settings.num_buckets) ) return false; - - resize(settings.table_size); // so the vector's sized ok - GroupsIterator group; - for ( group = groups.begin(); group != groups.end(); ++group ) - if ( group->read_metadata(fp) == false ) return false; - return true; - } - - // This code is identical to that for SparseGroup - // If your keys and values are simple enough, we can write them - // to disk for you. "simple enough" means no pointers. - // However, we don't try to normalize endianness - bool write_nopointer_data(FILE *fp) const { - for ( const_nonempty_iterator it = nonempty_begin(); - it != nonempty_end(); ++it ) { - if ( !fwrite(&*it, sizeof(*it), 1, fp) ) return false; - } - return true; - } - - // When reading, we have to override the potential const-ness of *it - bool read_nopointer_data(FILE *fp) { - for ( nonempty_iterator it = nonempty_begin(); - it != nonempty_end(); ++it ) { - if ( !fread(reinterpret_cast(&(*it)), sizeof(*it), 1, fp) ) - return false; - } - return true; - } - - // Comparisons. Note the comparisons are pretty arbitrary: we - // compare values of the first index that isn't equal (using default - // value for empty buckets). - bool operator==(const sparsetable& x) const { - return ( settings.table_size == x.settings.table_size && - settings.num_buckets == x.settings.num_buckets && - groups == x.groups ); - } - bool operator<(const sparsetable& x) const { // also from algobase.h - return STL_NAMESPACE::lexicographical_compare(begin(), end(), - x.begin(), x.end()); - } - bool operator!=(const sparsetable& x) const { return !(*this == x); } - bool operator<=(const sparsetable& x) const { return !(x < *this); } - bool operator>(const sparsetable& x) const { return x < *this; } - bool operator>=(const sparsetable& x) const { return !(*this < x); } - - - private: - // Package allocator with table_size and num_buckets to eliminate memory - // needed for the zero-size allocator. - // If new fields are added to this class, we should add them to - // operator= and swap. - class Settings : public allocator_type { - public: - typedef typename allocator_type::size_type size_type; - - Settings(const allocator_type& a, size_type sz = 0, size_type n = 0) - : allocator_type(a), table_size(sz), num_buckets(n) { } - - Settings(const Settings& s) - : allocator_type(s), - table_size(s.table_size), num_buckets(s.num_buckets) { } - - size_type table_size; // how many buckets they want - size_type num_buckets; // number of non-empty buckets - }; - - // The actual data - group_vector_type groups; // our list of groups - Settings settings; // allocator, table size, buckets -}; - -// We need a global swap as well -template -inline void swap(sparsetable &x, - sparsetable &y) { - x.swap(y); -} - -#undef GET_ -#undef PUT_ - -_END_GOOGLE_NAMESPACE_ - -#endif diff --git a/tommyds/benchmark/lib/google/type_traits.h b/tommyds/benchmark/lib/google/type_traits.h deleted file mode 100644 index 87729d4..0000000 --- a/tommyds/benchmark/lib/google/type_traits.h +++ /dev/null @@ -1,336 +0,0 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// 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. - -// ---- -// Author: Matt Austern -// -// Define a small subset of tr1 type traits. The traits we define are: -// is_integral -// is_floating_point -// is_pointer -// is_enum -// is_reference -// is_pod -// has_trivial_constructor -// has_trivial_copy -// has_trivial_assign -// has_trivial_destructor -// remove_const -// remove_volatile -// remove_cv -// remove_reference -// add_reference -// remove_pointer -// is_same -// is_convertible -// We can add more type traits as required. - -#ifndef BASE_TYPE_TRAITS_H_ -#define BASE_TYPE_TRAITS_H_ - -#include -#include // For pair - -_START_GOOGLE_NAMESPACE_ - -// integral_constant, defined in tr1, is a wrapper for an integer -// value. We don't really need this generality; we could get away -// with hardcoding the integer type to bool. We use the fully -// general integer_constant for compatibility with tr1. - -template -struct integral_constant { - static const T value = v; - typedef T value_type; - typedef integral_constant type; -}; - -template const T integral_constant::value; - -// Abbreviations: true_type and false_type are structs that represent -// boolean true and false values. -typedef integral_constant true_type; -typedef integral_constant false_type; - -// Types small_ and big_ are guaranteed such that sizeof(small_) < -// sizeof(big_) -typedef char small_; - -struct big_ { - char dummy[2]; -}; - -template struct is_integral; -template struct is_floating_point; -template struct is_pointer; -// MSVC can't compile this correctly, and neither can gcc 3.3.5 (at least) -#if !defined(_MSC_VER) && !(defined(__GNUC__) && __GNUC__ <= 3) -// is_enum uses is_convertible, which is not available on MSVC. -template struct is_enum; -#endif -template struct is_reference; -template struct is_pod; -template struct has_trivial_constructor; -template struct has_trivial_copy; -template struct has_trivial_assign; -template struct has_trivial_destructor; -template struct remove_const; -template struct remove_volatile; -template struct remove_cv; -template struct remove_reference; -template struct add_reference; -template struct remove_pointer; -template struct is_same; -#if !defined(_MSC_VER) && !(defined(__GNUC__) && __GNUC__ <= 3) -template struct is_convertible; -#endif - -// is_integral is false except for the built-in integer types. -template struct is_integral : false_type { }; -template<> struct is_integral : true_type { }; -template<> struct is_integral : true_type { }; -template<> struct is_integral : true_type { }; -template<> struct is_integral : true_type { }; -#if defined(_MSC_VER) -// wchar_t is not by default a distinct type from unsigned short in -// Microsoft C. -// See http://msdn2.microsoft.com/en-us/library/dh8che7s(VS.80).aspx -template<> struct is_integral<__wchar_t> : true_type { }; -#else -template<> struct is_integral : true_type { }; -#endif -template<> struct is_integral : true_type { }; -template<> struct is_integral : true_type { }; -template<> struct is_integral : true_type { }; -template<> struct is_integral : true_type { }; -template<> struct is_integral : true_type { }; -template<> struct is_integral : true_type { }; -#ifdef HAVE_LONG_LONG -template<> struct is_integral : true_type { }; -template<> struct is_integral : true_type { }; -#endif - - -// is_floating_point is false except for the built-in floating-point types. -template struct is_floating_point : false_type { }; -template<> struct is_floating_point : true_type { }; -template<> struct is_floating_point : true_type { }; -template<> struct is_floating_point : true_type { }; - - -// is_pointer is false except for pointer types. -template struct is_pointer : false_type { }; -template struct is_pointer : true_type { }; - -#if !defined(_MSC_VER) && !(defined(__GNUC__) && __GNUC__ <= 3) - -namespace internal { - -template struct is_class_or_union { - template static small_ tester(void (U::*)()); - template static big_ tester(...); - static const bool value = sizeof(tester(0)) == sizeof(small_); -}; - -// is_convertible chokes if the first argument is an array. That's why -// we use add_reference here. -template struct is_enum_impl - : is_convertible::type, int> { }; - -template struct is_enum_impl : false_type { }; - -} // namespace internal - -// Specified by TR1 [4.5.1] primary type categories. - -// Implementation note: -// -// Each type is either void, integral, floating point, array, pointer, -// reference, member object pointer, member function pointer, enum, -// union or class. Out of these, only integral, floating point, reference, -// class and enum types are potentially convertible to int. Therefore, -// if a type is not a reference, integral, floating point or class and -// is convertible to int, it's a enum. -// -// Is-convertible-to-int check is done only if all other checks pass, -// because it can't be used with some types (e.g. void or classes with -// inaccessible conversion operators). -template struct is_enum - : internal::is_enum_impl< - is_same::value || - is_integral::value || - is_floating_point::value || - is_reference::value || - internal::is_class_or_union::value, - T> { }; - -template struct is_enum : is_enum { }; -template struct is_enum : is_enum { }; -template struct is_enum : is_enum { }; - -#endif - -// is_reference is false except for reference types. -template struct is_reference : false_type {}; -template struct is_reference : true_type {}; - - -// We can't get is_pod right without compiler help, so fail conservatively. -// We will assume it's false except for arithmetic types, enumerations, -// pointers and const versions thereof. Note that std::pair is not a POD. -template struct is_pod - : integral_constant::value || - is_floating_point::value || -#if !defined(_MSC_VER) && !(defined(__GNUC__) && __GNUC__ <= 3) - // is_enum is not available on MSVC. - is_enum::value || -#endif - is_pointer::value)> { }; -template struct is_pod : is_pod { }; - - -// We can't get has_trivial_constructor right without compiler help, so -// fail conservatively. We will assume it's false except for: (1) types -// for which is_pod is true. (2) std::pair of types with trivial -// constructors. (3) array of a type with a trivial constructor. -// (4) const versions thereof. -template struct has_trivial_constructor : is_pod { }; -template struct has_trivial_constructor > - : integral_constant::value && - has_trivial_constructor::value)> { }; -template struct has_trivial_constructor - : has_trivial_constructor { }; -template struct has_trivial_constructor - : has_trivial_constructor { }; - -// We can't get has_trivial_copy right without compiler help, so fail -// conservatively. We will assume it's false except for: (1) types -// for which is_pod is true. (2) std::pair of types with trivial copy -// constructors. (3) array of a type with a trivial copy constructor. -// (4) const versions thereof. -template struct has_trivial_copy : is_pod { }; -template struct has_trivial_copy > - : integral_constant::value && - has_trivial_copy::value)> { }; -template struct has_trivial_copy - : has_trivial_copy { }; -template struct has_trivial_copy : has_trivial_copy { }; - -// We can't get has_trivial_assign right without compiler help, so fail -// conservatively. We will assume it's false except for: (1) types -// for which is_pod is true. (2) std::pair of types with trivial copy -// constructors. (3) array of a type with a trivial assign constructor. -template struct has_trivial_assign : is_pod { }; -template struct has_trivial_assign > - : integral_constant::value && - has_trivial_assign::value)> { }; -template struct has_trivial_assign - : has_trivial_assign { }; - -// We can't get has_trivial_destructor right without compiler help, so -// fail conservatively. We will assume it's false except for: (1) types -// for which is_pod is true. (2) std::pair of types with trivial -// destructors. (3) array of a type with a trivial destructor. -// (4) const versions thereof. -template struct has_trivial_destructor : is_pod { }; -template struct has_trivial_destructor > - : integral_constant::value && - has_trivial_destructor::value)> { }; -template struct has_trivial_destructor - : has_trivial_destructor { }; -template struct has_trivial_destructor - : has_trivial_destructor { }; - -// Specified by TR1 [4.7.1] -template struct remove_const { typedef T type; }; -template struct remove_const { typedef T type; }; -template struct remove_volatile { typedef T type; }; -template struct remove_volatile { typedef T type; }; -template struct remove_cv { - typedef typename remove_const::type>::type type; -}; - - -// Specified by TR1 [4.7.2] Reference modifications. -template struct remove_reference { typedef T type; }; -template struct remove_reference { typedef T type; }; - -template struct add_reference { typedef T& type; }; -template struct add_reference { typedef T& type; }; - -// Specified by TR1 [4.7.4] Pointer modifications. -template struct remove_pointer { typedef T type; }; -template struct remove_pointer { typedef T type; }; -template struct remove_pointer { typedef T type; }; -template struct remove_pointer { typedef T type; }; -template struct remove_pointer { - typedef T type; }; - -// Specified by TR1 [4.6] Relationships between types -template struct is_same : public false_type { }; -template struct is_same : public true_type { }; - -// Specified by TR1 [4.6] Relationships between types -#if !defined(_MSC_VER) && !(defined(__GNUC__) && __GNUC__ <= 3) -namespace internal { - -// This class is an implementation detail for is_convertible, and you -// don't need to know how it works to use is_convertible. For those -// who care: we declare two different functions, one whose argument is -// of type To and one with a variadic argument list. We give them -// return types of different size, so we can use sizeof to trick the -// compiler into telling us which function it would have chosen if we -// had called it with an argument of type From. See Alexandrescu's -// _Modern C++ Design_ for more details on this sort of trick. - -template -struct ConvertHelper { - static small_ Test(To); - static big_ Test(...); - static From Create(); -}; -} // namespace internal - -// Inherits from true_type if From is convertible to To, false_type otherwise. -template -struct is_convertible - : integral_constant::Test( - internal::ConvertHelper::Create())) - == sizeof(small_)> { -}; -#endif - -_END_GOOGLE_NAMESPACE_ - -#endif // BASE_TYPE_TRAITS_H_ diff --git a/tommyds/benchmark/lib/judy/Judy.h b/tommyds/benchmark/lib/judy/Judy.h deleted file mode 100644 index 6343eec..0000000 --- a/tommyds/benchmark/lib/judy/Judy.h +++ /dev/null @@ -1,626 +0,0 @@ -#ifndef _JUDY_INCLUDED -#define _JUDY_INCLUDED -// _________________ -// -// Copyright (C) 2000 - 2002 Hewlett-Packard Company -// -// This program is free software; you can redistribute it and/or modify it -// under the term of the GNU Lesser General Public License as published by the -// Free Software Foundation; either version 2 of the License, or (at your -// option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License -// for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with this program; if not, write to the Free Software Foundation, -// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// _________________ - -// @(#) $Revision: 4.52 $ $Source: /judy/src/Judy.h $ -// -// HEADER FILE FOR EXPORTED FEATURES IN JUDY LIBRARY, libJudy.* -// -// See the manual entries for details. -// -// Note: This header file uses old-style comments on #-directive lines and -// avoids "()" on macro names in comments for compatibility with older cc -Aa -// and some tools on some platforms. - - -// PLATFORM-SPECIFIC - -#ifdef JU_WIN /* =============================================== */ - -typedef __int8 int8_t; -typedef __int16 int16_t; -typedef __int32 int32_t; -typedef __int64 int64_t; - -typedef unsigned __int8 uint8_t; -typedef unsigned __int16 uint16_t; -typedef unsigned __int32 uint32_t; -typedef unsigned __int64 uint64_t; - -#else /* ================ ! JU_WIN ============================= */ - -// ISO C99: 7.8 Format conversion of integer types -#ifdef _MSC_VER -#include -#else -#include /* if this FAILS, try #include */ -#endif - -// ISO C99: 7.18 Integer types uint*_t -//#include - -#endif /* ================ ! JU_WIN ============================= */ - -// ISO C99 Standard: 7.20 General utilities -#include - -// ISO C99 Standard: 7.10/5.2.4.2.1 Sizes of integer types -#include - -#ifdef __cplusplus /* support use by C++ code */ -extern "C" { -#endif - - -// **************************************************************************** -// DECLARE SOME BASE TYPES IN CASE THEY ARE MISSING: -// -// These base types include "const" where appropriate, but only where of -// interest to the caller. For example, a caller cares that a variable passed -// by reference will not be modified, such as, "const void * Pindex", but not -// that the called function internally does not modify the pointer itself, such -// as, "void * const Pindex". -// -// Note that its OK to pass a Pvoid_t to a Pcvoid_t; the latter is the same, -// only constant. Callers need to do this so they can also pass & Pvoid_t to -// PPvoid_t (non-constant). - -#ifndef _PCVOID_T -#define _PCVOID_T -typedef const void * Pcvoid_t; -#endif - -#ifndef _PVOID_T -#define _PVOID_T -typedef void * Pvoid_t; -typedef void ** PPvoid_t; -#endif - -#ifndef _WORD_T -#define _WORD_T -typedef unsigned long Word_t, * PWord_t; // expect 32-bit or 64-bit words. -#endif - -#ifndef NULL -#define NULL 0 -#endif - - -// **************************************************************************** -// SUPPORT FOR ERROR HANDLING: -// -// Judy error numbers: -// -// Note: These are an enum so theres a related typedef, but the numbers are -// spelled out so you can map a number back to its name. - -typedef enum // uint8_t -- but C does not support this type of enum. -{ - -// Note: JU_ERRNO_NONE and JU_ERRNO_FULL are not real errors. They specify -// conditions which are otherwise impossible return values from 32-bit -// Judy1Count, which has 2^32 + 1 valid returns (0..2^32) plus one error -// return. These pseudo-errors support the return values that cannot otherwise -// be unambiguously represented in a 32-bit word, and will never occur on a -// 64-bit system. - - JU_ERRNO_NONE = 0, - JU_ERRNO_FULL = 1, - JU_ERRNO_NFMAX = JU_ERRNO_FULL, - -// JU_ERRNO_NOMEM comes from malloc(3C) when Judy cannot obtain needed memory. -// The system errno value is also set to ENOMEM. This error can be recoverable -// if the calling application frees other memory. -// -// TBD: Currently there is no guarantee the Judy array has no memory leaks -// upon JU_ERRNO_NOMEM. - - JU_ERRNO_NOMEM = 2, - -// Problems with parameters from the calling program: -// -// JU_ERRNO_NULLPPARRAY means PPArray was null; perhaps PArray was passed where -// &PArray was intended. Similarly, JU_ERRNO_NULLPINDEX means PIndex was null; -// perhaps &Index was intended. Also, JU_ERRNO_NONNULLPARRAY, -// JU_ERRNO_NULLPVALUE, and JU_ERRNO_UNSORTED, all added later (hence with -// higher numbers), mean: A non-null array was passed in where a null pointer -// was required; PValue was null; and unsorted indexes were detected. - - JU_ERRNO_NULLPPARRAY = 3, // see above. - JU_ERRNO_NONNULLPARRAY = 10, // see above. - JU_ERRNO_NULLPINDEX = 4, // see above. - JU_ERRNO_NULLPVALUE = 11, // see above. - JU_ERRNO_NOTJUDY1 = 5, // PArray is not to a Judy1 array. - JU_ERRNO_NOTJUDYL = 6, // PArray is not to a JudyL array. - JU_ERRNO_NOTJUDYSL = 7, // PArray is not to a JudySL array. - JU_ERRNO_UNSORTED = 12, // see above. - -// Errors below this point are not recoverable; further tries to access the -// Judy array might result in EFAULT and a core dump: -// -// JU_ERRNO_OVERRUN occurs when Judy detects, upon reallocation, that a block -// of memory in its own freelist was modified since being freed. - - JU_ERRNO_OVERRUN = 8, - -// JU_ERRNO_CORRUPT occurs when Judy detects an impossible value in a Judy data -// structure: -// -// Note: The Judy data structure contains some redundant elements that support -// this type of checking. - - JU_ERRNO_CORRUPT = 9 - -// Warning: At least some C or C++ compilers do not tolerate a trailing comma -// above here. At least we know of one case, in aCC; see JAGad58928. - -} JU_Errno_t; - - -// Judy errno structure: -// -// WARNING: For compatibility with possible future changes, the fields of this -// struct should not be referenced directly. Instead use the macros supplied -// below. - -// This structure should be declared on the stack in a threaded process. - -typedef struct J_UDY_ERROR_STRUCT -{ - JU_Errno_t je_Errno; // one of the enums above. - int je_ErrID; // often an internal source line number. - Word_t je_reserved[4]; // for future backward compatibility. - -} JError_t, * PJError_t; - - -// Related macros: -// -// Fields from error struct: - -#define JU_ERRNO(PJError) ((PJError)->je_Errno) -#define JU_ERRID(PJError) ((PJError)->je_ErrID) - -// For checking return values from various Judy functions: -// -// Note: Define JERR as -1, not as the seemingly more portable (Word_t) -// (~0UL), to avoid a compiler "overflow in implicit constant conversion" -// warning. - -#define JERR (-1) /* functions returning int or Word_t */ -#define PJERR ((Pvoid_t) (~0UL)) /* mainly for use here, see below */ -#define PPJERR ((PPvoid_t) (~0UL)) /* functions that return PPvoid_t */ - -// Convenience macro for when detailed error information (PJError_t) is not -// desired by the caller; a purposely short name: - -#define PJE0 ((PJError_t) NULL) - - -// **************************************************************************** -// JUDY FUNCTIONS: -// -// P_JE is a shorthand for use below: - -#define P_JE PJError_t PJError - -// **************************************************************************** -// JUDY1 FUNCTIONS: - -extern int Judy1Test( Pcvoid_t PArray, Word_t Index, P_JE); -extern int Judy1Set( PPvoid_t PPArray, Word_t Index, P_JE); -extern int Judy1SetArray( PPvoid_t PPArray, Word_t Count, - const Word_t * const PIndex, - P_JE); -extern int Judy1Unset( PPvoid_t PPArray, Word_t Index, P_JE); -extern Word_t Judy1Count( Pcvoid_t PArray, Word_t Index1, - Word_t Index2, P_JE); -extern int Judy1ByCount( Pcvoid_t PArray, Word_t Count, - Word_t * PIndex, P_JE); -extern Word_t Judy1FreeArray( PPvoid_t PPArray, P_JE); -extern Word_t Judy1MemUsed( Pcvoid_t PArray); -extern Word_t Judy1MemActive( Pcvoid_t PArray); -extern int Judy1First( Pcvoid_t PArray, Word_t * PIndex, P_JE); -extern int Judy1Next( Pcvoid_t PArray, Word_t * PIndex, P_JE); -extern int Judy1Last( Pcvoid_t PArray, Word_t * PIndex, P_JE); -extern int Judy1Prev( Pcvoid_t PArray, Word_t * PIndex, P_JE); -extern int Judy1FirstEmpty( Pcvoid_t PArray, Word_t * PIndex, P_JE); -extern int Judy1NextEmpty( Pcvoid_t PArray, Word_t * PIndex, P_JE); -extern int Judy1LastEmpty( Pcvoid_t PArray, Word_t * PIndex, P_JE); -extern int Judy1PrevEmpty( Pcvoid_t PArray, Word_t * PIndex, P_JE); - -extern PPvoid_t JudyLGet( Pcvoid_t PArray, Word_t Index, P_JE); -extern PPvoid_t JudyLIns( PPvoid_t PPArray, Word_t Index, P_JE); -extern int JudyLInsArray( PPvoid_t PPArray, Word_t Count, - const Word_t * const PIndex, - const Word_t * const PValue, - -// **************************************************************************** -// JUDYL FUNCTIONS: - P_JE); -extern int JudyLDel( PPvoid_t PPArray, Word_t Index, P_JE); -extern Word_t JudyLCount( Pcvoid_t PArray, Word_t Index1, - Word_t Index2, P_JE); -extern PPvoid_t JudyLByCount( Pcvoid_t PArray, Word_t Count, - Word_t * PIndex, P_JE); -extern Word_t JudyLFreeArray( PPvoid_t PPArray, P_JE); -extern Word_t JudyLMemUsed( Pcvoid_t PArray); -extern Word_t JudyLMemActive( Pcvoid_t PArray); -extern PPvoid_t JudyLFirst( Pcvoid_t PArray, Word_t * PIndex, P_JE); -extern PPvoid_t JudyLNext( Pcvoid_t PArray, Word_t * PIndex, P_JE); -extern PPvoid_t JudyLLast( Pcvoid_t PArray, Word_t * PIndex, P_JE); -extern PPvoid_t JudyLPrev( Pcvoid_t PArray, Word_t * PIndex, P_JE); -extern int JudyLFirstEmpty( Pcvoid_t PArray, Word_t * PIndex, P_JE); -extern int JudyLNextEmpty( Pcvoid_t PArray, Word_t * PIndex, P_JE); -extern int JudyLLastEmpty( Pcvoid_t PArray, Word_t * PIndex, P_JE); -extern int JudyLPrevEmpty( Pcvoid_t PArray, Word_t * PIndex, P_JE); - -// **************************************************************************** -// JUDYSL FUNCTIONS: - -extern PPvoid_t JudySLGet( Pcvoid_t, const uint8_t * Index, P_JE); -extern PPvoid_t JudySLIns( PPvoid_t, const uint8_t * Index, P_JE); -extern int JudySLDel( PPvoid_t, const uint8_t * Index, P_JE); -extern Word_t JudySLFreeArray( PPvoid_t, P_JE); -extern PPvoid_t JudySLFirst( Pcvoid_t, uint8_t * Index, P_JE); -extern PPvoid_t JudySLNext( Pcvoid_t, uint8_t * Index, P_JE); -extern PPvoid_t JudySLLast( Pcvoid_t, uint8_t * Index, P_JE); -extern PPvoid_t JudySLPrev( Pcvoid_t, uint8_t * Index, P_JE); - -// **************************************************************************** -// JUDYHSL FUNCTIONS: - -extern PPvoid_t JudyHSGet( Pcvoid_t, void *, Word_t); -extern PPvoid_t JudyHSIns( PPvoid_t, void *, Word_t, P_JE); -extern int JudyHSDel( PPvoid_t, void *, Word_t, P_JE); -extern Word_t JudyHSFreeArray( PPvoid_t, P_JE); - -extern const char *Judy1MallocSizes; -extern const char *JudyLMallocSizes; - -// **************************************************************************** -// JUDY memory interface to malloc() FUNCTIONS: - -extern Word_t JudyMalloc(Word_t); // words reqd => words allocd. -extern Word_t JudyMallocVirtual(Word_t); // words reqd => words allocd. -extern void JudyFree(Pvoid_t, Word_t); // free, size in words. -extern void JudyFreeVirtual(Pvoid_t, Word_t); // free, size in words. - -#define JLAP_INVALID 0x1 /* flag to mark pointer "not a Judy array" */ - -// **************************************************************************** -// MACRO EQUIVALENTS FOR JUDY FUNCTIONS: -// -// The following macros, such as J1T, are shorthands for calling Judy functions -// with parameter address-of and detailed error checking included. Since they -// are macros, the error checking code is replicated each time the macro is -// used, but it runs fast in the normal case of no error. -// -// If the caller does not like the way the default JUDYERROR macro handles -// errors (such as an exit(1) call when out of memory), they may define their -// own before the "#include ". A routine such as HandleJudyError -// could do checking on specific error numbers and print a different message -// dependent on the error. The following is one example: -// -// Note: the back-slashes are removed because some compilers will not accept -// them in comments. -// -// void HandleJudyError(uint8_t *, int, uint8_t *, int, int); -// #define JUDYERROR(CallerFile, CallerLine, JudyFunc, JudyErrno, JudyErrID) -// { -// HandleJudyError(CallerFile, CallerLine, JudyFunc, JudyErrno, JudyErrID); -// } -// -// The routine HandleJudyError could do checking on specific error numbers and -// print a different message dependent on the error. -// -// The macro receives five parameters that are: -// -// 1. CallerFile: Source filename where a Judy call returned a serious error. -// 2. CallerLine: Line number in that source file. -// 3. JudyFunc: Name of Judy function reporting the error. -// 4. JudyErrno: One of the JU_ERRNO* values enumerated above. -// 5. JudyErrID: The je_ErrID field described above. - -#ifndef JUDYERROR_NOTEST -#ifndef JUDYERROR /* supply a default error macro */ -#include - -#define JUDYERROR(CallerFile, CallerLine, JudyFunc, JudyErrno, JudyErrID) \ - { \ - (void) fprintf(stderr, "File '%s', line %d: %s(), " \ - "JU_ERRNO_* == %d, ID == %d\n", \ - CallerFile, CallerLine, \ - JudyFunc, JudyErrno, JudyErrID); \ - exit(1); \ - } - -#endif /* JUDYERROR */ -#endif /* JUDYERROR_NOTEST */ - -// If the JUDYERROR macro is not desired at all, then the following eliminates -// it. However, the return code from each Judy function (that is, the first -// parameter of each macro) must be checked by the caller to assure that an -// error did not occur. -// -// Example: -// -// #define JUDYERROR_NOTEST 1 -// #include -// -// or use this cc option at compile time: -// -// cc -DJUDYERROR_NOTEST ... -// -// Example code: -// -// J1S(Rc, PArray, Index); -// if (Rc == JERR) goto ...error -// -// or: -// -// JLI(PValue, PArray, Index); -// if (PValue == PJERR) goto ...error - - -// Internal shorthand macros for writing the J1S, etc. macros: - -#ifdef JUDYERROR_NOTEST /* ============================================ */ - -// "Judy Set Error": - -#define J_SE(FuncName,Errno) ((void) 0) - -// Note: In each J_*() case below, the digit is the number of key parameters -// to the Judy*() call. Just assign the Func result to the callers Rc value -// without a cast because none is required, and this keeps the API simpler. -// However, a family of different J_*() macros is needed to support the -// different numbers of key parameters (0,1,2) and the Func return type. -// -// In the names below, "I" = integer result; "P" = pointer result. Note, the -// Funcs for J_*P() return PPvoid_t, but cast this to a Pvoid_t for flexible, -// error-free assignment, and then compare to PJERR. - -#define J_0I(Rc,PArray,Func,FuncName) \ - { (Rc) = Func(PArray, PJE0); } - -#define J_1I(Rc,PArray,Index,Func,FuncName) \ - { (Rc) = Func(PArray, Index, PJE0); } - -#define J_1P(PV,PArray,Index,Func,FuncName) \ - { (PV) = (Pvoid_t) Func(PArray, Index, PJE0); } - -#define J_2I(Rc,PArray,Index,Arg2,Func,FuncName) \ - { (Rc) = Func(PArray, Index, Arg2, PJE0); } - -#define J_2C(Rc,PArray,Index1,Index2,Func,FuncName) \ - { (Rc) = Func(PArray, Index1, Index2, PJE0); } - -#define J_2P(PV,PArray,Index,Arg2,Func,FuncName) \ - { (PV) = (Pvoid_t) Func(PArray, Index, Arg2, PJE0); } - -// Variations for Judy*Set/InsArray functions: - -#define J_2AI(Rc,PArray,Count,PIndex,Func,FuncName) \ - { (Rc) = Func(PArray, Count, PIndex, PJE0); } -#define J_3AI(Rc,PArray,Count,PIndex,PValue,Func,FuncName) \ - { (Rc) = Func(PArray, Count, PIndex, PValue, PJE0); } - -#else /* ================ ! JUDYERROR_NOTEST ============================= */ - -#define J_E(FuncName,PJE) \ - JUDYERROR(__FILE__, __LINE__, FuncName, JU_ERRNO(PJE), JU_ERRID(PJE)) - -#define J_SE(FuncName,Errno) \ - { \ - JError_t J_Error; \ - JU_ERRNO(&J_Error) = (Errno); \ - JU_ERRID(&J_Error) = __LINE__; \ - J_E(FuncName, &J_Error); \ - } - -// Note: In each J_*() case below, the digit is the number of key parameters -// to the Judy*() call. Just assign the Func result to the callers Rc value -// without a cast because none is required, and this keeps the API simpler. -// However, a family of different J_*() macros is needed to support the -// different numbers of key parameters (0,1,2) and the Func return type. -// -// In the names below, "I" = integer result; "P" = pointer result. Note, the -// Funcs for J_*P() return PPvoid_t, but cast this to a Pvoid_t for flexible, -// error-free assignment, and then compare to PJERR. - -#define J_0I(Rc,PArray,Func,FuncName) \ - { \ - JError_t J_Error; \ - if (((Rc) = Func(PArray, &J_Error)) == JERR) \ - J_E(FuncName, &J_Error); \ - } - -#define J_1I(Rc,PArray,Index,Func,FuncName) \ - { \ - JError_t J_Error; \ - if (((Rc) = Func(PArray, Index, &J_Error)) == JERR) \ - J_E(FuncName, &J_Error); \ - } - -#define J_1P(Rc,PArray,Index,Func,FuncName) \ - { \ - JError_t J_Error; \ - if (((Rc) = (Pvoid_t) Func(PArray, Index, &J_Error)) == PJERR) \ - J_E(FuncName, &J_Error); \ - } - -#define J_2I(Rc,PArray,Index,Arg2,Func,FuncName) \ - { \ - JError_t J_Error; \ - if (((Rc) = Func(PArray, Index, Arg2, &J_Error)) == JERR) \ - J_E(FuncName, &J_Error); \ - } - -// Variation for Judy*Count functions, which return 0, not JERR, for error (and -// also for other non-error cases): -// -// Note: JU_ERRNO_NFMAX should only apply to 32-bit Judy1, but this header -// file lacks the necessary ifdefs to make it go away otherwise, so always -// check against it. - -#define J_2C(Rc,PArray,Index1,Index2,Func,FuncName) \ - { \ - JError_t J_Error; \ - if ((((Rc) = Func(PArray, Index1, Index2, &J_Error)) == 0) \ - && (JU_ERRNO(&J_Error) > JU_ERRNO_NFMAX)) \ - { \ - J_E(FuncName, &J_Error); \ - } \ - } - -#define J_2P(PV,PArray,Index,Arg2,Func,FuncName) \ - { \ - JError_t J_Error; \ - if (((PV) = (Pvoid_t) Func(PArray, Index, Arg2, &J_Error)) \ - == PJERR) J_E(FuncName, &J_Error); \ - } - -// Variations for Judy*Set/InsArray functions: - -#define J_2AI(Rc,PArray,Count,PIndex,Func,FuncName) \ - { \ - JError_t J_Error; \ - if (((Rc) = Func(PArray, Count, PIndex, &J_Error)) == JERR) \ - J_E(FuncName, &J_Error); \ - } - -#define J_3AI(Rc,PArray,Count,PIndex,PValue,Func,FuncName) \ - { \ - JError_t J_Error; \ - if (((Rc) = Func(PArray, Count, PIndex, PValue, &J_Error)) \ - == JERR) J_E(FuncName, &J_Error); \ - } - -#endif /* ================ ! JUDYERROR_NOTEST ============================= */ - -// Some of the macros are special cases that use inlined shortcuts for speed -// with root-level leaves: - -// This is a slower version with current processors, but in the future... - -#define J1T(Rc,PArray,Index) \ - (Rc) = Judy1Test((Pvoid_t)(PArray), Index, PJE0) - -#define J1S( Rc, PArray, Index) \ - J_1I(Rc, (&(PArray)), Index, Judy1Set, "Judy1Set") -#define J1SA(Rc, PArray, Count, PIndex) \ - J_2AI(Rc,(&(PArray)), Count, PIndex, Judy1SetArray, "Judy1SetArray") -#define J1U( Rc, PArray, Index) \ - J_1I(Rc, (&(PArray)), Index, Judy1Unset, "Judy1Unset") -#define J1F( Rc, PArray, Index) \ - J_1I(Rc, PArray, &(Index), Judy1First, "Judy1First") -#define J1N( Rc, PArray, Index) \ - J_1I(Rc, PArray, &(Index), Judy1Next, "Judy1Next") -#define J1L( Rc, PArray, Index) \ - J_1I(Rc, PArray, &(Index), Judy1Last, "Judy1Last") -#define J1P( Rc, PArray, Index) \ - J_1I(Rc, PArray, &(Index), Judy1Prev, "Judy1Prev") -#define J1FE(Rc, PArray, Index) \ - J_1I(Rc, PArray, &(Index), Judy1FirstEmpty, "Judy1FirstEmpty") -#define J1NE(Rc, PArray, Index) \ - J_1I(Rc, PArray, &(Index), Judy1NextEmpty, "Judy1NextEmpty") -#define J1LE(Rc, PArray, Index) \ - J_1I(Rc, PArray, &(Index), Judy1LastEmpty, "Judy1LastEmpty") -#define J1PE(Rc, PArray, Index) \ - J_1I(Rc, PArray, &(Index), Judy1PrevEmpty, "Judy1PrevEmpty") -#define J1C( Rc, PArray, Index1, Index2) \ - J_2C(Rc, PArray, Index1, Index2, Judy1Count, "Judy1Count") -#define J1BC(Rc, PArray, Count, Index) \ - J_2I(Rc, PArray, Count, &(Index), Judy1ByCount, "Judy1ByCount") -#define J1FA(Rc, PArray) \ - J_0I(Rc, (&(PArray)), Judy1FreeArray, "Judy1FreeArray") -#define J1MU(Rc, PArray) \ - (Rc) = Judy1MemUsed(PArray) - -#define JLG(PV,PArray,Index) \ - (PV) = (Pvoid_t)JudyLGet((Pvoid_t)PArray, Index, PJE0) - -#define JLI( PV, PArray, Index) \ - J_1P(PV, (&(PArray)), Index, JudyLIns, "JudyLIns") - -#define JLIA(Rc, PArray, Count, PIndex, PValue) \ - J_3AI(Rc,(&(PArray)), Count, PIndex, PValue, JudyLInsArray, \ - "JudyLInsArray") -#define JLD( Rc, PArray, Index) \ - J_1I(Rc, (&(PArray)), Index, JudyLDel, "JudyLDel") - -#define JLF( PV, PArray, Index) \ - J_1P(PV, PArray, &(Index), JudyLFirst, "JudyLFirst") - -#define JLN( PV, PArray, Index) \ - J_1P(PV, PArray, &(Index), JudyLNext, "JudyLNext") - -#define JLL( PV, PArray, Index) \ - J_1P(PV, PArray, &(Index), JudyLLast, "JudyLLast") -#define JLP( PV, PArray, Index) \ - J_1P(PV, PArray, &(Index), JudyLPrev, "JudyLPrev") -#define JLFE(Rc, PArray, Index) \ - J_1I(Rc, PArray, &(Index), JudyLFirstEmpty, "JudyLFirstEmpty") -#define JLNE(Rc, PArray, Index) \ - J_1I(Rc, PArray, &(Index), JudyLNextEmpty, "JudyLNextEmpty") -#define JLLE(Rc, PArray, Index) \ - J_1I(Rc, PArray, &(Index), JudyLLastEmpty, "JudyLLastEmpty") -#define JLPE(Rc, PArray, Index) \ - J_1I(Rc, PArray, &(Index), JudyLPrevEmpty, "JudyLPrevEmpty") -#define JLC( Rc, PArray, Index1, Index2) \ - J_2C(Rc, PArray, Index1, Index2, JudyLCount, "JudyLCount") -#define JLBC(PV, PArray, Count, Index) \ - J_2P(PV, PArray, Count, &(Index), JudyLByCount, "JudyLByCount") -#define JLFA(Rc, PArray) \ - J_0I(Rc, (&(PArray)), JudyLFreeArray, "JudyLFreeArray") -#define JLMU(Rc, PArray) \ - (Rc) = JudyLMemUsed(PArray) - -#define JHSI(PV, PArray, PIndex, Count) \ - J_2P(PV, (&(PArray)), PIndex, Count, JudyHSIns, "JudyHSIns") -#define JHSG(PV, PArray, PIndex, Count) \ - (PV) = (Pvoid_t) JudyHSGet(PArray, PIndex, Count) -#define JHSD(Rc, PArray, PIndex, Count) \ - J_2I(Rc, (&(PArray)), PIndex, Count, JudyHSDel, "JudyHSDel") -#define JHSFA(Rc, PArray) \ - J_0I(Rc, (&(PArray)), JudyHSFreeArray, "JudyHSFreeArray") - -#define JSLG( PV, PArray, Index) \ - J_1P( PV, PArray, Index, JudySLGet, "JudySLGet") -#define JSLI( PV, PArray, Index) \ - J_1P( PV, (&(PArray)), Index, JudySLIns, "JudySLIns") -#define JSLD( Rc, PArray, Index) \ - J_1I( Rc, (&(PArray)), Index, JudySLDel, "JudySLDel") -#define JSLF( PV, PArray, Index) \ - J_1P( PV, PArray, Index, JudySLFirst, "JudySLFirst") -#define JSLN( PV, PArray, Index) \ - J_1P( PV, PArray, Index, JudySLNext, "JudySLNext") -#define JSLL( PV, PArray, Index) \ - J_1P( PV, PArray, Index, JudySLLast, "JudySLLast") -#define JSLP( PV, PArray, Index) \ - J_1P( PV, PArray, Index, JudySLPrev, "JudySLPrev") -#define JSLFA(Rc, PArray) \ - J_0I( Rc, (&(PArray)), JudySLFreeArray, "JudySLFreeArray") - -#ifdef __cplusplus -} -#endif -#endif /* ! _JUDY_INCLUDED */ diff --git a/tommyds/benchmark/lib/judy/Judy.lib b/tommyds/benchmark/lib/judy/Judy.lib deleted file mode 100644 index e82cbd66c313ef2b0278a7e4a8525d12fcb71ea7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 201574 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

I0-NJh4ZjK_F2;GoHE`a(z+&OA_u(LYE0U}2z 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

Y#h7fuwN7DY>?|8gLvpNgWy=Z1n?T^=VP1-9 zFhuk0`4RphmimUA%2X`zMkJ==UTX5_@ov5%EP$7Z6|4bezbA8*Cc!6-_(DH*hFMB>tcyiimG&x`4P(({bWknvN0Q z*0e+9(@-%&e<{Z+(1L@82Q~mN!(m$s2tUFRwpLxVjkZ#UC??~!RRAa9KpH-f(-K?{ zY|+7&1K86@VM-)(FjKAw=U{=nqR`mo1-uytld?QAM{uwVmZi%$<1&!Ef=EH&MRTl` z7q8q#>QNkILwpiPt~R+;YL745#?E9P1Z(sF{5sDBua@iW0x4}>RhU?=v45fZPd@HvG2ikwaOGmha*5{YwaqJ&i8 zZF_Fpv!~|_9|(f;*s5|&0uH+d94m2Lfy0bFkjgQP-0sA|<{P(NwfQr6 z57A@K-r#<0S4aEUme$7QT}>HFd9Yjk*d>jPV_PaJj~~;q9EzLg)wgHHEN^M;>K zIw4h_onA7JZD@g_YAR(mSIexZZtQGXl}R20I4VfrKMxNsM0*)w$s(Ejv~l{?OVUo|p>% z_QHMPgRqt~eqs3c7p@JTvweTK|CU%sIN2OtYflV6jj$1^Se+9JuXUPZ{bQ42!(wI7 zYXA8Bznt`PZ1gqZcx+;L=GyQ>R_c{AW{10D3&VL^!uzdea9$hUYc1>uKW|M`o_DMl zmxgy)o#9z)!{xfT)35ESJ0H5{=f+MBx85J_mkK$!M$kWnc5kPZwKsTOkFD0J`1$oU zoO@Gg_P4M$8(!Bw-!t6w zJk<*)^w87oMbkKIl+sfCd1|H;U9IoT?0j|B^rr5)YqA69 zdkV}w6q2<9GS&G+<2XyR?^LCZXH=!NCbq9=Y!r-{<;%5)nZnl+4i2{Q9Be@y;hNQ* z8E8!Y?s&$$AW_`^&x*9Uw zE3>XpIfq_wEF!>MQ+*8)U@j^jrD-fp6^&M%jdwYMnwQrv=@?I4R;Xk*cD1J)+m?5& zXjOI|>gqc>=R?PNoPM);J&(7kYRvH!XimDTwW}l31l3h^BMn+8WqM3GsnOFW=PoQm zudV%Va^%#R^N&fY7x)+IqCSfEr;>&N`RPR6uJzRC+(3QKOP%WcqIEg7`BjT^^NXO= zSpcQZIFveL)sIn2bzv`|)p?<*y#|MEyVvkJy@sCEYv{CILs9J{j(s@ThEsqR6j9I2 z&37i}oq;oz9kDMDf@5qW9KJTR`?@+~?V07N_O_<_&U!1=nd$CK^}cRB#dJ-XhOQ;) zme%GrOX&KBhW5-VFC7gn`W9>nr?2dUx6G;+S|JLJ^2+K`>`;PwgPkv2WNOG_GRDk7=nY{iCGHe zs&M8fvvKTix*pBmAKK>^&unKX5}JD4f+(^ka|*9JZ$m z_!!R-=)15M&UgV~HBvk;(wo}pu(0<=H2#D&8YF+ewZr@n2+s3p7gB3o{9o>n8snU8 zGVy#qaL&*8t3hyP$@r&$;4G8zPY1zgsCFS$c9rp>|K@;xC6L~W0ui=w^8{QNBl3)Z zzh5Y3tqAxzF4o~N??>ur9r~rAzoUY`45^lm_WIN~D}}$5R9yv*<8U00V|+PIl{mgb z4-cj)r{SCK%HvZ@GM(v_jp@#%U9HPf4c%6HYTc}w>bmr-Q%;#XWnOw-bxqxrbdMUK zejF7c4CZ`bE_5;Y9C78Ic5$Qd-Xby`k)y)JrB&k|SR{VlV=lS~x01r7U>OmKMiOxo z@|wE?^fSagLa%3Xky(`sUEZ;_Dc@GnLUlVyx+UR25m&Bn& z3|W@T__2bM1ZxDjc!Kc@1ph&BsUW4Mj91Tk;Q8XeOmMT{)q*z&-YNJWf=>zZ8AX0? z2>wOzpkO}6ALEY{RH85Vm97Pld(|89r99 zR&b`^8o^5iuN1sN@E3yj3+@tpQSdFn_XIx`jAFpETqObq(pSe3TR&B*^XL8Ze1|HtBw;F-es zgkYKA1i>0XrOSbIP2#^;aFgIq1pif#`x2AygMzySe<%2c;Jbp9%rU*u(crGJd|nJB z=0bcXI8RXROoZ@eNvF2I!$YWB;=eE98;A&}`CRh3NwB9b#zPYR1QF#vC%8xO_Y$wH z+9IC1Zmci&I%fK~Ah(HRIW#@X_>mGmT9D>z8D1rLDiP^tOZ+^+GbO%J;+IPJMH2o) zL3AHxc=dCi^~mLO0+>WNr|I_##t{xHlHQAEK7OB5ZDW&%TMK(!W#zb}a))0F%XQs! z{NLac{s_E&oYN8F zuWzI>o%0NWyuG;BsASO=Fu~ni)37QY&PKj4$5{m zFJ-{>jJo0yoo*R`iyH5u*N#xc5^)}cC+aK!)10~Yfp&hwRA;RriI*|Ry*Ni42E_0c zptj1s8Cl(p4EGl`QcWFOW#5iauJ!o^V~fQbbGb$ZTV?CmxO))gVXJKIEpRVj;z8mq za`|S!R@pkX*!>Y>j}-4t+KVZ%;h#7*eY9lN4k@lcbIrbyEoBW zX1@g%T-10ES&gs{C~t+konC)YBgc}zRkqv2I;ZR(A~aN5Xy@6Y1>G|MU9>0TrIn9*SROM+*9pa73XB=XO4Xf|syU1k{ zKMmBP#)s)WiCOdb3xP$AuP{$F^UP%l%o9Z=WAj)EE^7Qo`sP|&}0R-iCVvXOlC?`Euty&PMQ6W7IEj^r*SrdSszwREpVTC9sBC*CWg zkQD3UbZp$^w(?jPSBg-si)&!w4qM%|rA02EJArj^Pcd(oGiUt5(&T-b~<2? z-klB|tp9!}1Ro0ecpO+$K&ZvRU?P8Z5lrB#D=0GW3qSv(xMG8t#DSy)GRy&oNa$a*Cn`zbRD_T*Q$%c$J+1IU2rl4 z;i*4`J7WD{7d~X8Uz8jooj$j#-WLc%+#zCCU-YBo%+p~-nWlf!vu918Id96Gv=XH# z$%c{uW(&VW#OjHt>1Sgt3VMvx)i--8!ssk9iCEJX`DKSHsXc?LvLvbFIWQtlL0nt9 zD_|BG-pP$kv&Z#rZ$-gRGD{hJshzf^9TZlnj;1irToQJ=miVfCd^i}VSx^- zuli1#%?{QwJ!{^yDRbubls%uyG&oN4 zNCwx~9Tcs7eHq;Uxzr6S710lcsyFnB|C9K|Rdb3AXCLtxf`d_FMHr~oV>_?I1ci|OGCLU1r3;MGYSDrf4FqKrmBAGWuce%8Xn}S-Xy@; z0h-~Qvz*E=GI1td9sI#|Y(YD!Pzlbp;@945$0E=s4@m2E@Zxu}afUnx0fuJWNnmob zNfd}%9iSQg7Sbh2h%-F8;1AaC*#Y_*uzqRdk3l@N8nL&QV z7%yib%s9i(_zecbzlr?I@F5;5@8U@H-J$ZXTt5=jq4KZni*cyDi@oT(L*-rU8Q)P} zWJ37tpgf4**2d9>;{@XYGVSyp{;Y2Xyg7m|lHR01cqI2O1%lW~Tcit`v6C@|X0hjJ zKWjXY_Qm+zvk@34;s#^OiM&6Ey~sB@cM3iw_@dxDf`1hZV<55}Tm(-XDmYH?WWkw&iv^bpo-epjkn?}! zbBo}w1RocqIs)VO33Ayq{UO}o#6rQO;8;PfL0~-f{D_MLmkF*Byj<{V!CM6H5abjG z^Ky2TZese&1daWwTJg6Do-cU0;MIaR2;Lz``*`HX*$d*Uf^P|OOI3y|*$a^N z7wA{A7hqESV+BtVoFTYKaGBs5!Ht5(uGKB{qmA1IjeRR6lfi?2m!yB52>(lx?lr-E t692Bm|5d{OCit1e=i>dx`cr3^SRttHADnnkvmRU%6`VWgcL(i9{5Q{)O?UtR diff --git a/tommyds/benchmark/lib/judy/libJudyMalloc.a b/tommyds/benchmark/lib/judy/libJudyMalloc.a deleted file mode 100644 index 433c82a376444190881d9fd9d58936770bcdeaa4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5852 zcmb7IYj9ju9Y1IHZgQKX+oTD71Ggm5fOI!$lTe_vO&=+N^g$n5Y4K(sH_5h}-LQ|+ z#?H{k7YY>Yh`^|$s54@#_{AB-p@>*;s`3Fx7?j}yDhP~z@HPE_X#M@p{omaiWPF^N zbASK$`Jewe_uR9)bakqfnOM`5bY`~RmHjvL_OHBgpx<$Nj4J$eoZcRvcbw5CrBn?4 zTOaMWRkMe;r1JSX(D%DiJrV8`E zMWV=D!}%c>B;#j<=RjYd{sX>E2BvLt1O3=QpjQs?I6(VT&*CGtjN(s}7@Y z5c{1c{^*<#Y1*DTw~X*H_A`wCe|^`< zja?SUoe}4OC0*z5u#{p_N?H0WCZ*TmG2~0s<3<4k-EaV8Bocca+Nc%F6)GxX$L^v~ z+dqXc5@}pcuo^$0&~6+gSh2?dRwwD#wi!udBh0MlK}T2JL?yb4o?<9#5yp)^bPy!s z9E4rWYOuaZRyWLn&bmn7&lDkP{2ItdVr}{`?g@yA*^F>p|K-um;Edw zFBP`iUP1OMVXwEJrE!=3KBSS(9(xHxU8A2fY@dA(*>0K9Dmz8v>xEryU!XRjj{zc` z>+J85U7>F^>_+=t8n4t>8+ORPj@mxWy%*`+Z11IZmHwq+Z?#{gc2GZPj7RLX5XWxQ zUoq@1`?u8IF6a6k2+BpuiU2>kVzfSEA{cAv^Gp}Yn zO7+uC@58z!c8+Ce?x4wYennLCG8+8QFNoFrG`S!9D{QL<>nLm+tY_%W9m46%=4PXm zd%{TH52VXf+)Pnlqn|QHN^w)N=^LU-&G`+&>t)nMPOH`z$z4lMZ*RK-aGj;p+(<>m zPOF9$T}rj+{svXU8CG5Ejv{hvIO{W)>C2#Md|jLM)||$?EF@~a-b2NEI3nk9IC!?{tK;p> z(fHzco1Uv%5jWDMnw!@^gg0DByrtGW6^-B2d~@?~J63lNOfVjSd}F(g#+#d=j%l#= zY~MCE+`bB*j9G!JQcX3(W*Rn&A%f7bMeE^q%4aWC!|h3o5Kri}E&jO6T87)1;<|>% z*0<1TPOO$u1kSB}GuXD1$2BI`3&XkhAJ;0iumm`vD;5zoCv6j#`m{s5O60^&pQedvJ$Uw_GX{}I7-JB(`m{qd z$1~(aZbaGxOe22>I%ULNK5Y{VKJ5^BGBY-jJ2D)D{M{lWR(#qfKI+pBkuNc05V@nn zF?dK~XQI_ot9_&>eG5IJcqnN7_xNl)HOq#slhtx5na`z@0G6(5{Vf?6}ojKG8jdbE7`0!mYCppmBUkB z86>jhEd>-_Hw>REf<*GL4UtgzV!Q=re$i$6-FKt2Z0GmqqOl$l873aXr&_GnR_D3&Ve>X@+wy6IHeQ~6?H+%b=8 zp`05pcv*+v^D$ne>N<)p9<6Jat{I^dN|wF+uRy8k`_7UZ5%uHu=B zxMfbat96cLEt zvM@u#Bb(Ibt-GBKj@eeqohqUx{&dP}FqIWesY>%KVIyucc%s6+kkd5)2?%n@>PM`~POw0~?c{|Cv#Y7sLQ*bj0l;n*5vSyF-SI!8U>(p2wdsj|+%x z?o!YN^Wa_#d_byR{30F>2?BEeN9?9i$ksp^(j5-bAl3biN;xE?1uj+a;ryOKVq62} zhpSR8zXw4FJ|NXu#Cw~Bv_N+s_;9=n2o&st+0X^^<(kun^=5krMsG;X$y3Mv4Z*-} z0!g8SwHDZYN9+Q{G?NcC+OhozopdnN5!lf;Xu%SmgCIPQS1^xm$XFMSAIyV$JJ?eo zRd%d?~APZU$FW>_V%PsZiOM#pitUegCCmqJXJC)T4z=iv$}m3ez@Zj3JEty)Dz1FJMb(@-11dV>3j|uYM8OG!PFvK&0e-OM#ME^kWpG2IyHVz!x&lBt< zLU*I^>xADZcq -#include -#include - -#ifdef linux - #define _FILE_OFFSET_BITS 64 - #define _LARGEFILE_SOURCE - #define __USE_FILE_OFFSET64 - - #include -#else - #ifdef __BIG_ENDIAN__ - #ifndef BYTE_ORDER - #define BYTE_ORDER 4321 - #endif - #else - #ifndef BYTE_ORDER - #define BYTE_ORDER 1234 - #endif - #endif - #ifndef BIG_ENDIAN - #define BIG_ENDIAN 4321 - #endif -#endif - -typedef unsigned char uchar; -typedef unsigned int uint; -#define PRIuint "u" - -#if defined(__LP64__) || \ - defined(__x86_64__) || \ - defined(__amd64__) || \ - defined(_WIN64) || \ - defined(__sparc64__) || \ - defined(__arch64__) || \ - defined(__powerpc64__) || \ - defined (__s390x__) - // defines for 64 bit - - typedef unsigned long long judyvalue; - typedef unsigned long long JudySlot; - #define JUDY_key_mask (0x07) - #define JUDY_key_size 8 - #define JUDY_slot_size 8 - #define JUDY_span_bytes (3 * JUDY_key_size) - #define JUDY_span_equiv JUDY_2 - #define JUDY_radix_equiv JUDY_8 - - #define PRIjudyvalue "llu" - -#else - // defines for 32 bit - - typedef uint judyvalue; - typedef uint JudySlot; - #define JUDY_key_mask (0x03) - #define JUDY_key_size 4 - #define JUDY_slot_size 4 - #define JUDY_span_bytes (7 * JUDY_key_size) - #define JUDY_span_equiv JUDY_4 - #define JUDY_radix_equiv JUDY_8 - - #define PRIjudyvalue "u" - -#endif - -#define JUDY_mask (~(JudySlot)0x07) - -// define the alignment factor for judy nodes and allocations -// to enable this feature, set to 64 - -#define JUDY_cache_line 8 // minimum size is 8 bytes - -#if defined(STANDALONE) || defined(ASKITIS) -#include -#include - -uint MaxMem = 0; - -// void judy_abort (char *msg) __attribute__ ((noreturn)); // Tell static analyser that this function will not return -void judy_abort (char *msg) -{ - fprintf(stderr, "%s\n", msg); - exit(1); -} -#endif - -#define JUDY_seg 65536 - -enum JUDY_types { - JUDY_radix = 0, // inner and outer radix fan-out - JUDY_1 = 1, // linear list nodes of designated count - JUDY_2 = 2, - JUDY_4 = 3, - JUDY_8 = 4, - JUDY_16 = 5, - JUDY_32 = 6, -#ifdef ASKITIS - JUDY_64 = 7 -#else - JUDY_span = 7 // up to 28 tail bytes of key contiguously stored -#endif -}; - -int JudySize[] = { - (JUDY_slot_size * 16), // JUDY_radix node size - (JUDY_slot_size + JUDY_key_size), // JUDY_1 node size - (2 * JUDY_slot_size + 2 * JUDY_key_size), - (4 * JUDY_slot_size + 4 * JUDY_key_size), - (8 * JUDY_slot_size + 8 * JUDY_key_size), - (16 * JUDY_slot_size + 16 * JUDY_key_size), - (32 * JUDY_slot_size + 32 * JUDY_key_size), -#ifndef ASKITIS - (JUDY_span_bytes + JUDY_slot_size) -#else - (64 * JUDY_slot_size + 64 * JUDY_key_size) -#endif -}; - -judyvalue JudyMask[9] = { -0, 0xff, 0xffff, 0xffffff, 0xffffffff, -#if JUDY_key_size > 4 -0xffffffffffULL, 0xffffffffffffULL, 0xffffffffffffffULL, 0xffffffffffffffffULL -#endif -}; - -typedef struct { - void *seg; // next used allocator - uint next; // next available offset -} JudySeg; - -typedef struct { - JudySlot next; // judy object - uint off; // offset within key - int slot; // slot within object -} JudyStack; - -typedef struct { - JudySlot root[1]; // root of judy array - void **reuse[8]; // reuse judy blocks - JudySeg *seg; // current judy allocator - uint level; // current height of stack - uint max; // max height of stack - uint depth; // number of Integers in a key, or zero for string keys - JudyStack stack[1]; // current cursor -} Judy; - -#ifdef ASKITIS -int Words = 0; -int Inserts = 0; -int Found = 0; - -#if JUDY_key_size < 8 -#define JUDY_max JUDY_16 -#else -#define JUDY_max JUDY_64 -#endif -#else -#define JUDY_max JUDY_32 -#endif - -// open judy object -// call with max key size -// and Integer tree depth. - -void *judy_open (uint max, uint depth) -{ -JudySeg *seg; -Judy *judy; -uint amt; - - max++; // allow for zero terminator on keys - - if( (seg = (JudySeg*)malloc(JUDY_seg)) ) { - seg->seg = NULL; - seg->next = JUDY_seg; - } else { -#if defined(STANDALONE) || defined(ASKITIS) - judy_abort ("No virtual memory"); -#else - return NULL; -#endif - } - - amt = sizeof(Judy) + max * sizeof(JudyStack); - - if( amt & (JUDY_cache_line - 1) ) - amt |= JUDY_cache_line - 1, amt++; - -#if defined(STANDALONE) || defined(ASKITIS) - MaxMem += JUDY_seg; -#endif - - seg->next -= (JudySlot)seg & (JUDY_cache_line - 1); - seg->next -= amt; - - judy = (Judy *)((uchar *)seg + seg->next); - memset(judy, 0, amt); - judy->depth = depth; - judy->seg = seg; - judy->max = max; - return judy; -} - -void judy_close (Judy *judy) -{ -JudySeg *seg, *nxt = judy->seg; - - while( (seg = nxt) ) - nxt = (JudySeg*)seg->seg, free (seg); -} - -uint judy_size(Judy *judy) -{ - JudySeg* seg; - uint count; - - seg = judy->seg; - count = 0; - while (seg) { - count += JUDY_seg - seg->next; - seg = (JudySeg*)seg->seg; - } - - return count; -} - -// allocate judy node - -void *judy_alloc (Judy *judy, uint type) -{ -uint amt, idx, min; -JudySeg *seg; -void **block; -void **rtn; - - if( !judy->seg ) -#if defined(STANDALONE) || defined(ASKITIS) - judy_abort("illegal allocation from judy clone"); -#else - return NULL; -#endif - - if( type == JUDY_radix ) - type = JUDY_radix_equiv; - -#ifndef ASKITIS - if( type == JUDY_span ) - type = JUDY_span_equiv; -#endif - - amt = JudySize[type]; - - if( amt & 0x07 ) - amt |= 0x07, amt += 1; - - // see if free block is already available - - if( (block = judy->reuse[type]) ) { - judy->reuse[type] = (void**)*block; - memset (block, 0, amt); - return (void *)block; - } - - // break down available larger block - // for reuse into smaller blocks - - if( type >= JUDY_1 ) - for( idx = type; idx++ < JUDY_max; ) - if( block = judy->reuse[idx] ) { - judy->reuse[idx] = (void**)*block; - while( idx-- > type) { - judy->reuse[idx] = block + JudySize[idx] / sizeof(void *); - block[JudySize[idx] / sizeof(void *)] = 0; - } - memset (block, 0, amt); - return (void *)block; - } - - min = amt < JUDY_cache_line ? JUDY_cache_line : amt; - - if( judy->seg->next < min + sizeof(*seg) ) { - if( (seg = (JudySeg*)malloc (JUDY_seg)) ) { - seg->next = JUDY_seg; - seg->seg = judy->seg; - judy->seg = seg; - seg->next -= (JudySlot)seg & (JUDY_cache_line - 1); - } else { -#if defined(STANDALONE) || defined(ASKITIS) - judy_abort("Out of virtual memory"); -#else - return NULL; -#endif - } - -#if defined(STANDALONE) || defined(ASKITIS) - MaxMem += JUDY_seg; -#endif - } - - // generate additional free blocks - // to fill up to cache line size - - rtn = (void **)((uchar *)judy->seg + judy->seg->next - amt); - - for( idx = type; amt & (JUDY_cache_line - 1); amt <<= 1 ) { - block = (void **)((uchar *)judy->seg + judy->seg->next - 2 * amt); - judy->reuse[idx++] = block; - *block = 0; - } - - judy->seg->next -= amt; - memset (rtn, 0, JudySize[type]); - return (void *)rtn; -} - -void *judy_data (Judy *judy, uint amt) - -{ -JudySeg *seg; -void *block; - - if( !judy->seg ) -#if defined(STANDALONE) || defined(ASKITIS) - judy_abort("illegal allocation from judy clone"); -#else - return NULL; -#endif - - if( amt & (JUDY_cache_line - 1)) - amt |= (JUDY_cache_line - 1), amt += 1; - - if( judy->seg->next < amt + sizeof(*seg) ) { - if( (seg = (JudySeg*)malloc (JUDY_seg)) ) { - seg->next = JUDY_seg; - seg->seg = judy->seg; - judy->seg = seg; - seg->next -= (JudySlot)seg & (JUDY_cache_line - 1); - } else { -#if defined(STANDALONE) || defined(ASKITIS) - judy_abort("Out of virtual memory"); -#else - return NULL; -#endif - } - -#if defined(STANDALONE) || defined(ASKITIS) - MaxMem += JUDY_seg; -#endif - } - - judy->seg->next -= amt; - - block = (void *)((uchar *)judy->seg + judy->seg->next); - memset (block, 0, amt); - return block; -} - -void *judy_clone (Judy *judy) -{ -Judy *clone; -uint amt; - - amt = sizeof(Judy) + judy->max * sizeof(JudyStack); - clone = (Judy*)judy_data (judy, amt); - memcpy (clone, judy, amt); - clone->seg = NULL; // stop allocations from cloned array - return clone; -} - -void judy_free (Judy *judy, void *block, int type) -{ - if( type == JUDY_radix ) - type = JUDY_radix_equiv; - -#ifndef ASKITIS - if( type == JUDY_span ) - type = JUDY_span_equiv; -#endif - - *((void **)(block)) = judy->reuse[type]; - judy->reuse[type] = (void **)block; - return; -} - -// assemble key from current path - -uint judy_key (Judy *judy, uchar *buff, uint max) -{ -judyvalue *dest = (judyvalue *)buff; -uint len = 0, idx = 0, depth; -int slot, off, type; -judyvalue value; -uchar *base; -int keysize; - - if( judy->depth ) - max = judy->depth * JUDY_key_size; - else - max--; // leave room for zero terminator - - while( len < max && ++idx <= judy->level ) { - type = judy->stack[idx].next & 0x07; - slot = judy->stack[idx].slot; - depth = len / JUDY_key_size; - - if( judy->depth ) - if( !(len & JUDY_key_mask) ) - dest[depth] = 0; - - switch( type ) { - case JUDY_1: - case JUDY_2: - case JUDY_4: - case JUDY_8: - case JUDY_16: - case JUDY_32: -#ifdef ASKITIS - case JUDY_64: -#endif - keysize = JUDY_key_size - (judy->stack[idx].off & JUDY_key_mask); - base = (uchar *)(judy->stack[idx].next & JUDY_mask); - - if( judy->depth ) { - value = *(judyvalue *)(base + slot * keysize); - value &= JudyMask[keysize]; - dest[depth++] |= value; - len += keysize; - - if( depth < judy->depth ) - continue; - - return len; - } - -#if BYTE_ORDER != BIG_ENDIAN - off = keysize; - - while( off-- && len < max ) - if( buff[len] = base[slot * keysize + off] ) - len++; - else - break; -#else - for( off = 0; off < keysize && len < max; off++ ) - if( buff[len] = base[slot * keysize + off] ) - len++; - else - break; -#endif - continue; - - case JUDY_radix: - if( judy->depth ) { - dest[depth] |= (judyvalue)slot << (JUDY_key_size - (++len & JUDY_key_mask)) * 8; - if( !(len & JUDY_key_mask) ) - depth++; - if( depth < judy->depth ) - continue; - - return len; - } - - if( !slot ) - break; - buff[len++] = (uchar)slot; - continue; - -#ifndef ASKITIS - case JUDY_span: - base = (uchar *)(judy->stack[idx].next & JUDY_mask); - - for( slot = 0; slot < JUDY_span_bytes && base[slot]; slot++ ) - if( len < max ) - buff[len++] = base[slot]; - continue; -#endif - } - } - buff[len] = 0; - return len; -} - -// find slot & setup cursor - -JudySlot *judy_slot (Judy *judy, uchar *buff, uint max) -{ -judyvalue *src = (judyvalue *)buff; -int slot, size, keysize, tst, cnt; -JudySlot next = *judy->root; -judyvalue value, test = 0; -JudySlot *table; -JudySlot *node; -uint depth = 0; -uint off = 0; -uchar *base; - -#ifndef ASKITIS - judy->level = 0; -#endif - - while( next ) { -#ifndef ASKITIS - if( judy->level < judy->max ) - judy->level++; - - judy->stack[judy->level].next = next; - judy->stack[judy->level].off = off; -#endif - size = JudySize[next & 0x07]; - - switch( next & 0x07 ) { - - case JUDY_1: - case JUDY_2: - case JUDY_4: - case JUDY_8: - case JUDY_16: - case JUDY_32: -#ifdef ASKITIS - case JUDY_64: -#endif - base = (uchar *)(next & JUDY_mask); - node = (JudySlot *)((next & JUDY_mask) + size); - keysize = JUDY_key_size - (off & JUDY_key_mask); - cnt = size / (sizeof(JudySlot) + keysize); - slot = cnt; - value = 0; - - if( judy->depth ) { - value = src[depth++]; - off |= JUDY_key_mask; - off++; - value &= JudyMask[keysize]; - } else - do { - value <<= 8; - if( off < max ) - value |= buff[off]; - } while( ++off & JUDY_key_mask ); - - // find slot > key - - while( slot-- ) { - test = *(judyvalue *)(base + slot * keysize); -#if BYTE_ORDER == BIG_ENDIAN - test >>= 8 * (JUDY_key_size - keysize); -#else - test &= JudyMask[keysize]; -#endif - if( test <= value ) - break; - } -#ifndef ASKITIS - judy->stack[judy->level].slot = slot; -#endif - if( test == value ) { - - // is this a leaf? - - if( !judy->depth && !(value & 0xFF) || judy->depth && depth == judy->depth ) - return &node[-slot-1]; - - next = node[-slot-1]; - continue; - } - - return NULL; - - case JUDY_radix: - table = (JudySlot *)(next & JUDY_mask); // outer radix - - if( judy->depth ) - slot = (src[depth] >> ((JUDY_key_size - ++off & JUDY_key_mask) * 8)) & 0xff; - else if( off < max ) - slot = buff[off++]; - else - slot = 0; -#ifndef ASKITIS - // put radix slot on judy stack - - judy->stack[judy->level].slot = slot; -#endif - if( (next = table[slot >> 4]) ) - table = (JudySlot *)(next & JUDY_mask); // inner radix - else - return NULL; - - if( judy->depth ) - if( !(off & JUDY_key_mask) ) - depth++; - - if( !judy->depth && !slot || judy->depth && depth == judy->depth ) // leaf? - if( table[slot & 0x0F] ) // occupied? - return &table[slot & 0x0F]; - else - return NULL; - - next = table[slot & 0x0F]; - continue; - -#ifndef ASKITIS - case JUDY_span: - node = (JudySlot *)((next & JUDY_mask) + JudySize[JUDY_span]); - base = (uchar *)(next & JUDY_mask); - cnt = tst = JUDY_span_bytes; - if( tst > (int)(max - off) ) - tst = max - off; - value = strncmp((const char *)base, (const char *)(buff + off), tst); - if( !value && tst < cnt && !base[tst] ) // leaf? - return &node[-1]; - - if( !value && tst == cnt ) { - next = node[-1]; - off += cnt; - continue; - } - return NULL; -#endif - } - } - - return NULL; -} - -// promote full nodes to next larger size - -JudySlot *judy_promote (Judy *judy, JudySlot *next, int idx, judyvalue value, int keysize) -{ -uchar *base = (uchar *)(*next & JUDY_mask); -int oldcnt, newcnt, slot; -#if BYTE_ORDER == BIG_ENDIAN - int i; -#endif -JudySlot *newnode, *node; -JudySlot *result; -uchar *newbase; -uint type; - - type = (*next & 0x07) + 1; - node = (JudySlot *)((*next & JUDY_mask) + JudySize[type-1]); - oldcnt = JudySize[type-1] / (sizeof(JudySlot) + keysize); - newcnt = JudySize[type] / (sizeof(JudySlot) + keysize); - - // promote node to next larger size - - newbase = (uchar*)judy_alloc (judy, type); - newnode = (JudySlot *)(newbase + JudySize[type]); - *next = (JudySlot)newbase | type; - - // open up slot at idx - - memcpy(newbase + (newcnt - oldcnt - 1) * keysize, base, idx * keysize); // copy keys - - for( slot = 0; slot < idx; slot++ ) - newnode[-(slot + newcnt - oldcnt)] = node[-(slot + 1)]; // copy ptr - - // fill in new node - -#if BYTE_ORDER != BIG_ENDIAN - memcpy(newbase + (idx + newcnt - oldcnt - 1) * keysize, &value, keysize); // copy key -#else - i = keysize; - - while( i-- ) - newbase[(idx + newcnt - oldcnt - 1) * keysize + i] = value, value >>= 8; -#endif - result = &newnode[-(idx + newcnt - oldcnt)]; - - // copy rest of old node - - memcpy(newbase + (idx + newcnt - oldcnt) * keysize, base + (idx * keysize), (oldcnt - slot) * keysize); // copy keys - - for( ; slot < oldcnt; slot++ ) - newnode[-(slot + newcnt - oldcnt + 1)] = node[-(slot + 1)]; // copy ptr - -#ifndef ASKITIS - judy->stack[judy->level].next = *next; - judy->stack[judy->level].slot = idx + newcnt - oldcnt - 1; -#endif - judy_free (judy, (void **)base, type - 1); - return result; -} - -// construct new node for JUDY_radix entry -// make node with slot - start entries -// moving key over one offset - -void judy_radix (Judy *judy, JudySlot *radix, uchar *old, int start, int slot, int keysize, uchar key, uint depth) -{ -int size, idx, cnt = slot - start, newcnt; -JudySlot *node, *oldnode; -uint type = JUDY_1 - 1; -JudySlot *table; -uchar *base; - - // if necessary, setup inner radix node - - if( !(table = (JudySlot *)(radix[key >> 4] & JUDY_mask)) ) { - table = (JudySlot*)judy_alloc (judy, JUDY_radix); - radix[key >> 4] = (JudySlot)table | JUDY_radix; - } - - oldnode = (JudySlot *)(old + JudySize[JUDY_max]); - - // is this slot a leaf? - - if( !judy->depth && (!key || !keysize) || judy->depth && !keysize && depth == judy->depth) { - table[key & 0x0F] = oldnode[-start-1]; - return; - } - - // calculate new node big enough to contain slots - - do { - type++; - size = JudySize[type]; - newcnt = size / (sizeof(JudySlot) + keysize); - } while( cnt > newcnt && type < JUDY_max ); - - // store new node pointer in inner table - - base = (uchar*)judy_alloc (judy, type); - node = (JudySlot *)(base + size); - table[key & 0x0F] = (JudySlot)base | type; - - // allocate node and copy old contents - // shorten keys by 1 byte during copy - - for( idx = 0; idx < cnt; idx++ ) { -#if BYTE_ORDER != BIG_ENDIAN - memcpy (base + (newcnt - idx - 1) * keysize, old + (start + cnt - idx - 1) * (keysize + 1), keysize); -#else - memcpy (base + (newcnt - idx - 1) * keysize, old + (start + cnt - idx - 1) * (keysize + 1) + 1, keysize); -#endif - node[-(newcnt - idx)] = oldnode[-(start + cnt - idx)]; - } -} - -// decompose full node to radix nodes - -void judy_splitnode (Judy *judy, JudySlot *next, uint size, uint keysize, uint depth) -{ -int cnt, slot, start = 0; -uint key = 0x0100, nxt; -JudySlot *newradix; -uchar *base; - - base = (uchar *)(*next & JUDY_mask); - cnt = size / (sizeof(JudySlot) + keysize); - - // allocate outer judy_radix node - - newradix = (JudySlot*)judy_alloc (judy, JUDY_radix); - *next = (JudySlot)newradix | JUDY_radix; - - for( slot = 0; slot < cnt; slot++ ) { -#if BYTE_ORDER != BIG_ENDIAN - nxt = base[slot * keysize + keysize - 1]; -#else - nxt = base[slot * keysize]; -#endif - - if( key > 0xFF ) - key = nxt; - if( nxt == key ) - continue; - - // decompose portion of old node into radix nodes - - judy_radix (judy, newradix, base, start, slot, keysize - 1, (uchar)key, depth); - start = slot; - key = nxt; - } - - judy_radix (judy, newradix, base, start, slot, keysize - 1, (uchar)key, depth); - judy_free (judy, (void **)base, JUDY_max); -} - -// return first leaf - -JudySlot *judy_first (Judy *judy, JudySlot next, uint off, uint depth) -{ -JudySlot *table, *inner; -uint keysize, size; -JudySlot *node; -int slot, cnt; -uchar *base; - - while( next ) { - if( judy->level < judy->max ) - judy->level++; - - judy->stack[judy->level].off = off; - judy->stack[judy->level].next = next; - size = JudySize[next & 0x07]; - - switch( next & 0x07 ) { - case JUDY_1: - case JUDY_2: - case JUDY_4: - case JUDY_8: - case JUDY_16: - case JUDY_32: -#ifdef ASKITIS - case JUDY_64: -#endif - keysize = JUDY_key_size - (off & JUDY_key_mask); - node = (JudySlot *)((next & JUDY_mask) + size); - base = (uchar *)(next & JUDY_mask); - cnt = size / (sizeof(JudySlot) + keysize); - - for( slot = 0; slot < cnt; slot++ ) - if( node[-slot-1] ) - break; - - judy->stack[judy->level].slot = slot; -#if BYTE_ORDER != BIG_ENDIAN - if( !judy->depth && !base[slot * keysize] || judy->depth && ++depth == judy->depth ) - return &node[-slot-1]; -#else - if( !judy->depth && !base[slot * keysize + keysize - 1] || judy->depth && ++depth == judy->depth ) - return &node[-slot-1]; -#endif - next = node[-slot - 1]; - off = (off | JUDY_key_mask) + 1; - continue; - case JUDY_radix: - off++; - - if( judy->depth ) - if( !(off & JUDY_key_mask) ) - depth++; - - table = (JudySlot *)(next & JUDY_mask); - for( slot = 0; slot < 256; slot++ ) - if( (inner = (JudySlot *)(table[slot >> 4] & JUDY_mask)) ) { - if( (next = inner[slot & 0x0F]) ) { - judy->stack[judy->level].slot = slot; - if( !judy->depth && !slot || judy->depth && depth == judy->depth ) - return &inner[slot & 0x0F]; - else - break; - } - } else - slot |= 0x0F; - continue; -#ifndef ASKITIS - case JUDY_span: - node = (JudySlot *)((next & JUDY_mask) + JudySize[JUDY_span]); - base = (uchar *)(next & JUDY_mask); - cnt = JUDY_span_bytes; - if( !base[cnt - 1] ) // leaf node? - return &node[-1]; - next = node[-1]; - off += cnt; - continue; -#endif - } - } - return NULL; -} - -// return last leaf cell pointer - -JudySlot *judy_last (Judy *judy, JudySlot next, uint off, uint depth) -{ -JudySlot *table, *inner; -uint keysize, size; -JudySlot *node; -int slot, cnt; -uchar *base; - - while( next ) { - if( judy->level < judy->max ) - judy->level++; - - judy->stack[judy->level].next = next; - judy->stack[judy->level].off = off; - size = JudySize[next & 0x07]; - switch( next & 0x07 ) { - case JUDY_1: - case JUDY_2: - case JUDY_4: - case JUDY_8: - case JUDY_16: - case JUDY_32: -#ifdef ASKITIS - case JUDY_64: -#endif - keysize = JUDY_key_size - (off & JUDY_key_mask); - slot = size / (sizeof(JudySlot) + keysize); - base = (uchar *)(next & JUDY_mask); - node = (JudySlot *)((next & JUDY_mask) + size); - judy->stack[judy->level].slot = --slot; - -#if BYTE_ORDER != BIG_ENDIAN - if( !judy->depth && !base[slot * keysize] || judy->depth && ++depth == judy->depth ) -#else - if( !judy->depth && !base[slot * keysize + keysize - 1] || judy->depth && ++depth == judy->depth ) -#endif - return &node[-slot-1]; - - next = node[-slot-1]; - off += keysize; - continue; - - case JUDY_radix: - table = (JudySlot *)(next & JUDY_mask); - off++; - - if( judy->depth ) - if( !(off & JUDY_key_mask) ) - depth++; - - for( slot = 256; slot--; ) { - judy->stack[judy->level].slot = slot; - if( (inner = (JudySlot *)(table[slot >> 4] & JUDY_mask)) ) { - if( (next = inner[slot & 0x0F]) ) - if( !judy->depth && !slot || judy->depth && depth == judy->depth ) - return &inner[0]; - else - break; - } else - slot &= 0xF0; - } - continue; - -#ifndef ASKITIS - case JUDY_span: - node = (JudySlot *)((next & JUDY_mask) + JudySize[JUDY_span]); - base = (uchar *)(next & JUDY_mask); - cnt = JUDY_span_bytes; - if( !base[cnt - 1] ) // leaf node? - return &node[-1]; - next = node[-1]; - off += cnt; - continue; -#endif - } - } - return NULL; -} - -// judy_end: return last entry - -JudySlot *judy_end (Judy *judy) -{ - judy->level = 0; - return judy_last (judy, *judy->root, 0, 0); -} // judy_nxt: return next entry - -JudySlot *judy_nxt (Judy *judy) -{ -JudySlot *table, *inner; -int slot, size, cnt; -JudySlot *node; -JudySlot next; -uint keysize; -uchar *base; -uint depth; -uint off; - - if( !judy->level ) - return judy_first (judy, *judy->root, 0, 0); - - while( judy->level ) { - next = judy->stack[judy->level].next; - slot = judy->stack[judy->level].slot; - off = judy->stack[judy->level].off; - keysize = JUDY_key_size - (off & JUDY_key_mask); - size = JudySize[next & 0x07]; - depth = off / JUDY_key_size; - - switch( next & 0x07 ) { - case JUDY_1: - case JUDY_2: - case JUDY_4: - case JUDY_8: - case JUDY_16: - case JUDY_32: -#ifdef ASKITIS - case JUDY_64: -#endif - cnt = size / (sizeof(JudySlot) + keysize); - node = (JudySlot *)((next & JUDY_mask) + size); - base = (uchar *)(next & JUDY_mask); - if( ++slot < cnt ) -#if BYTE_ORDER != BIG_ENDIAN - if( !judy->depth && !base[slot * keysize] || judy->depth && ++depth == judy->depth ) -#else - if( !judy->depth && !base[slot * keysize + keysize - 1] || judy->depth && ++depth == judy->depth ) -#endif - { - judy->stack[judy->level].slot = slot; - return &node[-slot - 1]; - } else { - judy->stack[judy->level].slot = slot; - return judy_first (judy, node[-slot-1], (off | JUDY_key_mask) + 1, depth); - } - judy->level--; - continue; - - case JUDY_radix: - table = (JudySlot *)(next & JUDY_mask); - - if( judy->depth ) - if( !((off+1) & JUDY_key_mask) ) - depth++; - - while( ++slot < 256 ) - if( (inner = (JudySlot *)(table[slot >> 4] & JUDY_mask)) ) { - if( inner[slot & 0x0F] ) { - judy->stack[judy->level].slot = slot; - if( !judy->depth || depth < judy->depth ) - return judy_first(judy, inner[slot & 0x0F], off + 1, depth); - return &inner[slot & 0x0F]; - } - } else - slot |= 0x0F; - - judy->level--; - continue; -#ifndef ASKITIS - case JUDY_span: - judy->level--; - continue; -#endif - } - } - return NULL; -} - -// judy_prv: return ptr to previous entry - -JudySlot *judy_prv (Judy *judy) -{ -int slot, size, keysize; -JudySlot *table, *inner; -JudySlot *node, next; -uchar *base; -uint depth; -uint off; - - if( !judy->level ) - return judy_last (judy, *judy->root, 0, 0); - - while( judy->level ) { - next = judy->stack[judy->level].next; - slot = judy->stack[judy->level].slot; - off = judy->stack[judy->level].off; - size = JudySize[next & 0x07]; - depth = off / JUDY_key_size; - - switch( next & 0x07 ) { - case JUDY_1: - case JUDY_2: - case JUDY_4: - case JUDY_8: - case JUDY_16: - case JUDY_32: -#ifdef ASKITIS - case JUDY_64: -#endif - node = (JudySlot *)((next & JUDY_mask) + size); - if( !slot || !node[-slot] ) { - judy->level--; - continue; - } - - base = (uchar *)(next & JUDY_mask); - judy->stack[judy->level].slot--; - keysize = JUDY_key_size - (off & JUDY_key_mask); - -#if BYTE_ORDER != BIG_ENDIAN - if( !judy->depth && !base[(slot - 1) * keysize] || judy->depth && ++depth == judy->depth ) -#else - if( !judy->depth && !base[(slot - 1) * keysize + keysize - 1] || judy->depth && ++depth == judy->depth ) -#endif - return &node[-slot]; - return judy_last (judy, node[-slot], (off | JUDY_key_mask) + 1, depth); - - case JUDY_radix: - table = (JudySlot *)(next & JUDY_mask); - - if( judy->depth ) - if( !((off + 1) & JUDY_key_mask) ) - depth++; - - while( slot-- ) { - judy->stack[judy->level].slot--; - if( (inner = (JudySlot *)(table[slot >> 4] & JUDY_mask)) ) - if( inner[slot & 0x0F] ) - if( !judy->depth && !slot || judy->depth && depth == judy->depth ) - return &inner[0]; - else - return judy_last(judy, inner[slot & 0x0F], off + 1, depth); - } - - judy->level--; - continue; - -#ifndef ASKITIS - case JUDY_span: - judy->level--; - continue; -#endif - } - } - return NULL; -} - -// judy_del: delete string from judy array -// returning previous entry. - -JudySlot *judy_del (Judy *judy) -{ -int slot, off, size, type, high; -JudySlot *table, *inner; -JudySlot next, *node; -int keysize, cnt; -uchar *base; - - while( judy->level ) { - next = judy->stack[judy->level].next; - slot = judy->stack[judy->level].slot; - off = judy->stack[judy->level].off; - size = JudySize[next & 0x07]; - - switch( type = next & 0x07 ) { - case JUDY_1: - case JUDY_2: - case JUDY_4: - case JUDY_8: - case JUDY_16: - case JUDY_32: -#ifdef ASKITIS - case JUDY_64: -#endif - keysize = JUDY_key_size - (off & JUDY_key_mask); - cnt = size / (sizeof(JudySlot) + keysize); - node = (JudySlot *)((next & JUDY_mask) + size); - base = (uchar *)(next & JUDY_mask); - - // move deleted slot to first slot - - while( slot ) { - node[-slot-1] = node[-slot]; - memcpy (base + slot * keysize, base + (slot - 1) * keysize, keysize); - slot--; - } - - // zero out first slot - - node[-1] = 0; - memset (base, 0, keysize); - - if( node[-cnt] ) { // does node have any slots left? - judy->stack[judy->level].slot++; - return judy_prv (judy); - } - - judy_free (judy, base, type); - judy->level--; - continue; - - case JUDY_radix: - table = (JudySlot *)(next & JUDY_mask); - inner = (JudySlot *)(table[slot >> 4] & JUDY_mask); - inner[slot & 0x0F] = 0; - high = slot & 0xF0; - - for( cnt = 16; cnt--; ) - if( inner[cnt] ) - return judy_prv (judy); - - judy_free (judy, inner, JUDY_radix); - table[slot >> 4] = 0; - - for( cnt = 16; cnt--; ) - if( table[cnt] ) - return judy_prv (judy); - - judy_free (judy, table, JUDY_radix); - judy->level--; - continue; - -#ifndef ASKITIS - case JUDY_span: - base = (uchar *)(next & JUDY_mask); - judy_free (judy, base, type); - judy->level--; - continue; -#endif - } - } - - // tree is now empty - - *judy->root = 0; - return NULL; -} - -// return cell for first key greater than or equal to given key - -JudySlot *judy_strt (Judy *judy, uchar *buff, uint max) -{ -JudySlot *cell; - - judy->level = 0; - - if( !max ) - return judy_first (judy, *judy->root, 0, 0); - - if( (cell = judy_slot (judy, buff, max)) ) - return cell; - - return judy_nxt (judy); -} - -// split open span node - -#ifndef ASKITIS -void judy_splitspan (Judy *judy, JudySlot *next, uchar *base) -{ -JudySlot *node = (JudySlot *)(base + JudySize[JUDY_span]); -uint cnt = JUDY_span_bytes; -uchar *newbase; -uint off = 0; -#if BYTE_ORDER != BIG_ENDIAN -int i; -#endif - - do { - newbase = (uchar*)judy_alloc (judy, JUDY_1); - *next = (JudySlot)newbase | JUDY_1; - -#if BYTE_ORDER != BIG_ENDIAN - i = JUDY_key_size; - while( i-- ) - *newbase++ = base[off + i]; -#else - memcpy (newbase, base + off, JUDY_key_size); - newbase += JUDY_key_size; -#endif - next = (JudySlot *)newbase; - - off += JUDY_key_size; - cnt -= JUDY_key_size; - } while( cnt && base[off - 1] ); - - *next = node[-1]; - judy_free (judy, base, JUDY_span); -} -#endif - -// judy_cell: add string to judy array - -JudySlot *judy_cell (Judy *judy, uchar *buff, uint max) -{ -judyvalue *src = (judyvalue *)buff; -int size, idx, slot, cnt, tst; -JudySlot *next = judy->root; -judyvalue test, value; -uint off = 0, start; -JudySlot *table; -JudySlot *node; -uint depth = 0; -uint keysize; -uchar *base; - - judy->level = 0; -#ifdef ASKITIS - Words++; -#endif - - while( *next ) { -#ifndef ASKITIS - if( judy->level < judy->max ) - judy->level++; - - judy->stack[judy->level].next = *next; - judy->stack[judy->level].off = off; -#endif - switch( *next & 0x07 ) { - default: - size = JudySize[*next & 0x07]; - keysize = JUDY_key_size - (off & JUDY_key_mask); - cnt = size / (sizeof(JudySlot) + keysize); - base = (uchar *)(*next & JUDY_mask); - node = (JudySlot *)((*next & JUDY_mask) + size); - start = off; - slot = cnt; - value = 0; - - if( judy->depth ) { - value = src[depth++]; - off |= JUDY_key_mask; - off++; - value &= JudyMask[keysize]; - } else - do { - value <<= 8; - if( off < max ) - value |= buff[off]; - } while( ++off & JUDY_key_mask ); - - // find slot > key - - while( slot-- ) { - test = *(judyvalue *)(base + slot * keysize); -#if BYTE_ORDER == BIG_ENDIAN - test >>= 8 * (JUDY_key_size - keysize); -#else - test &= JudyMask[keysize]; -#endif - if( test <= value ) - break; - } -#ifndef ASKITIS - judy->stack[judy->level].slot = slot; -#endif - if( test == value ) { // new key is equal to slot key - next = &node[-slot-1]; - - // is this a leaf? - - if( !judy->depth && !(value & 0xFF) || judy->depth && depth == judy->depth ) { -#ifdef ASKITIS - if( *next ) - Found++; - else - Inserts++; -#endif - return next; - } - - continue; - } - - // if this node is not full - // open up cell after slot - - if( !node[-1] ) { - memmove(base, base + keysize, slot * keysize); // move keys less than new key down one slot -#if BYTE_ORDER != BIG_ENDIAN - memcpy(base + slot * keysize, &value, keysize); // copy new key into slot -#else - test = value; - idx = keysize; - - while( idx-- ) - base[slot * keysize + idx] = test, test >>= 8; -#endif - for( idx = 0; idx < slot; idx++ ) - node[-idx-1] = node[-idx-2];// copy tree ptrs/cells down one slot - - node[-slot-1] = 0; // set new tree ptr/cell - next = &node[-slot-1]; - - if( !judy->depth && !(value & 0xFF) || judy->depth && depth == judy->depth ) { -#ifdef ASKITIS - if( *next ) - Found++; - else - Inserts++; -#endif - return next; - } - - continue; - } - - if( size < JudySize[JUDY_max] ) { - next = judy_promote (judy, next, slot+1, value, keysize); - - if( !judy->depth && !(value & 0xFF) || judy->depth && depth == judy->depth ) { -#ifdef ASKITIS - if( *next ) - Found++; - else - Inserts++; -#endif - return next; - } - - continue; - } - - // split full maximal node into JUDY_radix nodes - // loop to reprocess new insert - - judy_splitnode (judy, next, size, keysize, depth); -#ifndef ASKITIS - judy->level--; -#endif - off = start; - if( judy->depth ) - depth--; - continue; - - case JUDY_radix: - table = (JudySlot *)(*next & JUDY_mask); // outer radix - - if( judy->depth ) - slot = (src[depth] >> ((JUDY_key_size - ++off & JUDY_key_mask) * 8)) & 0xff; - else if( off < max ) - slot = buff[off++]; - else - slot = 0, off++; - - if( judy->depth ) - if( !(off & JUDY_key_mask) ) - depth++; - - // allocate inner radix if empty - - if( !table[slot >> 4] ) - table[slot >> 4] = (JudySlot)judy_alloc (judy, JUDY_radix) | JUDY_radix; - - table = (JudySlot *)(table[slot >> 4] & JUDY_mask); -#ifndef ASKITIS - judy->stack[judy->level].slot = slot; -#endif - next = &table[slot & 0x0F]; - - if( !judy->depth && !slot || judy->depth && depth == judy->depth ) { // leaf? -#ifdef ASKITIS - if( *next ) - Found++; - else - Inserts++; -#endif - return next; - } - - continue; - -#ifndef ASKITIS - case JUDY_span: - base = (uchar *)(*next & JUDY_mask); - node = (JudySlot *)((*next & JUDY_mask) + JudySize[JUDY_span]); - cnt = JUDY_span_bytes; - tst = cnt; - - if( tst > (int)(max - off) ) - tst = max - off; - - value = strncmp((const char *)base, (const char *)(buff + off), tst); - - if( !value && tst < cnt && !base[tst] ) // leaf? - return &node[-1]; - - if( !value && tst == cnt ) { - next = &node[-1]; - off += cnt; - continue; - } - - // bust up JUDY_span node and produce JUDY_1 nodes - // then loop to reprocess insert - - judy_splitspan (judy, next, base); - judy->level--; - continue; -#endif - } - } - - // place JUDY_1 node under JUDY_radix node(s) - -#ifndef ASKITIS - if( off & JUDY_key_mask ) - if( judy->depth || off <= max ) { -#else - while( off <= max ) { -#endif - base = (uchar*)judy_alloc (judy, JUDY_1); - keysize = JUDY_key_size - (off & JUDY_key_mask); - node = (JudySlot *)(base + JudySize[JUDY_1]); - *next = (JudySlot)base | JUDY_1; - - // fill in slot 0 with bytes of key - - if( judy->depth ) { - value = src[depth]; -#if BYTE_ORDER != BIG_ENDIAN - memcpy(base, &value, keysize); // copy new key into slot -#else - while( keysize-- ) - base[keysize] = value, value >>= 8; -#endif - } else { -#if BYTE_ORDER != BIG_ENDIAN - while( keysize ) - if( off + keysize <= max ) - *base++ = buff[off + --keysize]; - else - base++, --keysize; -#else - tst = keysize; - - if( tst > (int)(max - off) ) - tst = max - off; - - memcpy (base, buff + off, tst); -#endif - } -#ifndef ASKITIS - if( judy->level < judy->max ) - judy->level++; - judy->stack[judy->level].next = *next; - judy->stack[judy->level].slot = 0; - judy->stack[judy->level].off = off; -#endif - next = &node[-1]; - - off |= JUDY_key_mask; - depth++; - off++; - } - - // produce span nodes to consume rest of key - // or judy_1 nodes if not string tree - -#ifndef ASKITIS - if( !judy->depth ) - while( off <= max ) { - base = (uchar*)judy_alloc (judy, JUDY_span); - *next = (JudySlot)base | JUDY_span; - node = (JudySlot *)(base + JudySize[JUDY_span]); - cnt = tst = JUDY_span_bytes; - if( tst > (int)(max - off) ) - tst = max - off; - memcpy (base, buff + off, tst); - - if( judy->level < judy->max ) - judy->level++; - judy->stack[judy->level].next = *next; - judy->stack[judy->level].slot = 0; - judy->stack[judy->level].off = off; - next = &node[-1]; - off += tst; - depth++; - - if( !base[cnt-1] ) // done on leaf - break; - } - else - while( depth < judy->depth ) { - base = (uchar*)judy_alloc (judy, JUDY_1); - node = (JudySlot *)(base + JudySize[JUDY_1]); - *next = (JudySlot)base | JUDY_1; - - // fill in slot 0 with bytes of key - - *(judyvalue *)base = src[depth]; - - if( judy->level < judy->max ) - judy->level++; - judy->stack[judy->level].next = *next; - judy->stack[judy->level].slot = 0; - judy->stack[judy->level].off = off; - next = &node[-1]; - off |= JUDY_key_mask; - depth++; - off++; - } -#endif - -#ifdef ASKITIS - Inserts++; -#endif - return next; -} - -#if defined(STANDALONE) || defined(ASKITIS) - -#if defined(__APPLE__) || defined(linux) -#include -#include -#include -#include -#include -#else -#include -#include -#endif - -#include - -// memory map input file and sort - -// define pennysort parameters - -uint PennyRecs = (4096 * 400); // records to sort to temp files -uint PennyLine = 100; // length of input record -uint PennyKey = 10; // length of input key -uint PennyOff = 0; // key offset in input record - -unsigned long long PennyMerge; // PennyRecs * PennyLine = file map length -uint PennyPasses; // number of intermediate files created -uint PennySortTime; // cpu time to run sort -uint PennyMergeTime; // cpu time to run merge - -typedef struct { - void *buff; // record pointer in input file map - void *next; // duplicate chain -} PennySort; - -void sort (FILE *infile, char *outname) -{ -unsigned long long size, off, offset, part; -int ifd = fileno (infile); -char filename[512]; -PennySort *line; -JudySlot *cell; -uchar *inbuff; -void *judy; -FILE *out; -#if defined(_WIN32) -HANDLE hndl, fm; -DWORD hiword; -FILETIME dummy[1]; -FILETIME user[1]; -#else -struct tms buff[1]; -#endif -time_t start = time(NULL); - - if( PennyOff + PennyKey > PennyLine ) - fprintf (stderr, "Key Offset + Key Length > Record Length\n"), exit(1); - - offset = 0; - PennyPasses = 0; - -#if defined(_WIN32) - hndl = (HANDLE)_get_osfhandle(ifd); - size = GetFileSize (hndl, &hiword); - fm = CreateFileMapping(hndl, NULL, PAGE_READONLY, hiword, (DWORD)size, NULL); - if( !fm ) - fprintf (stderr, "CreateFileMapping error %d\n", GetLastError()), exit(1); - size |= (unsigned long long)hiword << 32; -#else - size = lseek (ifd, 0L, 2); -#endif - - while( offset < size ) { -#if defined(_WIN32) - part = offset + PennyMerge > size ? size - offset : PennyMerge; - inbuff = MapViewOfFile( fm, FILE_MAP_READ, offset >> 32, offset, part); - if( !inbuff ) - fprintf (stderr, "MapViewOfFile error %d\n", GetLastError()), exit(1); -#else - inbuff = mmap (NULL, PennyMerge, PROT_READ, MAP_SHARED, ifd, offset); - - if( inbuff == MAP_FAILED ) - fprintf (stderr, "mmap error %d\n", errno), exit(1); - - if( madvise (inbuff, PennyMerge, MADV_WILLNEED | MADV_SEQUENTIAL) < 0 ) - fprintf (stderr, "madvise error %d\n", errno); -#endif - judy = judy_open (PennyKey, 0); - - off = 0; - - // build judy array from mapped input chunk - - while( offset + off < size && off < PennyMerge ) { - line = judy_data (judy, sizeof(PennySort)); - cell = judy_cell (judy, inbuff + off + PennyOff, PennyKey); - line->next = *(void **)cell; - line->buff = inbuff + off; - - *(PennySort **)cell = line; - off += PennyLine; - } - - sprintf (filename, "%s.%d", outname, PennyPasses); - out = fopen (filename, "wb"); - setvbuf (out, NULL, _IOFBF, 4096 * 1024); - -#ifndef _WIN32 - if( madvise (inbuff, PennyMerge, MADV_WILLNEED | MADV_RANDOM) < 0 ) - fprintf (stderr, "madvise error %d\n", errno); -#endif - - // write judy array in sorted order to temporary file - - cell = judy_strt (judy, NULL, 0); - - if( cell ) do { - line = *(PennySort **)cell; - do fwrite (line->buff, PennyLine, 1, out); - while( line = line->next ); - } while( cell = judy_nxt (judy) ); - -#if defined(_WIN32) - UnmapViewOfFile (inbuff); -#else - munmap (inbuff, PennyMerge); -#endif - judy_close (judy); - offset += off; - fflush (out); - fclose (out); - PennyPasses++; - } - fprintf (stderr, "End Sort %d secs", time(NULL) - start); -#if defined(_WIN32) - CloseHandle (fm); - GetProcessTimes (GetCurrentProcess(), dummy, dummy, dummy, user); - PennySortTime = *(unsigned long long*)user / 10000000; -#else - times (buff); - PennySortTime = buff->tms_utime/100; -#endif - fprintf (stderr, " Cpu %d\n", PennySortTime); -} - -int merge (FILE *out, char *outname) -{ -time_t start = time(NULL); -char filename[512]; -JudySlot *cell; -uint nxt, idx; -uchar **line; -uint *next; -void *judy; -FILE **in; - - next = calloc (PennyPasses + 1, sizeof(uint)); - line = calloc (PennyPasses, sizeof(void *)); - in = calloc (PennyPasses, sizeof(void *)); - - judy = judy_open (PennyKey, 0); - - // initialize merge with one record from each temp file - - for( idx = 0; idx < PennyPasses; idx++ ) { - sprintf (filename, "%s.%d", outname, idx); - in[idx] = fopen (filename, "rb"); - line[idx] = malloc (PennyLine); - setvbuf (in[idx], NULL, _IOFBF, 4096 * 1024); - fread (line[idx], PennyLine, 1, in[idx]); - cell = judy_cell (judy, line[idx] + PennyOff, PennyKey); - next[idx + 1] = *(uint *)cell; - *cell = idx + 1; - } - - // output records, replacing smallest each time - - while( cell = judy_strt (judy, NULL, 0) ) { - nxt = *(uint *)cell; - judy_del (judy); - - // process duplicates - - while( idx = nxt ) { - nxt = next[idx--]; - fwrite (line[idx], PennyLine, 1, out); - - if( fread (line[idx], PennyLine, 1, in[idx]) ) { - cell = judy_cell (judy, line[idx] + PennyOff, PennyKey); - next[idx + 1] = *(uint *)cell; - *cell = idx + 1; - } else - next[idx + 1] = 0; - } - } - - for( idx = 0; idx < PennyPasses; idx++ ) { - fclose (in[idx]); - free (line[idx]); - } - - free (line); - free (next); - free (in); - - fprintf (stderr, "End Merge %d secs", time(NULL) - start); -#ifdef _WIN32 - { - FILETIME dummy[1]; - FILETIME user[1]; - GetProcessTimes (GetCurrentProcess(), dummy, dummy, dummy, user); - PennyMergeTime = *(unsigned long long*)user / 10000000; - } -#else - { - struct tms buff[1]; - times (buff); - PennyMergeTime = buff->tms_utime/100; - } -#endif - fprintf (stderr, " Cpu %d\n", PennyMergeTime - PennySortTime); - judy_close (judy); - fflush (out); - fclose (out); - return 0; -} - -// compilation: -// cc -O3 judy64j.c - -// usage: -// a.out [in-file] [out-file] [keysize] [recordlen] [keyoffset] [mergerecs] -// where keysize is 10 to indicate pennysort files - -#if !defined(_WIN32) -typedef struct timeval timer; -#endif - -// ASKITIS compilation: -// cc -O3 judy64n.c - -// usage: -// a.out [in-file] [out-file] [keysize] [recordlen] [keyoffset] [mergerecs] -// where keysize is 10 to indicate pennysort files - -// naskitis.com. -// g++ -O3 -fpermissive -fomit-frame-pointer -w -D STANDALONE -D ASKITIS -o judy64n judy64n.c -// ./judy64n [input-file-to-build-judy] e.g. distinct_1 or skew1_1 - -// note: the judy array is an in-memory data structure. As such, make sure you -// have enough memory to hold the entire input file + data structure, otherwise -// you'll have to break the input file into smaller pieces and load them in -// on-by-one. - -// Also, the file to search judy is hardcoded to skew1_1. - -int main (int argc, char **argv) -{ -uchar buff[1024]; -JudySlot max = 0; -JudySlot *cell; -FILE *in, *out; -void *judy; -uint len; -uint idx; -#ifdef ASKITIS -char *askitis; -int prev, off; -float insert_real_time=0.0; -float search_real_time=0.0; -int size; -#if !defined(_WIN32) -timer start, stop; -#else -time_t start[1], stop[1]; -#endif -#endif - - if( argc > 1 ) - in = fopen (argv[1], "rb"); - else - in = stdin; - - if( argc > 2 ) - out = fopen (argv[2], "wb"); - else - out = stdout; - - setvbuf (out, NULL, _IOFBF, 4096 * 1024); - - if( !in ) - fprintf (stderr, "unable to open input file\n"); - - if( !out ) - fprintf (stderr, "unable to open output file\n"); - - if( argc > 6 ) - PennyRecs = atoi(argv[6]); - - if( argc > 5 ) - PennyOff = atoi(argv[5]); - - if( argc > 4 ) - PennyLine = atoi(argv[4]); - - PennyMerge = (unsigned long long)PennyLine * PennyRecs; - - if( argc > 3 ) { - PennyKey = atoi(argv[3]); - sort (in, argv[2]); - return merge (out, argv[2]); - } - -#ifdef ASKITIS - judy = judy_open (1024, 0); - -// build judy array - size = lseek (fileno(in), 0L, 2); - askitis = malloc(size); - lseek (fileno(in), 0L, 0); - read (fileno(in), askitis,size); - prev = 0; -// naskitis.com: -// Start the timer. - -#if !defined(_WIN32) - gettimeofday(&start, NULL); -#else - time(start); -#endif - - for( off = 0; off < size; off++ ) - if( askitis[off] == '\n' ) { - *(judy_cell (judy, askitis+prev, off - prev)) += 1; // count instances of string - prev = off + 1; - } -// naskitis.com: -// Stop the timer and do some math to compute the time required to insert the strings into the judy array. - -#if !defined(_WIN32) - gettimeofday(&stop, NULL); - - insert_real_time = 1000.0 * ( stop.tv_sec - start.tv_sec ) + 0.001 * (stop.tv_usec - start.tv_usec ); - insert_real_time = insert_real_time/1000.0; -#else - time (stop); - insert_real_time = *stop - *start; -#endif - -// naskitis.com: -// Free the input buffer used to store the first file. We must do this before we get the process size below. - free (askitis); - fprintf(stderr, "JudyArray@Karl_Malbrain\nDASKITIS option enabled\n-------------------------------\n%-20s %.2f MB\n%-20s %.2f sec\n", - "Judy Array size:", MaxMem/1000000., "Time to insert:", insert_real_time); - fprintf(stderr, "%-20s %d\n", "Words:", Words); - fprintf(stderr, "%-20s %d\n", "Inserts:", Inserts); - fprintf(stderr, "%-20s %d\n", "Found:", Found); - - Words = 0; - Inserts = 0; - Found = 0; - -// search judy array - if( in = freopen ("skew1_1", "rb", in) ) - size = lseek (fileno(in), 0L, 2); - else - exit(0); - askitis = malloc(size); - lseek (fileno(in), 0L, 0); - read (fileno(in), askitis,size); - prev = 0; - -#if !defined(_WIN32) - gettimeofday(&start, NULL); -#else - time(start); -#endif - - for( off = 0; off < size; off++ ) - if( askitis[off] == '\n' ) { - *judy_cell (judy, askitis+prev, off - prev) += 1; - prev = off + 1; - } -// naskitis.com: -// Stop the timer and do some math to compute the time required to search the judy array. - -#if !defined(_WIN32) - gettimeofday(&stop, NULL); - search_real_time = 1000.0 * ( stop.tv_sec - start.tv_sec ) + 0.001 - * (stop.tv_usec - start.tv_usec ); - search_real_time = search_real_time/1000.0; -#else - time(stop); - search_real_time = *stop - *start; -#endif - -// naskitis.com: -// To do: report a count on the number of strings found. - - fprintf(stderr,"\n%-20s %.2f MB\n%-20s %.2f sec\n", - "Judy Array size:", MaxMem/1000000., "Time to search:", search_real_time); - fprintf(stderr, "%-20s %d\n", "Words:", Words); - fprintf(stderr, "%-20s %d\n", "Inserts:", Inserts); - fprintf(stderr, "%-20s %d\n", "Found:", Found); - exit(0); -#endif -#ifdef HEXKEYS - judy = judy_open (1024, 16/JUDY_key_size); - - while( fgets((char *)buff, sizeof(buff), in) ) { - judyvalue key[16/JUDY_key_size]; - if( len = strlen((const char *)buff) ) - buff[--len] = 0; // remove LF -#if JUDY_key_size == 4 - key[3] = strtoul (buff + 24, NULL, 16); - buff[24] = 0; - key[2] = strtoul (buff + 16, NULL, 16); - buff[16] = 0; - key[1] = strtoul (buff + 8, NULL, 16); - buff[8] = 0; - key[0] = strtoul (buff, NULL, 16); -#else - key[1] = strtoull (buff + 16, NULL, 16); - buff[16] = 0; - key[0] = strtoull (buff, NULL, 16); -#endif - *(judy_cell (judy, (void *)key, 0)) += 1; // count instances of string - max++; - } - - fprintf(stderr, "%" PRIuint " memory used\n", MaxMem); - - cell = judy_strt (judy, NULL, 0); - - if( cell ) do { - judyvalue key[16/JUDY_key_size]; - len = judy_key(judy, (void *)key, 0); - for( idx = 0; idx < *cell; idx++ ){ // spit out duplicates -#if JUDY_key_size == 4 - fprintf (out, "%.8X", key[0]); - fprintf (out, "%.8X", key[1]); - fprintf (out, "%.8X", key[2]); - fprintf (out, "%.8X", key[3]); -#else - fprintf (out, "%.16llX", key[0]); - fprintf (out, "%.16llX", key[1]); -#endif - fputc('\n', out); - } - } while( cell = judy_nxt (judy) ); - -#else - judy = judy_open (1024, 0); - - while( fgets((char *)buff, sizeof(buff), in) ) { - if( len = strlen((const char *)buff) ) - buff[--len] = 0; // remove LF - *(judy_cell (judy, buff, len)) += 1; // count instances of string - max++; - } - - fprintf(stderr, "%" PRIuint " memory used\n", MaxMem); - - cell = judy_strt (judy, NULL, 0); - - if( cell ) do { - len = judy_key(judy, buff, sizeof(buff)); - for( idx = 0; idx < *cell; idx++ ){ // spit out duplicates - fwrite(buff, len, 1, out); - fputc('\n', out); - } - } while( cell = judy_nxt (judy) ); -#endif -#if 0 - // test deletion all the way to an empty tree - - if( cell = judy_prv (judy) ) - do max -= *cell; - while( cell = judy_del (judy) ); - - assert (max == 0); -#endif - judy_close(judy); - return 0; -} -#endif - diff --git a/tommyds/benchmark/lib/khash/khash.h b/tommyds/benchmark/lib/khash/khash.h deleted file mode 100644 index 22d80e8..0000000 --- a/tommyds/benchmark/lib/khash/khash.h +++ /dev/null @@ -1,315 +0,0 @@ -/* The MIT License - - Copyright (c) 2008, by Attractive Chaos - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ - -/* - An example: - -#include "khash.h" -KHASH_MAP_INIT_INT(32, char) -int main() { - int ret, is_missing; - khiter_t k; - khash_t(32) *h = kh_init(32); - k = kh_put(32, h, 5, &ret); - if (!ret) kh_del(32, h, k); - kh_value(h, k) = 10; - k = kh_get(32, h, 10); - is_missing = (k == kh_end(h)); - k = kh_get(32, h, 5); - kh_del(32, h, k); - for (k = kh_begin(h); k != kh_end(h); ++k) - if (kh_exist(h, k)) kh_value(h, k) = 1; - kh_destroy(32, h); - return 0; -} -*/ - -/* - 2008-09-19 (0.2.3): - - * Corrected the example - * Improved interfaces - - 2008-09-11 (0.2.2): - - * Improved speed a little in kh_put() - - 2008-09-10 (0.2.1): - - * Added kh_clear() - * Fixed a compiling error - - 2008-09-02 (0.2.0): - - * Changed to token concatenation which increases flexibility. - - 2008-08-31 (0.1.2): - - * Fixed a bug in kh_get(), which has not been tested previously. - - 2008-08-31 (0.1.1): - - * Added destructor -*/ - - -#ifndef __AC_KHASH_H -#define __AC_KHASH_H - -#define AC_VERSION_KHASH_H "0.2.2" - -#include -#include - -typedef uint32_t khint_t; -typedef khint_t khiter_t; - -#define __ac_HASH_PRIME_SIZE 32 -static const uint32_t __ac_prime_list[__ac_HASH_PRIME_SIZE] = -{ - 0ul, 3ul, 11ul, 23ul, 53ul, - 97ul, 193ul, 389ul, 769ul, 1543ul, - 3079ul, 6151ul, 12289ul, 24593ul, 49157ul, - 98317ul, 196613ul, 393241ul, 786433ul, 1572869ul, - 3145739ul, 6291469ul, 12582917ul, 25165843ul, 50331653ul, - 100663319ul, 201326611ul, 402653189ul, 805306457ul, 1610612741ul, - 3221225473ul, 4294967291ul -}; - -#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2) -#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1) -#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3) -#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1))) -#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1))) -#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1))) -#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1)) - -static const double __ac_HASH_UPPER = 0.77; - -#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ - typedef struct { \ - khint_t n_buckets, size, n_occupied, upper_bound; \ - uint32_t *flags; \ - khkey_t *keys; \ - khval_t *vals; \ - } kh_##name##_t; \ - static inline kh_##name##_t *kh_init_##name() { \ - return (kh_##name##_t*)calloc(1, sizeof(kh_##name##_t)); \ - } \ - static inline void kh_destroy_##name(kh_##name##_t *h) \ - { \ - if (h) { \ - free(h->keys); free(h->flags); \ - free(h->vals); \ - free(h); \ - } \ - } \ - static inline void kh_clear_##name(kh_##name##_t *h) \ - { \ - if (h && h->flags) { \ - memset(h->flags, 0xaa, ((h->n_buckets>>4) + 1) * sizeof(uint32_t)); \ - h->size = h->n_occupied = 0; \ - } \ - } \ - static inline khint_t kh_get_##name(kh_##name##_t *h, khkey_t key) \ - { \ - if (h->n_buckets) { \ - khint_t inc, k, i, last; \ - k = __hash_func(key); i = k % h->n_buckets; \ - inc = 1 + k % (h->n_buckets - 1); last = i; \ - while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ - if (i + inc >= h->n_buckets) i = i + inc - h->n_buckets; \ - else i += inc; \ - if (i == last) return h->n_buckets; \ - } \ - return __ac_iseither(h->flags, i)? h->n_buckets : i; \ - } else return 0; \ - } \ - static inline void kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \ - { \ - uint32_t *new_flags = 0; \ - khint_t j = 1; \ - { \ - khint_t t = __ac_HASH_PRIME_SIZE - 1; \ - while (__ac_prime_list[t] > new_n_buckets) --t; \ - new_n_buckets = __ac_prime_list[t+1]; \ - if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; \ - else { \ - new_flags = (uint32_t*)malloc(((new_n_buckets>>4) + 1) * sizeof(uint32_t)); \ - memset(new_flags, 0xaa, ((new_n_buckets>>4) + 1) * sizeof(uint32_t)); \ - if (h->n_buckets < new_n_buckets) { \ - h->keys = (khkey_t*)realloc(h->keys, new_n_buckets * sizeof(khkey_t)); \ - if (kh_is_map) \ - h->vals = (khval_t*)realloc(h->vals, new_n_buckets * sizeof(khval_t)); \ - } \ - } \ - } \ - if (j) { \ - for (j = 0; j != h->n_buckets; ++j) { \ - if (__ac_iseither(h->flags, j) == 0) { \ - khkey_t key = h->keys[j]; \ - khval_t val; \ - if (kh_is_map) val = h->vals[j]; \ - __ac_set_isdel_true(h->flags, j); \ - while (1) { \ - khint_t inc, k, i; \ - k = __hash_func(key); \ - i = k % new_n_buckets; \ - inc = 1 + k % (new_n_buckets - 1); \ - while (!__ac_isempty(new_flags, i)) { \ - if (i + inc >= new_n_buckets) i = i + inc - new_n_buckets; \ - else i += inc; \ - } \ - __ac_set_isempty_false(new_flags, i); \ - if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { \ - { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \ - if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \ - __ac_set_isdel_true(h->flags, i); \ - } else { \ - h->keys[i] = key; \ - if (kh_is_map) h->vals[i] = val; \ - break; \ - } \ - } \ - } \ - } \ - if (h->n_buckets > new_n_buckets) { \ - h->keys = (khkey_t*)realloc(h->keys, new_n_buckets * sizeof(khkey_t)); \ - if (kh_is_map) \ - h->vals = (khval_t*)realloc(h->vals, new_n_buckets * sizeof(khval_t)); \ - } \ - free(h->flags); \ - h->flags = new_flags; \ - h->n_buckets = new_n_buckets; \ - h->n_occupied = h->size; \ - h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \ - } \ - } \ - static inline khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \ - { \ - khint_t x; \ - if (h->n_occupied >= h->upper_bound) { \ - if (h->n_buckets > (h->size<<1)) kh_resize_##name(h, h->n_buckets - 1); \ - else kh_resize_##name(h, h->n_buckets + 1); \ - } \ - { \ - khint_t inc, k, i, site, last; \ - x = site = h->n_buckets; k = __hash_func(key); i = k % h->n_buckets; \ - if (__ac_isempty(h->flags, i)) x = i; \ - else { \ - inc = 1 + k % (h->n_buckets - 1); last = i; \ - while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ - if (__ac_isdel(h->flags, i)) site = i; \ - if (i + inc >= h->n_buckets) i = i + inc - h->n_buckets; \ - else i += inc; \ - if (i == last) { x = site; break; } \ - } \ - if (x == h->n_buckets) { \ - if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \ - else x = i; \ - } \ - } \ - } \ - if (__ac_isempty(h->flags, x)) { \ - h->keys[x] = key; \ - __ac_set_isboth_false(h->flags, x); \ - ++h->size; ++h->n_occupied; \ - *ret = 1; \ - } else if (__ac_isdel(h->flags, x)) { \ - h->keys[x] = key; \ - __ac_set_isboth_false(h->flags, x); \ - ++h->size; \ - *ret = 2; \ - } else *ret = 0; \ - return x; \ - } \ - static inline void kh_del_##name(kh_##name##_t *h, khint_t x) \ - { \ - if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \ - __ac_set_isdel_true(h->flags, x); \ - --h->size; \ - } \ - } - -/* --- BEGIN OF HASH FUNCTIONS --- */ - -#define kh_int_hash_func(key) (uint32_t)(key) -#define kh_int_hash_equal(a, b) (a == b) -#define kh_int64_hash_func(key) (uint32_t)((key)>>33^(key)^(key)<<11) -#define kh_int64_hash_equal(a, b) (a == b) -static inline khint_t __ac_X31_hash_string(const char *s) -{ - khint_t h = *s; - if (h) for (++s ; *s; ++s) h = (h << 5) - h + *s; - return h; -} -#define kh_str_hash_func(key) __ac_X31_hash_string(key) -#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0) - -/* --- END OF HASH FUNCTIONS --- */ - -/* Other necessary macros... */ - -#define khash_t(name) kh_##name##_t - -#define kh_init(name) kh_init_##name() -#define kh_destroy(name, h) kh_destroy_##name(h) -#define kh_clear(name, h) kh_clear_##name(h) -#define kh_resize(name, h, s) kh_resize_##name(h, s) -#define kh_put(name, h, k, r) kh_put_##name(h, k, r) -#define kh_get(name, h, k) kh_get_##name(h, k) -#define kh_del(name, h, k) kh_del_##name(h, k) - -#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x))) -#define kh_key(h, x) ((h)->keys[x]) -#define kh_val(h, x) ((h)->vals[x]) -#define kh_value(h, x) ((h)->vals[x]) -#define kh_begin(h) (khint_t)(0) -#define kh_end(h) ((h)->n_buckets) -#define kh_size(h) ((h)->size) -#define kh_n_buckets(h) ((h)->n_buckets) - -/* More conenient interfaces */ - -#define KHASH_SET_INIT_INT(name) \ - KHASH_INIT(name, uint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal) - -#define KHASH_MAP_INIT_INT(name, khval_t) \ - KHASH_INIT(name, uint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal) - -#define KHASH_SET_INIT_INT64(name) \ - KHASH_INIT(name, uint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal) - -#define KHASH_MAP_INIT_INT64(name, khval_t) \ - KHASH_INIT(name, uint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal) - -typedef const char *kh_cstr_t; -#define KHASH_SET_INIT_STR(name) \ - KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal) - -#define KHASH_MAP_INIT_STR(name, khval_t) \ - KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal) - -#endif /* __AC_KHASH_H */ diff --git a/tommyds/benchmark/lib/nedtries/nedtrie.h b/tommyds/benchmark/lib/nedtries/nedtrie.h deleted file mode 100644 index c68d0af..0000000 --- a/tommyds/benchmark/lib/nedtries/nedtrie.h +++ /dev/null @@ -1,1642 +0,0 @@ -/* An in-place binary trie implementation for C and C++ aka. the -ridiculously fast way of indexing stuff. (C) 2010 Niall Douglas. - - -Boost Software License - Version 1.0 - August 17th, 2003 - -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, FILE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. -*/ - -#include -#include - -/*! \def RESTRICT -\brief Defined to the restrict keyword or equivalent if available */ -#ifndef RESTRICT -#if __STDC_VERSION__ >= 199901L /* C99 or better */ - #define RESTRICT restrict -#else - #if defined(_MSC_VER) && _MSC_VER>=1400 - #define RESTRICT __restrict - #endif - #ifdef __GNUC__ - #define RESTRICT __restrict - #endif -#endif -#ifndef RESTRICT - #define RESTRICT -#endif -#endif - -/*! \def INLINE -\brief Defined to the inline keyword or equivalent if available */ -#ifndef INLINE -#if __STDC_VERSION__ >= 199901L /* C99 or better */ || defined(__cplusplus) - #define INLINE inline -#else - #if defined(_MSC_VER) - #define INLINE __inline - #endif - #ifdef __GNUC__ - #define INLINE __inline - #endif -#endif -#ifndef INLINE - #define INLINE -#endif -#endif - -/*! \def NOINLINE -\brief Defined to whatever compiler magic inhibits inlining if available */ -#ifndef NOINLINE - #if defined(__GNUC__) - #define NOINLINE __attribute__ ((noinline)) - #elif defined(_MSC_VER) - #define NOINLINE __declspec(noinline) - #else - #define NOINLINE - #endif -#endif - -/*! \def DEBUGINLINE -\brief Defined to be INLINE when NDEBUG is defined, NOINLINE when DEBUG is defined, unset otherwise. -*/ -#ifndef DEBUGINLINE -#ifdef NDEBUG -#define DEBUGINLINE INLINE -#elif defined(DEBUG) -#define DEBUGINLINE NOINLINE -#else -#define DEBUGINLINE -#endif -#endif - -/*! \def NEDTRIEUSEMACROS -\brief Define to 1 to force usage of the macro implementation of nedtries. This is always 1 when -compiling in C, but defaults to 0 when compiling in C++ as a template function implementation offers -much more scope to the optimiser and is much easier to debug. -*/ -#ifndef NEDTRIEUSEMACROS -#ifdef __NO__cplusplus -#define NEDTRIEUSEMACROS 0 -#else -#define NEDTRIEUSEMACROS 1 -#endif -#endif - -/*! \def NEDTRIEDEBUG -\brief Define to 1 if you wish a full trie validation to be performed every time you modify the trie. -Requires assert() to work, so disables itself if NDEBUG is defined. -*/ -#ifndef NEDTRIEDEBUG -#ifdef DEBUG -#define NEDTRIEDEBUG 1 -#else -#define NEDTRIEDEBUG 0 -#endif -#endif -#ifdef NDEBUG -#undef NEDTRIEDEBUG -#define NEDTRIEDEBUG 0 -#endif - -/* Define bit scanning intrinsics */ -#ifdef _MSC_VER -#include -#endif - -#ifdef __NO__cplusplus -#include -namespace { -#endif -static INLINE unsigned nedtriebitscanr(size_t value) -{ -#ifdef _MSC_VER - unsigned long bitpos; -#if defined(_M_IA64) || defined(_M_X64) || defined(WIN64) - assert(8==sizeof(size_t)); - _BitScanReverse64(&bitpos, value | 1); -#else - assert(4==sizeof(size_t)); - _BitScanReverse(&bitpos, value | 1); -#endif - return (unsigned) bitpos; -#elif defined(__GNUC__) - return sizeof(value)*__CHAR_BIT__ - 1 - (unsigned) __builtin_clz(value | 1); -#else - /* The following code is illegal C, but it almost certainly will work. - If not use the legal implementation below */ - unsigned bitpos; -#if 1 - union { - unsigned asInt[2]; - double asDouble; - }; - int n; - - asDouble = (double)value + 0.5; - n = (asInt[0 /*Use 1 if your CPU is big endian!*/] >> 20) - 1023; -#warning Make sure you change the line above me if your CPU is big endian! - return (unsigned) n; -#else - size_t x=value; - x = x | (x >> 1); - x = x | (x >> 2); - x = x | (x >> 4); - x = x | (x >> 8); - x = x | (x >>16); - x = ~x; - x = x - ((x >> 1) & 0x55555555); - x = (x & 0x33333333) + ((x >> 2) & 0x33333333); - x = (x + (x >> 4)) & 0x0F0F0F0F; - x = x + (x << 8); - x = x + (x << 16); - return x >> 24; -#endif -#endif -} - -#ifdef __NO__cplusplus -} /* Anonymous namespace */ -#endif - -/*! \def NEDTRIE_INDEXBINS -\brief Defines the number of top level bit bins to use. The default based on size_t is usually fine. -*/ -#define NEDTRIE_INDEXBINS (8*sizeof(void *)) -/*! \def NEDTRIE_HEAD -\brief Substitutes the type used to store the head of the trie. -*/ -#define NEDTRIE_HEAD2(name, type) \ -struct name { \ - size_t count; \ - type *triebins[NEDTRIE_INDEXBINS]; /* each containing (1<count) -/*! \def NEDTRIE_COUNT -\brief Returns the number of items in a nedtrie. -*/ -#define NEDTRIE_COUNT(head) ((head)->count) - -/* As macro instantiated code is a royal PITA to debug and even to see what -the hell is going on, we use a templated implementation when in C++. This -aids future debuggability by keeping the template and macro implementations -side by side and hopefully harmonised. */ -#ifdef __NO__cplusplus -namespace nedtries { - - template int trienobblezeros(trietype *head) - { - return 0; - } - template int trienobbleones(trietype *head) - { - return 1; - } - template int trienobbleequally(trietype *head) - { - return (head->nobbledir=!head->nobbledir); - } -/*! \def NEDTRIE_NOBBLEZEROS -\brief A nobble function which preferentially nobbles zeros. -*/ -#define NEDTRIE_NOBBLEZEROS(name) nedtries::trienobblezeros -/*! \def NEDTRIE_NOBBLEONES -\brief A nobble function which preferentially nobbles ones. -*/ -#define NEDTRIE_NOBBLEONES(name) nedtries::trienobbleones -/*! \def NEDTRIE_NOBBLEEQUALLY -\brief A nobble function which alternates between nobbling zeros and ones. -*/ -#define NEDTRIE_NOBBLEEQUALLY(name) nedtries::trienobbleequally -#define NEDTRIE_GENERATE_NOBBLES(proto, name, type, field, keyfunct) -#else -#define NEDTRIE_NOBBLEZEROS(name) name##_nobblezeros -#define NEDTRIE_NOBBLEONES(name) name##_nobbleones -#define NEDTRIE_NOBBLEEQUALLY(name) name##_nobbleequally -#define NEDTRIE_GENERATE_NOBBLES(proto, name, type, field, keyfunct) \ - static INLINE int name##_nobblezeros(struct name *head) { return 0; } \ - static INLINE int name##_nobbleones(struct name *head) { return 1; } \ - static INLINE int name##_nobbleequally(struct name *head) { return (head->nobbledir=!head->nobbledir); } -#endif /* __NO__cplusplus */ - -#ifdef __NO__cplusplus - template struct TrieLink_t { - type *trie_parent; /* parent element */ - type *trie_child[2]; /* my children based on whether they are zero or one. */ - type *trie_prev, *trie_next; /* my siblings of identical key to me. */ - }; - template DEBUGINLINE void triecheckvalidity(trietype *head); - -} /* namespace */ -#endif - -/* GCC recently has started puking if you use operators -> and & in template parameters :( */ -#ifdef __GNUC__ -#define NEDTRIEFIELDOFFSET2(type, field) __builtin_offsetof(type, field) -#else -#define NEDTRIEFIELDOFFSET2(type, field) ((size_t) &(((type *)0)->field)) -#endif -#define NEDTRIEFIELDOFFSET(type, field) NEDTRIEFIELDOFFSET2(struct type, field) - -#ifdef __NO__cplusplus -namespace nedtries { - template DEBUGINLINE void trieinsert(trietype *RESTRICT head, type *RESTRICT r) - { - type *RESTRICT node, *RESTRICT childnode; - TrieLink_t *RESTRICT nodelink, *RESTRICT rlink; - size_t rkey=keyfunct(r), keybit, nodekey; - unsigned bitidx; - int keybitset; - - rlink=(TrieLink_t *RESTRICT)((size_t) r + fieldoffset); - memset(rlink, 0, sizeof(TrieLink_t)); - bitidx=nedtriebitscanr(rkey); - assert(bitidxtriebins[bitidx])) - { /* Bottom two bits set indicates a node hanging off of head */ - rlink->trie_parent=(type *RESTRICT)(size_t)(3|(bitidx<<2)); - head->triebins[bitidx]=r; - goto end; - } - /* Avoid variable bit shifts where possible, their performance can suck */ - keybit=(size_t) 1< *RESTRICT)((size_t) node + fieldoffset); - nodekey=keyfunct(node); - if(nodekey==rkey) - { /* Insert into ring list */ - rlink->trie_parent=0; - rlink->trie_prev=node; - rlink->trie_next=nodelink->trie_next; - nodelink->trie_next=r; - if(rlink->trie_next) ((TrieLink_t *RESTRICT)((size_t) rlink->trie_next + fieldoffset))->trie_prev=r; - break; - } - keybit>>=1; - keybitset=!!(rkey&keybit); - childnode=nodelink->trie_child[keybitset]; - if(!childnode) - { /* Insert here */ - rlink->trie_parent=node; - nodelink->trie_child[keybitset]=r; - break; - } - } -end: - head->count++; -#if NEDTRIEDEBUG - triecheckvalidity(head); -#endif - } -} -#endif /* __NO__cplusplus */ -#if NEDTRIEUSEMACROS -#define NEDTRIE_GENERATE_INSERT(proto, name, type, field, keyfunct) \ - proto INLINE void name##_NEDTRIE_INSERT(struct name *RESTRICT head, struct type *RESTRICT r) \ - { \ - struct type *RESTRICT node, *RESTRICT childnode; \ - size_t rkey=keyfunct(r), keybit, nodekey; \ - unsigned bitidx; \ - int keybitset; \ -\ - memset(&r->field, 0, sizeof(r->field)); \ - bitidx=nedtriebitscanr(rkey); \ - assert(bitidxtriebins[bitidx])) \ - { /* Bottom two bits set indicates a node hanging off of head */ \ - r->field.trie_parent=(struct type *RESTRICT)(size_t)(3|(bitidx<<2)); \ - head->triebins[bitidx]=r; \ - goto end; \ - } \ - /* Avoid variable bit shifts where possible, their performance can suck */ \ - keybit=(size_t) 1<field.trie_parent=0; \ - r->field.trie_prev=node; \ - r->field.trie_next=node->field.trie_next; \ - node->field.trie_next=r; \ - if(r->field.trie_next) r->field.trie_next->field.trie_prev=r; \ - break; \ - } \ - keybit>>=1; \ - keybitset=!!(rkey&keybit); \ - childnode=node->field.trie_child[keybitset]; \ - if(!childnode) \ - { /* Insert here */ \ - r->field.trie_parent=node; \ - node->field.trie_child[keybitset]=r; \ - break; \ - } \ - } \ -end: \ - head->count++; \ - } -#else /* NEDTRIEUSEMACROS */ -#define NEDTRIE_GENERATE_INSERT(proto, name, type, field, keyfunct) \ - proto INLINE void name##_NEDTRIE_INSERT(struct name *RESTRICT head, struct type *RESTRICT r) \ -{ \ - nedtries::trieinsert(head, r); \ -} -#endif /* NEDTRIEUSEMACROS */ - -#ifdef __NO__cplusplus -namespace nedtries { - template DEBUGINLINE void trieremove(trietype *RESTRICT head, type *RESTRICT r) - { - type *RESTRICT node, **myaddrinparent=0; - TrieLink_t *RESTRICT nodelink, *RESTRICT childlink, *RESTRICT rlink; - unsigned bitidx; - - rlink=(TrieLink_t *RESTRICT)((size_t) r + fieldoffset); - /* Am I a leaf off the tree? */ - if(rlink->trie_prev) - { /* Remove from linked list */ - assert(!rlink->trie_parent); - node=rlink->trie_prev; - nodelink=(TrieLink_t *RESTRICT)((size_t) node + fieldoffset); - nodelink->trie_next=rlink->trie_next; - if(rlink->trie_next) - { - nodelink=(TrieLink_t *RESTRICT)((size_t) rlink->trie_next + fieldoffset); - nodelink->trie_prev=node; - } - goto functexit; - } - /* I must therefore be part of the tree */ - assert(rlink->trie_parent); - assert(!rlink->trie_prev); - /* Am I at the top of the tree? */ - if(((size_t) rlink->trie_parent & 3)==3) - { /* Extract my bitidx */ - bitidx=(unsigned)(((size_t) rlink->trie_parent)>>2); - assert(head->triebins[bitidx]==r); - /* Set the node addr to be modified */ - myaddrinparent=&head->triebins[bitidx]; - } - else - { /* Otherwise I am one of my parent's children */ - node=rlink->trie_parent; - nodelink=(TrieLink_t *RESTRICT)((size_t) node + fieldoffset); - myaddrinparent=(nodelink->trie_child[0]==r) ? &nodelink->trie_child[0] : &nodelink->trie_child[1]; - } - assert(*myaddrinparent==r); - node=0; - /* Can I replace me with a sibling? */ - if(rlink->trie_next) - { - node=rlink->trie_next; - nodelink=(TrieLink_t *RESTRICT)((size_t) node + fieldoffset); - assert(nodelink->trie_prev==r); - nodelink->trie_prev=0; - goto end; - } - /* Can I simply remove myself from my parent? */ - if(!rlink->trie_child[0] && !rlink->trie_child[1]) - goto end; - /* I need someone to replace me in the trie, so simply find any - grandchild of mine (who has the right bits to be here) which has no children. - */ - { - type *RESTRICT *RESTRICT childaddrinparent=myaddrinparent, *RESTRICT *RESTRICT newchildaddrinparent; - int nobbledir=nobblefunct(head); - while(*(newchildaddrinparent=&(((TrieLink_t *RESTRICT)((size_t) *childaddrinparent + fieldoffset))->trie_child[nobbledir])) - || *(newchildaddrinparent=&(((TrieLink_t *RESTRICT)((size_t) *childaddrinparent + fieldoffset))->trie_child[!nobbledir]))) - childaddrinparent=newchildaddrinparent; - node=*childaddrinparent; - *childaddrinparent=0; - } - end: - if(node) - { - nodelink=(TrieLink_t *RESTRICT)((size_t) node + fieldoffset); - assert(!nodelink->trie_child[0] && !nodelink->trie_child[1]); - nodelink->trie_parent=rlink->trie_parent; - nodelink->trie_child[0]=rlink->trie_child[0]; - nodelink->trie_child[1]=rlink->trie_child[1]; - if(nodelink->trie_child[0]) - { - childlink=(TrieLink_t *RESTRICT)((size_t) nodelink->trie_child[0] + fieldoffset); - childlink->trie_parent=node; - } - if(nodelink->trie_child[1]) - { - childlink=(TrieLink_t *RESTRICT)((size_t) nodelink->trie_child[1] + fieldoffset); - childlink->trie_parent=node; - } - } - *myaddrinparent=node; - functexit: - head->count--; -#if NEDTRIEDEBUG - triecheckvalidity(head); -#endif - } -} -#endif /* __NO__cplusplus */ -#if NEDTRIEUSEMACROS -#define NEDTRIE_GENERATE_REMOVE(proto, name, type, field, keyfunct, nobblefunct) \ - proto INLINE void name##_NEDTRIE_REMOVE(struct name *RESTRICT head, struct type *RESTRICT r) \ - { \ - struct type *RESTRICT node, **myaddrinparent=0; \ - unsigned bitidx; \ -\ - /* Am I a leaf off the tree? */ \ - if(r->field.trie_prev) \ - { /* Remove from linked list */ \ - assert(!r->field.trie_parent); \ - node=r->field.trie_prev; \ - node->field.trie_next=r->field.trie_next; \ - if(r->field.trie_next) \ - { \ - r->field.trie_next->field.trie_prev=node; \ - } \ - goto functexit; \ - } \ - /* I must therefore be part of the tree */ \ - assert(r->field.trie_parent); \ - assert(!r->field.trie_prev); \ - /* Am I at the top of the tree? */ \ - if(((size_t) r->field.trie_parent & 3)==3) \ - { /* Extract my bitidx */ \ - bitidx=(unsigned)(((size_t) r->field.trie_parent)>>2); \ - assert(head->triebins[bitidx]==r); \ - /* Set the node addr to be modified */ \ - myaddrinparent=&head->triebins[bitidx]; \ - } \ - else \ - { /* Otherwise I am one of my parent's children */ \ - node=r->field.trie_parent; \ - myaddrinparent=(node->field.trie_child[0]==r) ? &node->field.trie_child[0] : &node->field.trie_child[1]; \ - } \ - assert(*myaddrinparent==r); \ - node=0; \ - /* Can I replace me with a sibling? */ \ - if(r->field.trie_next) \ - { \ - node=r->field.trie_next; \ - assert(node->field.trie_prev==r); \ - node->field.trie_prev=0; \ - goto end; \ - } \ - /* Can I simply remove myself from my parent? */ \ - if(!r->field.trie_child[0] && !r->field.trie_child[1]) \ - goto end; \ - /* I need someone to replace me in the trie, so simply find any \ - grandchild of mine (who has the right bits to be here) which has no children. \ - */ \ - { \ - struct type *RESTRICT *RESTRICT childaddrinparent=myaddrinparent, *RESTRICT *RESTRICT newchildaddrinparent; \ - int nobbledir=nobblefunct(head); \ - while(*(newchildaddrinparent=&(*childaddrinparent)->field.trie_child[nobbledir]) \ - || *(newchildaddrinparent=&(*childaddrinparent)->field.trie_child[!nobbledir])) \ - childaddrinparent=newchildaddrinparent; \ - node=*childaddrinparent; \ - *childaddrinparent=0; \ - } \ - end: \ - if(node) \ - { \ - assert(!node->field.trie_child[0] && !node->field.trie_child[1]); \ - node->field.trie_parent=r->field.trie_parent; \ - node->field.trie_child[0]=r->field.trie_child[0]; \ - node->field.trie_child[1]=r->field.trie_child[1]; \ - if(node->field.trie_child[0]) \ - { \ - node->field.trie_child[0]->field.trie_parent=node; \ - } \ - if(node->field.trie_child[1]) \ - { \ - node->field.trie_child[1]->field.trie_parent=node; \ - } \ - } \ - *myaddrinparent=node; \ - functexit: \ - head->count--; \ - } -#else /* NEDTRIEUSEMACROS */ -#define NEDTRIE_GENERATE_REMOVE(proto, name, type, field, keyfunct, nobblefunct) \ - proto INLINE void name##_NEDTRIE_REMOVE(struct name *RESTRICT head, struct type *RESTRICT r) \ -{ \ - nedtries::trieremove(head, r); \ -} -#endif /* NEDTRIEUSEMACROS */ - -#ifdef __NO__cplusplus -namespace nedtries { - template DEBUGINLINE type *triefind(const trietype *RESTRICT head, const type *RESTRICT r) - { - const type *RESTRICT node, *RESTRICT childnode; - const TrieLink_t *RESTRICT nodelink, *RESTRICT rlink; - size_t rkey=keyfunct(r), keybit, nodekey; - unsigned bitidx; - int keybitset; - - if(!head->count) return 0; - rlink=(const TrieLink_t *RESTRICT)((size_t) r + fieldoffset); - bitidx=nedtriebitscanr(rkey); - assert(bitidxtriebins[bitidx])) - return 0; - /* Avoid variable bit shifts where possible, their performance can suck */ - keybit=(size_t) 1< *RESTRICT)((size_t) node + fieldoffset); - nodekey=keyfunct(node); - if(nodekey==rkey) - goto end; - keybit>>=1; - keybitset=!!(rkey&keybit); - childnode=nodelink->trie_child[keybitset]; - if(!childnode) - return 0; - } - return 0; - end: - return nodelink->trie_next ? nodelink->trie_next : (type *) node; - } -} -#endif /* __NO__cplusplus */ -#if NEDTRIEUSEMACROS -#define NEDTRIE_GENERATE_FIND(proto, name, type, field, keyfunct) \ - proto INLINE struct type * name##_NEDTRIE_FIND(struct name *RESTRICT head, struct type *RESTRICT r) \ - { \ - struct type *RESTRICT node, *RESTRICT childnode; \ - size_t rkey=keyfunct(r), keybit, nodekey; \ - unsigned bitidx; \ - int keybitset; \ -\ - if(!head->count) return 0; \ - bitidx=nedtriebitscanr(rkey); \ - assert(bitidxtriebins[bitidx])) \ - return 0; \ - /* Avoid variable bit shifts where possible, their performance can suck */ \ - keybit=(size_t) 1<>=1; \ - keybitset=!!(rkey&keybit); \ - childnode=node->field.trie_child[keybitset]; \ - if(!childnode) \ - return 0; \ - } \ - return 0; \ - end: \ - return node->field.trie_next ? node->field.trie_next : node; \ - } -#else /* NEDTRIEUSEMACROS */ -#define NEDTRIE_GENERATE_FIND(proto, name, type, field, keyfunct) \ - proto INLINE struct type * name##_NEDTRIE_FIND(struct name *RESTRICT head, struct type *RESTRICT r) \ -{ \ - return nedtries::triefind(head, r); \ -} -#endif /* NEDTRIEUSEMACROS */ - -#ifdef __NO__cplusplus -namespace nedtries { - template DEBUGINLINE int trieexactfind(const trietype *RESTRICT head, const type *RESTRICT r) - { - const type *RESTRICT node; - const TrieLink_t *RESTRICT nodelink; - - if(!head->count) return 0; - if(!(node=triefind(head, r))) return 0; - nodelink=(const TrieLink_t *RESTRICT)((size_t) node + fieldoffset); - if(nodelink->trie_prev) node=nodelink->trie_prev; - do - { - if(node==r) return 1; - nodelink=(const TrieLink_t *RESTRICT)((size_t) node + fieldoffset); - node=nodelink->trie_next; - } while(node); - return 0; - } -} -#endif /* __NO__cplusplus */ -#if NEDTRIEUSEMACROS -#define NEDTRIE_GENERATE_EXACTFIND(proto, name, type, field, keyfunct) \ - proto INLINE int name##_NEDTRIE_EXACTFIND(struct name *RESTRICT head, struct type *RESTRICT r) \ - { \ - struct type *RESTRICT node; \ -\ - if(!head->count) return 0; \ - if(!(node=name##_NEDTRIE_FIND(head, r))) return 0; \ - if(node->field.trie_prev) node=node->field.trie_prev; \ - do \ - { \ - if(node==r) return 1; \ - node=node->field.trie_next; \ - } while(node); \ - return 0; \ - } -#else /* NEDTRIEUSEMACROS */ -#define NEDTRIE_GENERATE_EXACTFIND(proto, name, type, field, keyfunct) \ - proto INLINE int name##_NEDTRIE_EXACTFIND(struct name *RESTRICT head, struct type *RESTRICT r) \ -{ \ - return nedtries::trieexactfind(head, r); \ -} -#endif /* NEDTRIEUSEMACROS */ - -#ifdef __NO__cplusplus -namespace nedtries { - template DEBUGINLINE type *trieNfind(const trietype *RESTRICT head, const type *RESTRICT r) - { - const type *RESTRICT node=0, *RESTRICT childnode, *RESTRICT ret=0; - const TrieLink_t *RESTRICT nodelink, *RESTRICT rlink; - size_t rkey=keyfunct(r), keybit, nodekey; - unsigned binbitidx; - int keybitset; - - if(!head->count) return 0; - rlink=(const TrieLink_t *RESTRICT)((size_t) r + fieldoffset); - binbitidx=nedtriebitscanr(rkey); - assert(binbitidxtriebins[binbitidx])) - binbitidx++; - if(binbitidx>=NEDTRIE_INDEXBINS) - return 0; - bitidx=binbitidx; - /* Avoid variable bit shifts where possible, their performance can suck */ - keybit=(size_t) 1< *RESTRICT)((size_t) node + fieldoffset); - nodekey=keyfunct(node); - if(nodekey>=rkey && nodekey-rkey>=1; - keybitset=!!(rkey&keybit); - childnode=nodelink->trie_child[keybitset]; - if(!childnode) - break; - } - if(!ret) - { /* If we didn't find any node bigger than rkey, bump up a bin - and look for the smallest possible key in that */ - binbitidx++; - rkey=0; - continue; - } - } while(!ret); - end: - nodelink=(const TrieLink_t *RESTRICT)((size_t) ret + fieldoffset); - return nodelink->trie_next ? nodelink->trie_next : (type *) ret; - } -} -#endif /* __NO__cplusplus */ -#if NEDTRIEUSEMACROS -#define NEDTRIE_GENERATE_NFIND(proto, name, type, field, keyfunct) \ - proto INLINE struct type * name##_NEDTRIE_NFIND(struct name *RESTRICT head, struct type *RESTRICT r) \ - { \ - struct type *RESTRICT node=0, *RESTRICT childnode, *RESTRICT ret=0; \ - size_t rkey=keyfunct(r), keybit, nodekey; \ - unsigned binbitidx; \ - int keybitset; \ - \ - if(!head->count) return 0; \ - binbitidx=nedtriebitscanr(rkey); \ - assert(binbitidxtriebins[binbitidx])) \ - binbitidx++; \ - if(binbitidx>=NEDTRIE_INDEXBINS) \ - return 0; \ - bitidx=binbitidx; \ - /* Avoid variable bit shifts where possible, their performance can suck */ \ - keybit=(size_t) 1<=rkey && nodekey-rkey>=1; \ - keybitset=!!(rkey&keybit); \ - childnode=node->field.trie_child[keybitset]; \ - if(!childnode) \ - break; \ - } \ - if(!ret) \ - { /* If we didn't find any node bigger than rkey, bump up a bin \ - and look for the smallest possible key in that */ \ - binbitidx++; \ - rkey=0; \ - continue; \ - } \ - } while(!ret); \ - end: \ - return ret->field.trie_next ? ret->field.trie_next : ret; \ - } -#else /* NEDTRIEUSEMACROS */ -#define NEDTRIE_GENERATE_NFIND(proto, name, type, field, keyfunct) \ - proto INLINE struct type * name##_NEDTRIE_NFIND(struct name *RESTRICT head, struct type *RESTRICT r) \ -{ \ - return nedtries::trieNfind(head, r); \ -} -#endif /* NEDTRIEUSEMACROS */ - -#ifdef __NO__cplusplus -namespace nedtries { - template DEBUGINLINE type *trieminmax(const trietype *RESTRICT head, unsigned dir) - { - const type *RESTRICT node=0, *RESTRICT child; - const TrieLink_t *RESTRICT nodelink; - unsigned bitidx; - if(!head->count) return 0; - if(!dir) - { /* He wants min */ - for(bitidx=0; bitidxtriebins[bitidx]); bitidx++); - assert(node); - return (type *) node; - } - /* He wants max */ - for(bitidx=NEDTRIE_INDEXBINS-1; bitidxtriebins[bitidx]); bitidx--); - assert(node); - nodelink=(const TrieLink_t *RESTRICT)((size_t) node + fieldoffset); - while((child=nodelink->trie_child[1] ? nodelink->trie_child[1] : nodelink->trie_child[0])) - { - node=child; - nodelink=(const TrieLink_t *RESTRICT)((size_t) node + fieldoffset); - } - /* Now go to end leaf */ - while(nodelink->trie_next) - { - node=nodelink->trie_next; - nodelink=(const TrieLink_t *RESTRICT)((size_t) node + fieldoffset); - } - return (type *) node; - } -} -#endif /* __NO__cplusplus */ -#if NEDTRIEUSEMACROS -#define NEDTRIE_GENERATE_MINMAX(proto, name, type, field, keyfunct) \ - proto INLINE struct type * name##_NEDTRIE_MINMAX(struct name *RESTRICT head, unsigned dir) \ - { \ - struct type *RESTRICT node=0, *RESTRICT child; \ - unsigned bitidx; \ - if(!head->count) return 0; \ - if(!dir) \ - { /* He wants min */ \ - for(bitidx=0; bitidxtriebins[bitidx]); bitidx++); \ - assert(node); \ - return node; \ - } \ - /* He wants max */ \ - for(bitidx=NEDTRIE_INDEXBINS-1; bitidxtriebins[bitidx]); bitidx--); \ - assert(node); \ - while((child=node->field.trie_child[1] ? node->field.trie_child[1] : node->field.trie_child[0])) \ - { \ - node=child; \ - } \ - /* Now go to end leaf */ \ - while(node->field.trie_next) \ - { \ - node=node->field.trie_next; \ - } \ - return node; \ - } -#else /* NEDTRIEUSEMACROS */ -#define NEDTRIE_GENERATE_MINMAX(proto, name, type, field, keyfunct) \ - proto INLINE struct type * name##_NEDTRIE_MINMAX(struct name *RESTRICT head, unsigned dir) \ -{ \ - return nedtries::trieminmax(head, dir); \ -} -#endif /* NEDTRIEUSEMACROS */ - -#ifdef __NO__cplusplus -namespace nedtries { - template DEBUGINLINE type *trieprev(const trietype *RESTRICT head, const type *RESTRICT r) - { - const type *RESTRICT node=0, *RESTRICT child; - const TrieLink_t *RESTRICT nodelink, *RESTRICT rlink; - unsigned bitidx; - - rlink=(TrieLink_t *RESTRICT)((size_t) r + fieldoffset); - /* Am I a leaf off the tree? */ - if(rlink->trie_prev) - { - assert(!rlink->trie_parent); - return rlink->trie_prev; - } - /* Trace up my parents to prev branch */ - while(((size_t) rlink->trie_parent & 3)!=3) - { - node=rlink->trie_parent; - nodelink=(const TrieLink_t *RESTRICT)((size_t) node + fieldoffset); - /* If I was on child[1] and there is a child[0], go to bottom of child[0] */ - if(nodelink->trie_child[1]==r && nodelink->trie_child[0]) - { - node=nodelink->trie_child[0]; - goto returnbottomofchild; - } - /* If I was already on child[0] or there are no more children, return this node */ - goto returnendleaf; - } - /* I have reached the top of my trie, so on to prev bin */ - bitidx=(unsigned)(((size_t) rlink->trie_parent)>>2); - assert(head->triebins[bitidx]==r); - for(bitidx--; bitidxtriebins[bitidx]); bitidx--); - if(bitidx>=NEDTRIE_INDEXBINS) return 0; - returnbottomofchild: - nodelink=(const TrieLink_t *RESTRICT)((size_t) node + fieldoffset); - /* Follow child[1] preferentially downwards */ - while((child=nodelink->trie_child[1] ? nodelink->trie_child[1] : nodelink->trie_child[0])) - { - node=child; - nodelink=(const TrieLink_t *RESTRICT)((size_t) node + fieldoffset); - } - returnendleaf: - /* Now go to end leaf */ - while(nodelink->trie_next) - { - node=nodelink->trie_next; - nodelink=(const TrieLink_t *RESTRICT)((size_t) node + fieldoffset); - } - return (type *) node; - } -} -#endif /* __NO__cplusplus */ -#if NEDTRIEUSEMACROS -#define NEDTRIE_GENERATE_PREV(proto, name, type, field, keyfunct) \ - proto INLINE struct type * name##_NEDTRIE_PREV(struct name *RESTRICT head, struct type *RESTRICT r) \ - { \ - struct type *RESTRICT node=0, *RESTRICT child; \ - unsigned bitidx; \ -\ - /* Am I a leaf off the tree? */ \ - if(r->field.trie_prev) \ - { \ - assert(!r->field.trie_parent); \ - return r->field.trie_prev; \ - } \ - /* Trace up my parents to prev branch */ \ - while(((size_t) r->field.trie_parent & 3)!=3) \ - { \ - node=r->field.trie_parent; \ - /* If I was on child[1] and there is a child[0], go to bottom of child[0] */ \ - if(node->field.trie_child[1]==r && node->field.trie_child[0]) \ - { \ - node=node->field.trie_child[0]; \ - goto returnbottomofchild; \ - } \ - /* If I was already on child[0] or there are no more children, return this node */ \ - goto returnendleaf; \ - } \ - /* I have reached the top of my trie, so on to prev bin */ \ - bitidx=(unsigned)(((size_t) r->field.trie_parent)>>2); \ - assert(head->triebins[bitidx]==r); \ - for(bitidx--; bitidxtriebins[bitidx]); bitidx--); \ - if(bitidx>=NEDTRIE_INDEXBINS) return 0; \ - returnbottomofchild: \ - /* Follow child[1] preferentially downwards */ \ - while((child=node->field.trie_child[1] ? node->field.trie_child[1] : node->field.trie_child[0])) \ - { \ - node=child; \ - } \ - returnendleaf: \ - /* Now go to end leaf */ \ - while(node->field.trie_next) \ - { \ - node=node->field.trie_next; \ - } \ - return node; \ - } -#else /* NEDTRIEUSEMACROS */ -#define NEDTRIE_GENERATE_PREV(proto, name, type, field, keyfunct) \ - proto INLINE struct type * name##_NEDTRIE_PREV(struct name *RESTRICT head, struct type *RESTRICT r) \ -{ \ - return nedtries::trieprev(head, r); \ -} -#endif /* NEDTRIEUSEMACROS */ - -#ifdef __NO__cplusplus -namespace nedtries { - template DEBUGINLINE type *trienext(const trietype *RESTRICT head, const type *RESTRICT r) - { - const type *RESTRICT node; - const TrieLink_t *RESTRICT nodelink, *RESTRICT rlink; - unsigned bitidx; - - rlink=(const TrieLink_t *RESTRICT)((size_t) r + fieldoffset); - /* Am I a leaf off the tree? */ - if(rlink->trie_next) - return rlink->trie_next; - /* If I am the end leaf off a tree, put me back at my tree node */ - while(!rlink->trie_parent) - { - r=rlink->trie_prev; - rlink=(const TrieLink_t *RESTRICT)((size_t) r + fieldoffset); - } - /* Follow my children, preferring child[0] */ - if((node=rlink->trie_child[0] ? rlink->trie_child[0] : rlink->trie_child[1])) - { - nodelink=(const TrieLink_t *RESTRICT)((size_t) node + fieldoffset); - assert(nodelink->trie_parent==r); - return (type *) node; - } - /* Trace up my parents to next branch */ - while(((size_t) rlink->trie_parent & 3)!=3) - { - node=rlink->trie_parent; - nodelink=(const TrieLink_t *RESTRICT)((size_t) node + fieldoffset); - if(nodelink->trie_child[0]==r && nodelink->trie_child[1]) - { - return nodelink->trie_child[1]; - } - r=node; - rlink=nodelink; - } - /* I have reached the top of my trie, so on to next bin */ - bitidx=(unsigned)(((size_t) rlink->trie_parent)>>2); - assert(head->triebins[bitidx]==r); - for(bitidx++; bitidxtriebins[bitidx]); bitidx++); - if(bitidx>=NEDTRIE_INDEXBINS) return 0; - return (type *) node; - } -} -#endif /* __NO__cplusplus */ -#if NEDTRIEUSEMACROS -#define NEDTRIE_GENERATE_NEXT(proto, name, type, field, keyfunct) \ - proto INLINE struct type * name##_NEDTRIE_NEXT(struct name *RESTRICT head, struct type *RESTRICT r) \ - { \ - struct type *RESTRICT node; \ - unsigned bitidx; \ -\ - /* Am I a leaf off the tree? */ \ - if(r->field.trie_next) \ - return r->field.trie_next; \ - /* If I am the end leaf off a tree, put me back at my tree node */ \ - while(!r->field.trie_parent) \ - { \ - r=r->field.trie_prev; \ - } \ - /* Follow my children, preferring child[0] */ \ - if((node=r->field.trie_child[0] ? r->field.trie_child[0] : r->field.trie_child[1])) \ - { \ - assert(node->field.trie_parent==r); \ - return node; \ - } \ - /* Trace up my parents to next branch */ \ - while(((size_t) r->field.trie_parent & 3)!=3) \ - { \ - node=r->field.trie_parent; \ - if(node->field.trie_child[0]==r && node->field.trie_child[1]) \ - { \ - return node->field.trie_child[1]; \ - } \ - r=node; \ - } \ - /* I have reached the top of my trie, so on to next bin */ \ - bitidx=(unsigned)(((size_t) r->field.trie_parent)>>2); \ - assert(head->triebins[bitidx]==r); \ - for(bitidx++; bitidxtriebins[bitidx]); bitidx++); \ - if(bitidx>=NEDTRIE_INDEXBINS) return 0; \ - return node; \ - } -#else /* NEDTRIEUSEMACROS */ -#define NEDTRIE_GENERATE_NEXT(proto, name, type, field, keyfunct) \ - proto INLINE struct type * name##_NEDTRIE_NEXT(struct name *RESTRICT head, struct type *RESTRICT r) \ -{ \ - return nedtries::trienext(head, r); \ -} -#endif /* NEDTRIEUSEMACROS */ - - -/*! \def NEDTRIE_GENERATE -\brief Substitutes a set of nedtrie implementation function definitions specialised according to type. -*/ -#define NEDTRIE_GENERATE(proto, name, type, field, keyfunct, nobblefunct) \ - NEDTRIE_GENERATE_NOBBLES (proto, name, type, field, keyfunct) \ - NEDTRIE_GENERATE_INSERT (proto, name, type, field, keyfunct) \ - NEDTRIE_GENERATE_REMOVE (proto, name, type, field, keyfunct, nobblefunct) \ - NEDTRIE_GENERATE_FIND (proto, name, type, field, keyfunct) \ - NEDTRIE_GENERATE_EXACTFIND(proto, name, type, field, keyfunct) \ - NEDTRIE_GENERATE_NFIND (proto, name, type, field, keyfunct) \ - NEDTRIE_GENERATE_MINMAX (proto, name, type, field, keyfunct) \ - NEDTRIE_GENERATE_PREV (proto, name, type, field, keyfunct) \ - NEDTRIE_GENERATE_NEXT (proto, name, type, field, keyfunct) \ - proto INLINE struct type * name##_NEDTRIE_PREVLEAF(struct type *r) { return (r)->field.trie_prev; } \ - proto INLINE struct type * name##_NEDTRIE_NEXTLEAF(struct type *r) { return (r)->field.trie_next; } - -/*! \def NEDTRIE_INSERT -\brief Inserts item y into nedtrie x. -*/ -#define NEDTRIE_INSERT(name, x, y) name##_NEDTRIE_INSERT(x, y) -/*! \def NEDTRIE_REMOVE -\brief Removes item y from nedtrie x. -*/ -#define NEDTRIE_REMOVE(name, x, y) name##_NEDTRIE_REMOVE(x, y) -/*! \def NEDTRIE_FIND -\brief Finds the item with the same key as y in nedtrie x. -*/ -#define NEDTRIE_FIND(name, x, y) name##_NEDTRIE_FIND(x, y) -/*! \def NEDTRIE_EXACTFIND -\brief Returns true if there is an item with the same key and address as y in nedtrie x. -*/ -#define NEDTRIE_EXACTFIND(name, x, y) name##_NEDTRIE_EXACTFIND(x, y) -/*! \def NEDTRIE_NFIND -\brief Finds the item with the nearest key to y in nedtrie x. -*/ -#define NEDTRIE_NFIND(name, x, y) name##_NEDTRIE_NFIND(x, y) -/*! \def NEDTRIE_PREV -\brief Returns the item preceding y in nedtrie x. -*/ -#define NEDTRIE_PREV(name, x, y) name##_NEDTRIE_PREV(x, y) -/*! \def NEDTRIE_NEXT -\brief Returns the item following y in nedtrie x. -*/ -#define NEDTRIE_NEXT(name, x, y) name##_NEDTRIE_NEXT(x, y) -/*! \def NEDTRIE_PREVLEAF -\brief Returns the item with an identical key preceding y in nedtrie x. -*/ -#define NEDTRIE_PREVLEAF(name, x) name##_NEDTRIE_PREVLEAF(x) -/*! \def NEDTRIE_NEXTLEAF -\brief Returns the item with an identical key following y in nedtrie x. -*/ -#define NEDTRIE_NEXTLEAF(name, x) name##_NEDTRIE_NEXTLEAF(x) -/*! \def NEDTRIE_MIN -\brief Returns the lowest item in nedtrie x. This item will approximately have the smallest key. -*/ -#define NEDTRIE_MIN(name, x) name##_NEDTRIE_MINMAX(x, 0) -/*! \def NEDTRIE_MAX -\brief Returns the highest item in nedtrie x. This item will approximately have the biggest key. -*/ -#define NEDTRIE_MAX(name, x) name##_NEDTRIE_MINMAX(x, 1) - -/*! \def NEDTRIE_FOREACH -\brief Substitutes a for loop which forward iterates into x all items in nedtrie head. -*/ -#define NEDTRIE_FOREACH(x, name, head) \ - for ((x) = NEDTRIE_MIN(name, head); \ - (x) != NULL; \ - (x) = NEDTRIE_NEXT(name, head, x)) - -/*! \def NEDTRIE_FOREACH_REVERSE -\brief Substitutes a for loop which forward iterates into x all items in nedtrie head. -*/ -#define NEDTRIE_FOREACH_REVERSE(x, name, head) \ - for ((x) = NEDTRIE_MAX(name, head); \ - (x) != NULL; \ - (x) = NEDTRIE_PREV(name, head, x)) - -/*! \defd NEDTRIE_HASNODEHEADER -\brief Returns true if this item's node header is active. Useful as a quick check for whether a node is in some trie. -*/ -#define NEDTRIE_HASNODEHEADER(treevar, node, link) ((node)->link.trie_parent || (node)->link.trie_prev) - -#ifdef __NO__cplusplus -namespace nedtries { - -#ifndef NDEBUG - typedef struct TrieValidityState_t - { - size_t count, smallestkey, largestkey, tops, lefts, rights, leafs; - } TrieValidityState; - - template DEBUGINLINE - void triecheckvaliditybranch(trietype *head, type *RESTRICT node, unsigned bitidx, TrieValidityState &state) - { - type *RESTRICT child; - TrieLink_t *RESTRICT nodelink, *RESTRICT childlink; - size_t nodekey=keyfunct(node); - - if(nodekeystate.largestkey) state.largestkey=nodekey; - nodelink=(TrieLink_t *RESTRICT)((size_t) node + fieldoffset); - assert(nodelink->trie_parent); - child=nodelink->trie_parent; - childlink=(TrieLink_t *RESTRICT)((size_t) child + fieldoffset); - assert(childlink->trie_child[0]==node || childlink->trie_child[1]==node); - assert(node==childlink->trie_child[!!(nodekey & ((size_t) 1<trie_prev); - while((child=nodelink->trie_next)) - { - state.leafs++; - childlink=(TrieLink_t *RESTRICT)((size_t) child + fieldoffset); - assert(!childlink->trie_parent); - assert(!childlink->trie_child[0]); - assert(!childlink->trie_child[1]); - assert(childlink->trie_prev); - assert(!childlink->trie_next || child==((TrieLink_t *RESTRICT)((size_t) childlink->trie_next + fieldoffset))->trie_prev); - nodelink=childlink; - state.count++; - } - nodelink=(TrieLink_t *RESTRICT)((size_t) node + fieldoffset); - state.count++; - if(nodelink->trie_child[0]) - { - state.lefts++; - triecheckvaliditybranch(head, nodelink->trie_child[0], bitidx-1, state); - } - if(nodelink->trie_child[1]) - { - state.rights++; - triecheckvaliditybranch(head, nodelink->trie_child[1], bitidx-1, state); - } - } -#endif - template DEBUGINLINE void triecheckvalidity(trietype *head) - { -#ifndef NDEBUG - type *RESTRICT node, *RESTRICT child; - TrieLink_t *RESTRICT nodelink, *RESTRICT childlink; - unsigned n, bitidx; - TrieValidityState state={0}; - for(n=0; ntriebins[n])) - { - size_t nodekey=keyfunct(node); - state.tops++; - nodelink=(TrieLink_t *RESTRICT)((size_t) node + fieldoffset); - bitidx=(unsigned)(((size_t) nodelink->trie_parent)>>2); - assert(bitidx==n); - assert(head->triebins[bitidx]==node); - assert(((((size_t)-1)<trie_prev); - while((child=nodelink->trie_next)) - { - state.leafs++; - childlink=(TrieLink_t *RESTRICT)((size_t) child + fieldoffset); - assert(!childlink->trie_parent); - assert(!childlink->trie_child[0]); - assert(!childlink->trie_child[1]); - assert(childlink->trie_prev); - assert(!childlink->trie_next || child==((TrieLink_t *RESTRICT)((size_t) childlink->trie_next + fieldoffset))->trie_prev); - nodelink=childlink; - state.count++; - } - nodelink=(TrieLink_t *RESTRICT)((size_t) node + fieldoffset); - state.count++; - if(nodelink->trie_child[0]) - { - state.lefts++; - state.smallestkey=(size_t)-1; - state.largestkey=0; - triecheckvaliditybranch(head, nodelink->trie_child[0], bitidx-1, state); - assert(state.smallestkey>=(size_t)1<trie_child[1]) - { - state.rights++; - state.smallestkey=(size_t)-1; - state.largestkey=0; - triecheckvaliditybranch(head, nodelink->trie_child[1], bitidx-1, state); - assert(state.smallestkey>=(size_t)1<count); - for(state.count=0, node=trieminmax(head, 0); node; (node=trienext(head, node)), state.count++) -#if 0 - printf("%p\n", node) -#endif - ; - if(state.count!=head->count) - { - assert(state.count==head->count); - } -#if 0 - printf("\n"); -#endif - for(state.count=0, node=trieminmax(head, 1); node; (node=trieprev(head, node)), state.count++) -#if 0 - printf("%p\n", node) -#endif - ; - if(state.count!=head->count) - { - assert(state.count==head->count); - } -#if 0 - printf("\n"); -#endif -#if !defined(NDEBUG) && 0 - if(count>50) - printf("Of count %u, tops %.2lf%%, lefts %.2lf%%, rights %.2lf%%, leafs %.2lf%%\n", count, 100.0*tops/count, 100.0*lefts/count, 100.0*rights/count, 100.0*leafs/count); -#endif -#endif /* !NDEBUG */ - } - - /*! \def HAVE_CPP0XRVALUEREFS - \ingroup C++ - \brief Enables rvalue references - - Define to enable the usage of rvalue references which enables move semantics and - other things. Automatically defined if __cplusplus indicates a C++0x compiler, - otherwise you'll need to set it yourself. - */ -#if __NO__cplusplus > 199711L || defined(HAVE_CPP0X) /* Do we have C++0x? */ -#undef HAVE_CPP0XRVALUEREFS -#define HAVE_CPP0XRVALUEREFS 1 -#undef HAVE_CPP0XTYPETRAITS -#define HAVE_CPP0XTYPETRAITS 1 -#endif - -/*! \brief The policy namespace in which all nedtries policies live. */ - namespace nedpolicy - { - /*! \class nobblezeros - \brief A policy nobbling zeros - */ - template class nobblezeros - { - protected: - template static int trie_nobblefunction(trietype *head) - { - return 0; - } - }; - /*! \class nobbleones - \brief A policy nobbling ones - */ - template class nobbleones - { - protected: - template static int trie_nobblefunction(trietype *head) - { - return 1; - } - }; - /*! \class nobbleequally - \brief A policy nobbling zeros and ones equally - */ - template class nobbleequally - { - protected: - template static int trie_nobblefunction(trietype *head) - { - return (head->nobbledir=!head->nobbledir); - } - }; - } // namspace - template NEDTRIE_HEAD2(trie_map_head, type); - template struct trie_maptype; - template size_t trie_maptype_keyfunct(const trie_maptype *); - template, std::list::iterator> >, - template class nobblepolicy=nedpolicy::nobblezeros, - class stlcontainer=std::list, std::list::iterator> > > class trie_map; - /*! \struct trie_maptype - \ingroup C++ - \brief Encapsulates the nedtrie metadata with the given type - - Note that the nedtrie metadata is kept \em after the typed value - this prevents the nedtrie metadata interfering - with any special data alignment you might be using from a specialised STL allocator. - */ - template struct trie_maptype - { - private: - template class nobblepolicy, class stlcontainer> friend class trie_map; - template friend size_t trie_maptype_keyfunct(const trie_maptype *); - typedef type trie_value_type; - typedef iteratortype trie_iterator_type; - type trie_value; - iteratortype trie_iterator; - TrieLink_t trie_link; - static const size_t trie_link_offset=sizeof(type)+sizeof(iteratortype); // GCC won't accept offsetof() as a template argument sadly :( - public: - trie_maptype(const type &v) : trie_value(v) { } - template trie_maptype(const trie_maptype &o) : trie_value(o.trie_value) { } -#ifdef HAVE_CPP0XRVALUEREFS - template trie_maptype(trie_maptype &&o) : trie_value(std::move(o.trie_value)) { } -#endif - //! Silent const lvalue converter for type - operator const type &() const { return trie_value; } - }; - template size_t trie_maptype_keyfunct(const trie_maptype *v) - { - return v->trie_value.first; - } - - /*! \class trie_map - \ingroup C++ - \brief A STL container wrapper using nedtries to map keys to values. - - This class can be used to wrap any arbitrary STL container with nedtrie associativity. For example, if you - had a std::vector<> list of items, you could add nedtrie's fast nearly constant time algorithm for accessing them - - though one would expect that a std::list<> would be the most common combination. There is no strict reason why - one could not wrap std::unordered_map<>, though what one would gain is hard to imagine! - - Usage in the simplest sense is like this as the default template parameters use std::list<> as the underlying - container: - \code - trie_map fooMap; - fooMap[5]=Foo(); - fooMap.erase(fooMap.find(5)); - \endcode - - Unlike a proper STL container implementation, this wrapper is very much a hack in the sense that it's a very quick - and dirty way of implementing lots of nedtrie based STL containers at once. In this sense it does require its user - to not be stupid, and to know what they're doing. STL containers go out of their way to enforce correctness - well, - this wrapper most certainly does not. If you want to blow off your own foot, this implementation won't stop you! - - For example, despite the protected STL container inheritance, all common STL functions are made public so you - can if you want easily corrupt the internal state. Equally, if you know what you are doing you can pass in the - wrapper as a const version of its underlying STL container by reintrpret_cast<>-ing it. Despite this, the wrapper - is fairly typesafe in that its design won't introduce subtle bugs or cause existing code to magically break itself. - - If you would like a more proper bitwise trie STL container class implemented, or would like to be advised on any - algorithmic problems from which your IT project may be suffering, my consulting company ned Productions Consulting Ltd would be happy to advise. In particular - I would love to see a full bitwise trie implementation submitted to the Boost C++ libraries but I don't have the - unpaid time to devote to such an endeavour sadly. - - \warning If you use std::vector<> as the STL container, make SURE you resize() it to its maximum size before use. - Otherwise the iterators trie_map uses to link nedtrie items into the STL items will become invalidated on storage - expansion. - */ - template class nobblepolicy, class stlcontainer> class trie_map : protected stlcontainer, protected nobblepolicy > - { - typedef nobblepolicy > nobblepolicytype; - typedef typename stlcontainer::value_type mapvaluetype; - static const size_t trie_fieldoffset=mapvaluetype::trie_link_offset; - public: - typedef typename stlcontainer::allocator_type allocator_type; - typedef typename stlcontainer::const_iterator const_iterator; - typedef typename stlcontainer::const_pointer const_pointer; - typedef typename stlcontainer::const_reference const_reference; - typedef typename stlcontainer::const_reverse_iterator const_reverse_iterator; - typedef typename stlcontainer::difference_type difference_type; - typedef typename stlcontainer::iterator iterator; - typedef keytype key_type; - typedef type mapped_type; - typedef typename stlcontainer::pointer pointer; - typedef typename stlcontainer::reference reference; - typedef typename stlcontainer::reverse_iterator reverse_iterator; - typedef typename stlcontainer::size_type size_type; - typedef typename stlcontainer::value_type::trie_value_type value_type; - private: - trie_map_head triehead; - static const_iterator &to_iterator(const typename mapvaluetype::trie_iterator_type &it) - { - void *_it=(void *) ⁢ - return *(const_iterator *)_it; - } - static iterator &to_iterator(typename mapvaluetype::trie_iterator_type &it) - { - void *_it=(void *) ⁢ - return *(iterator *)_it; - } - static typename mapvaluetype::trie_iterator_type &from_iterator(iterator &it) - { - void *_it=(void *) ⁢ - return *(typename mapvaluetype::trie_iterator_type *)_it; - } - // Wipes and resets the nedtrie index - void triehead_reindex() - { - NEDTRIE_INIT(&triehead); - for(iterator it=begin(); it!=end(); ++it) - { - trieinsert, mapvaluetype, trie_fieldoffset, trie_maptype_keyfunct>(&triehead, &(*it)); - it->trie_iterator=it; - } - } - const mapvaluetype *triehead_find(const key_type &key) const - { // Avoid a value_type construction - char buffer[sizeof(mapvaluetype)]; - mapvaluetype *RESTRICT r=(mapvaluetype *RESTRICT) buffer; - r->trie_value.first=key; - return triefind, mapvaluetype, trie_fieldoffset, trie_maptype_keyfunct>(&triehead, r); - } - iterator triehead_insert(const value_type &val) - { - iterator it=stlcontainer::insert(end(), std::move(val)); - it->trie_iterator=from_iterator(it); - trieinsert, mapvaluetype, trie_fieldoffset, trie_maptype_keyfunct>(&triehead, &(*it)); - return it; - } -#ifdef HAVE_CPP0XRVALUEREFS - iterator triehead_insert(value_type &&val) - { - iterator it=stlcontainer::insert(end(), std::move(val)); - it->trie_iterator=from_iterator(it); - trieinsert, mapvaluetype, trie_fieldoffset, trie_maptype_keyfunct>(&triehead, &(*it)); - return it; - } -#endif - public: - using stlcontainer::begin; - using stlcontainer::clear; - //! Returns the number of items with the key \em key - size_type count(const key_type &key) const - { - size_type ret=0; - const mapvaluetype *r=triehead_find(key); - if(r) - { - if(r->trie_link.prev) r=r->trie_link.trie_prev; - for(; r; r=r->trie_link.trie_next) ret++; - } - return ret; - } - using stlcontainer::empty; - using stlcontainer::end; - //std::pair equal_range(const key_type &key); - //std::pair equal_range(const key_type &key) const; - //! Removes the item specified by \em it from the container - iterator erase(iterator it) - { - //int (*nobblefunct)(trietype *head) - trieremove, mapvaluetype, trie_fieldoffset, trie_maptype_keyfunct, - // Need to give MSVC a little bit of help -#ifdef _MSC_VER - nobblepolicytype::trie_nobblefunction > -#else - nobblepolicytype::trie_nobblefunction -#endif - >(&triehead, &(*it)); - return stlcontainer::erase(it); - } - //! Removes the items between \em first and \em last from the container - iterator erase(iterator first, iterator last) - { - for(iterator it=first; it!=last; ++it) - { - trieremove, mapvaluetype, trie_fieldoffset, trie_maptype_keyfunct, -#ifdef _MSC_VER - nobblepolicytype::trie_nobblefunction > -#else - nobblepolicytype::trie_nobblefunction -#endif - >(&triehead, &(*it)); - } - return stlcontainer::erase(first, last); - } - //! Finds the item with key \em key - iterator find(const key_type &key) { const_iterator it=static_cast(this)->find(key); void *_it=(void *) ⁢ return *(iterator *)_it; } - //! Finds the item with key \em key - const_iterator find(const key_type &key) const - { - const mapvaluetype *r=triehead_find(key); - return !r ? end() : to_iterator(r->trie_iterator); - } - using stlcontainer::get_allocator; - //! Inserts the item \em val - std::pair insert(const value_type &val) - { - mapvaluetype *r=const_cast(triehead_find(val.trie_value.first)); - if(r) - { - r->trie_value=std::move(val.trie_value); - return std::make_pair(to_iterator(r->trie_iterator), false); - } - return std::make_pair(triehead_insert(std::move(val)), true); - } - //! Inserts the item \em val at position \em at - iterator insert(iterator at, const value_type &val) - { - iterator it=stlcontainer::insert(at, val); - it->trie_iterator=from_iterator(it); - trieinsert(&triehead, &(*it)); - return it; - } - //! Inserts the items between \em first and \em last - template void insert(inputiterator first, inputiterator last) - { - iterator it=--end(); - stlcontainer::insert(first, last); - for(; it!=end(); ++it) - { - it->trie_iterator=from_iterator(it); - trieinsert(&triehead, &(*it)); - } - } - //key_compare key_comp() const; - //iterator lower_bound(const key_type &key); - //const_iterator lower_bound(const key_type &key) const; - using stlcontainer::max_size; - using stlcontainer::rbegin; - using stlcontainer::rend; - using stlcontainer::size; - using stlcontainer::swap; - //iterator upper_bound(const key_type &key); - //const_iterator upper_bound(const key_type &key) const; - //value_compare value_comp() const; - //! Returns an lvalue reference to the item with key \em key - mapped_type &operator[](const keytype &key) - { - mapvaluetype *r=const_cast(triehead_find(key)); - iterator it=r ? to_iterator(r->trie_iterator) : triehead_insert(std::move(value_type(key, std::move(type())))); - return it->trie_value.second; - } - - template class nobblepolicy_, class stlcontainer_> friend bool operator!=(const trie_map &a, const trie_map &b); - template class nobblepolicy_, class stlcontainer_> friend bool operator<(const trie_map &a, const trie_map &b); - template class nobblepolicy_, class stlcontainer_> friend bool operator<=(const trie_map &a, const trie_map &b); - template class nobblepolicy_, class stlcontainer_> friend bool operator==(const trie_map &a, const trie_map &b); - template class nobblepolicy_, class stlcontainer_> friend bool operator>(const trie_map &a, const trie_map &b); - template class nobblepolicy_, class stlcontainer_> friend bool operator>=(const trie_map &a, const trie_map &b); - - //! Constructs a trie_map. Has all the typical STL overloads - trie_map() : stlcontainer() { NEDTRIE_INIT(&triehead); } - explicit trie_map(const allocator &a) : stlcontainer(a) { NEDTRIE_INIT(&triehead); } - template trie_map(const trie_map &o) : stlcontainer(o) { triehead_reindex(); } - template trie_map &operator=(const trie_map &o) { *static_cast(this)=static_cast(o); triehead_reindex(); return *this; } -#ifdef HAVE_CPP0XRVALUEREFS - template trie_map(trie_map &&o) : stlcontainer(std::move(o)) - { - memcpy(&triehead, &o.triehead, sizeof(triehead)); - } - template trie_map &operator=(trie_map &&o) - { - *static_cast(this)=std::move(static_cast(o)); - memcpy(&triehead, &o.triehead, sizeof(triehead)); - return *this; - } -#endif - template trie_map(inputiterator s, inputiterator e) : stlcontainer(s, e) { triehead_reindex(); } - template trie_map(inputiterator s, inputiterator e, const allocator &a) : stlcontainer(s, e, a) { triehead_reindex(); } - }; - template class nobblepolicy, class stlcontainer> bool operator!=(const trie_map &a, const trie_map &b) - { - return static_cast(a)!=static_cast(b); - } - template class nobblepolicy, class stlcontainer> bool operator<(const trie_map &a, const trie_map &b) - { - return static_cast(a)(b); - } - template class nobblepolicy, class stlcontainer> bool operator<=(const trie_map &a, const trie_map &b) - { - return static_cast(a)<=static_cast(b); - } - template class nobblepolicy, class stlcontainer> bool operator==(const trie_map &a, const trie_map &b) - { - return static_cast(a)==static_cast(b); - } - template class nobblepolicy, class stlcontainer> bool operator>(const trie_map &a, const trie_map &b) - { - return static_cast(a)>static_cast(b); - } - template class nobblepolicy, class stlcontainer> bool operator>=(const trie_map &a, const trie_map &b) - { - return static_cast(a)>=static_cast(b); - } -} /* namespace */ -#endif diff --git a/tommyds/benchmark/lib/rb/rb_new.h b/tommyds/benchmark/lib/rb/rb_new.h deleted file mode 100644 index 8f301bb..0000000 --- a/tommyds/benchmark/lib/rb/rb_new.h +++ /dev/null @@ -1,973 +0,0 @@ -/*- - * cpp macro implementation of left-leaning red-black trees. - * - * Usage: - * - * (Optional, see assert(3).) - * #define NDEBUG - * - * (Required.) - * #include - * #include - * ... - * - * All operations are done non-recursively. Parent pointers are not used, and - * color bits are stored in the least significant bit of right-child pointers, - * thus making node linkage as compact as is possible for red-black trees. - * - * Some macros use a comparison function pointer, which is expected to have the - * following prototype: - * - * int (a_cmp *)(a_type *a_node, a_type *a_other); - * ^^^^^^ - * or a_key - * - * Interpretation of comparision function return values: - * - * -1 : a_node < a_other - * 0 : a_node == a_other - * 1 : a_node > a_other - * - * In all cases, the a_node or a_key macro argument is the first argument to the - * comparison function, which makes it possible to write comparison functions - * that treat the first argument specially. - * - ******************************************************************************/ - -#ifndef RB_H_ -#define RB_H_ - -#define RB_COMPACT -#ifdef RB_COMPACT -/* Node structure. */ -#define rb_node(a_type) \ -struct { \ - a_type *rbn_left; \ - a_type *rbn_right_red; \ -} -#else -#define rb_node(a_type) \ -struct { \ - a_type *rbn_left; \ - a_type *rbn_right; \ - bool rbn_red; \ -} -#endif - -/* Root structure. */ -#define rb_tree(a_type) \ -struct { \ - a_type *rbt_root; \ - a_type rbt_nil; \ -} - -/* Left accessors. */ -#define rbp_left_get(a_type, a_field, a_node) \ - ((a_node)->a_field.rbn_left) -#define rbp_left_set(a_type, a_field, a_node, a_left) do { \ - (a_node)->a_field.rbn_left = a_left; \ -} while (0) - -#ifdef RB_COMPACT -/* Right accessors. */ -#define rbp_right_get(a_type, a_field, a_node) \ - ((a_type *) (((intptr_t) (a_node)->a_field.rbn_right_red) \ - & ((ssize_t)-2))) -#define rbp_right_set(a_type, a_field, a_node, a_right) do { \ - (a_node)->a_field.rbn_right_red = (a_type *) (((uintptr_t) a_right) \ - | (((uintptr_t) (a_node)->a_field.rbn_right_red) & ((size_t)1))); \ -} while (0) - -/* Color accessors. */ -#define rbp_red_get(a_type, a_field, a_node) \ - ((bool) (((uintptr_t) (a_node)->a_field.rbn_right_red) \ - & ((size_t)1))) -#define rbp_color_set(a_type, a_field, a_node, a_red) do { \ - (a_node)->a_field.rbn_right_red = (a_type *) ((((intptr_t) \ - (a_node)->a_field.rbn_right_red) & ((ssize_t)-2)) \ - | ((ssize_t)a_red)); \ -} while (0) -#define rbp_red_set(a_type, a_field, a_node) do { \ - (a_node)->a_field.rbn_right_red = (a_type *) (((uintptr_t) \ - (a_node)->a_field.rbn_right_red) | ((size_t)1)); \ -} while (0) -#define rbp_black_set(a_type, a_field, a_node) do { \ - (a_node)->a_field.rbn_right_red = (a_type *) (((intptr_t) \ - (a_node)->a_field.rbn_right_red) & ((ssize_t)-2)); \ -} while (0) -#else -/* Right accessors. */ -#define rbp_right_get(a_type, a_field, a_node) \ - ((a_node)->a_field.rbn_right) -#define rbp_right_set(a_type, a_field, a_node, a_right) do { \ - (a_node)->a_field.rbn_right = a_right; \ -} while (0) - -/* Color accessors. */ -#define rbp_red_get(a_type, a_field, a_node) \ - ((a_node)->a_field.rbn_red) -#define rbp_color_set(a_type, a_field, a_node, a_red) do { \ - (a_node)->a_field.rbn_red = (a_red); \ -} while (0) -#define rbp_red_set(a_type, a_field, a_node) do { \ - (a_node)->a_field.rbn_red = true; \ -} while (0) -#define rbp_black_set(a_type, a_field, a_node) do { \ - (a_node)->a_field.rbn_red = false; \ -} while (0) -#endif - -/* Node initializer. */ -#define rbp_node_new(a_type, a_field, a_tree, a_node) do { \ - rbp_left_set(a_type, a_field, (a_node), &(a_tree)->rbt_nil); \ - rbp_right_set(a_type, a_field, (a_node), &(a_tree)->rbt_nil); \ - rbp_red_set(a_type, a_field, (a_node)); \ -} while (0) - -/* Tree initializer. */ -#define rb_new(a_type, a_field, a_tree) do { \ - (a_tree)->rbt_root = &(a_tree)->rbt_nil; \ - rbp_node_new(a_type, a_field, a_tree, &(a_tree)->rbt_nil); \ - rbp_black_set(a_type, a_field, &(a_tree)->rbt_nil); \ -} while (0) - -/* Tree operations. */ -#define rbp_black_height(a_type, a_field, a_tree, r_height) do { \ - a_type *rbp_bh_t; \ - for (rbp_bh_t = (a_tree)->rbt_root, (r_height) = 0; \ - rbp_bh_t != &(a_tree)->rbt_nil; \ - rbp_bh_t = rbp_left_get(a_type, a_field, rbp_bh_t)) { \ - if (rbp_red_get(a_type, a_field, rbp_bh_t) == false) { \ - (r_height)++; \ - } \ - } \ -} while (0) - -#define rbp_first(a_type, a_field, a_tree, a_root, r_node) do { \ - for ((r_node) = (a_root); \ - rbp_left_get(a_type, a_field, (r_node)) != &(a_tree)->rbt_nil; \ - (r_node) = rbp_left_get(a_type, a_field, (r_node))) { \ - } \ -} while (0) - -#define rbp_last(a_type, a_field, a_tree, a_root, r_node) do { \ - for ((r_node) = (a_root); \ - rbp_right_get(a_type, a_field, (r_node)) != &(a_tree)->rbt_nil; \ - (r_node) = rbp_right_get(a_type, a_field, (r_node))) { \ - } \ -} while (0) - -#define rbp_next(a_type, a_field, a_cmp, a_tree, a_node, r_node) do { \ - if (rbp_right_get(a_type, a_field, (a_node)) \ - != &(a_tree)->rbt_nil) { \ - rbp_first(a_type, a_field, a_tree, rbp_right_get(a_type, \ - a_field, (a_node)), (r_node)); \ - } else { \ - a_type *rbp_n_t = (a_tree)->rbt_root; \ - assert(rbp_n_t != &(a_tree)->rbt_nil); \ - (r_node) = &(a_tree)->rbt_nil; \ - while (true) { \ - int rbp_n_cmp = (a_cmp)((a_node), rbp_n_t); \ - if (rbp_n_cmp < 0) { \ - (r_node) = rbp_n_t; \ - rbp_n_t = rbp_left_get(a_type, a_field, rbp_n_t); \ - } else if (rbp_n_cmp > 0) { \ - rbp_n_t = rbp_right_get(a_type, a_field, rbp_n_t); \ - } else { \ - break; \ - } \ - assert(rbp_n_t != &(a_tree)->rbt_nil); \ - } \ - } \ -} while (0) - -#define rbp_prev(a_type, a_field, a_cmp, a_tree, a_node, r_node) do { \ - if (rbp_left_get(a_type, a_field, (a_node)) != &(a_tree)->rbt_nil) {\ - rbp_last(a_type, a_field, a_tree, rbp_left_get(a_type, \ - a_field, (a_node)), (r_node)); \ - } else { \ - a_type *rbp_p_t = (a_tree)->rbt_root; \ - assert(rbp_p_t != &(a_tree)->rbt_nil); \ - (r_node) = &(a_tree)->rbt_nil; \ - while (true) { \ - int rbp_p_cmp = (a_cmp)((a_node), rbp_p_t); \ - if (rbp_p_cmp < 0) { \ - rbp_p_t = rbp_left_get(a_type, a_field, rbp_p_t); \ - } else if (rbp_p_cmp > 0) { \ - (r_node) = rbp_p_t; \ - rbp_p_t = rbp_right_get(a_type, a_field, rbp_p_t); \ - } else { \ - break; \ - } \ - assert(rbp_p_t != &(a_tree)->rbt_nil); \ - } \ - } \ -} while (0) - -#define rb_first(a_type, a_field, a_tree, r_node) do { \ - rbp_first(a_type, a_field, a_tree, (a_tree)->rbt_root, (r_node)); \ - if ((r_node) == &(a_tree)->rbt_nil) { \ - (r_node) = NULL; \ - } \ -} while (0) - -#define rb_last(a_type, a_field, a_tree, r_node) do { \ - rbp_last(a_type, a_field, a_tree, (a_tree)->rbt_root, r_node); \ - if ((r_node) == &(a_tree)->rbt_nil) { \ - (r_node) = NULL; \ - } \ -} while (0) - -#define rb_next(a_type, a_field, a_cmp, a_tree, a_node, r_node) do { \ - rbp_next(a_type, a_field, a_cmp, a_tree, (a_node), (r_node)); \ - if ((r_node) == &(a_tree)->rbt_nil) { \ - (r_node) = NULL; \ - } \ -} while (0) - -#define rb_prev(a_type, a_field, a_cmp, a_tree, a_node, r_node) do { \ - rbp_prev(a_type, a_field, a_cmp, a_tree, (a_node), (r_node)); \ - if ((r_node) == &(a_tree)->rbt_nil) { \ - (r_node) = NULL; \ - } \ -} while (0) - -#define rb_search(a_type, a_field, a_cmp, a_tree, a_key, r_node) do { \ - int rbp_se_cmp; \ - (r_node) = (a_tree)->rbt_root; \ - while ((r_node) != &(a_tree)->rbt_nil \ - && (rbp_se_cmp = (a_cmp)((a_key), (r_node))) != 0) { \ - if (rbp_se_cmp < 0) { \ - (r_node) = rbp_left_get(a_type, a_field, (r_node)); \ - } else { \ - (r_node) = rbp_right_get(a_type, a_field, (r_node)); \ - } \ - } \ - if ((r_node) == &(a_tree)->rbt_nil) { \ - (r_node) = NULL; \ - } \ -} while (0) - -/* - * Find a match if it exists. Otherwise, find the next greater node, if one - * exists. - */ -#define rb_nsearch(a_type, a_field, a_cmp, a_tree, a_key, r_node) do { \ - a_type *rbp_ns_t = (a_tree)->rbt_root; \ - (r_node) = NULL; \ - while (rbp_ns_t != &(a_tree)->rbt_nil) { \ - int rbp_ns_cmp = (a_cmp)((a_key), rbp_ns_t); \ - if (rbp_ns_cmp < 0) { \ - (r_node) = rbp_ns_t; \ - rbp_ns_t = rbp_left_get(a_type, a_field, rbp_ns_t); \ - } else if (rbp_ns_cmp > 0) { \ - rbp_ns_t = rbp_right_get(a_type, a_field, rbp_ns_t); \ - } else { \ - (r_node) = rbp_ns_t; \ - break; \ - } \ - } \ -} while (0) - -/* - * Find a match if it exists. Otherwise, find the previous lesser node, if one - * exists. - */ -#define rb_psearch(a_type, a_field, a_cmp, a_tree, a_key, r_node) do { \ - a_type *rbp_ps_t = (a_tree)->rbt_root; \ - (r_node) = NULL; \ - while (rbp_ps_t != &(a_tree)->rbt_nil) { \ - int rbp_ps_cmp = (a_cmp)((a_key), rbp_ps_t); \ - if (rbp_ps_cmp < 0) { \ - rbp_ps_t = rbp_left_get(a_type, a_field, rbp_ps_t); \ - } else if (rbp_ps_cmp > 0) { \ - (r_node) = rbp_ps_t; \ - rbp_ps_t = rbp_right_get(a_type, a_field, rbp_ps_t); \ - } else { \ - (r_node) = rbp_ps_t; \ - break; \ - } \ - } \ -} while (0) - -#define rbp_rotate_left(a_type, a_field, a_node, r_node) do { \ - (r_node) = rbp_right_get(a_type, a_field, (a_node)); \ - rbp_right_set(a_type, a_field, (a_node), \ - rbp_left_get(a_type, a_field, (r_node))); \ - rbp_left_set(a_type, a_field, (r_node), (a_node)); \ -} while (0) - -#define rbp_rotate_right(a_type, a_field, a_node, r_node) do { \ - (r_node) = rbp_left_get(a_type, a_field, (a_node)); \ - rbp_left_set(a_type, a_field, (a_node), \ - rbp_right_get(a_type, a_field, (r_node))); \ - rbp_right_set(a_type, a_field, (r_node), (a_node)); \ -} while (0) - -#define rbp_lean_left(a_type, a_field, a_node, r_node) do { \ - bool rbp_ll_red; \ - rbp_rotate_left(a_type, a_field, (a_node), (r_node)); \ - rbp_ll_red = rbp_red_get(a_type, a_field, (a_node)); \ - rbp_color_set(a_type, a_field, (r_node), rbp_ll_red); \ - rbp_red_set(a_type, a_field, (a_node)); \ -} while (0) - -#define rbp_lean_right(a_type, a_field, a_node, r_node) do { \ - bool rbp_lr_red; \ - rbp_rotate_right(a_type, a_field, (a_node), (r_node)); \ - rbp_lr_red = rbp_red_get(a_type, a_field, (a_node)); \ - rbp_color_set(a_type, a_field, (r_node), rbp_lr_red); \ - rbp_red_set(a_type, a_field, (a_node)); \ -} while (0) - -#define rbp_move_red_left(a_type, a_field, a_node, r_node) do { \ - a_type *rbp_mrl_t, *rbp_mrl_u; \ - rbp_mrl_t = rbp_left_get(a_type, a_field, (a_node)); \ - rbp_red_set(a_type, a_field, rbp_mrl_t); \ - rbp_mrl_t = rbp_right_get(a_type, a_field, (a_node)); \ - rbp_mrl_u = rbp_left_get(a_type, a_field, rbp_mrl_t); \ - if (rbp_red_get(a_type, a_field, rbp_mrl_u)) { \ - rbp_rotate_right(a_type, a_field, rbp_mrl_t, rbp_mrl_u); \ - rbp_right_set(a_type, a_field, (a_node), rbp_mrl_u); \ - rbp_rotate_left(a_type, a_field, (a_node), (r_node)); \ - rbp_mrl_t = rbp_right_get(a_type, a_field, (a_node)); \ - if (rbp_red_get(a_type, a_field, rbp_mrl_t)) { \ - rbp_black_set(a_type, a_field, rbp_mrl_t); \ - rbp_red_set(a_type, a_field, (a_node)); \ - rbp_rotate_left(a_type, a_field, (a_node), rbp_mrl_t); \ - rbp_left_set(a_type, a_field, (r_node), rbp_mrl_t); \ - } else { \ - rbp_black_set(a_type, a_field, (a_node)); \ - } \ - } else { \ - rbp_red_set(a_type, a_field, (a_node)); \ - rbp_rotate_left(a_type, a_field, (a_node), (r_node)); \ - } \ -} while (0) - -#define rbp_move_red_right(a_type, a_field, a_node, r_node) do { \ - a_type *rbp_mrr_t; \ - rbp_mrr_t = rbp_left_get(a_type, a_field, (a_node)); \ - if (rbp_red_get(a_type, a_field, rbp_mrr_t)) { \ - a_type *rbp_mrr_u, *rbp_mrr_v; \ - rbp_mrr_u = rbp_right_get(a_type, a_field, rbp_mrr_t); \ - rbp_mrr_v = rbp_left_get(a_type, a_field, rbp_mrr_u); \ - if (rbp_red_get(a_type, a_field, rbp_mrr_v)) { \ - rbp_color_set(a_type, a_field, rbp_mrr_u, \ - rbp_red_get(a_type, a_field, (a_node))); \ - rbp_black_set(a_type, a_field, rbp_mrr_v); \ - rbp_rotate_left(a_type, a_field, rbp_mrr_t, rbp_mrr_u); \ - rbp_left_set(a_type, a_field, (a_node), rbp_mrr_u); \ - rbp_rotate_right(a_type, a_field, (a_node), (r_node)); \ - rbp_rotate_left(a_type, a_field, (a_node), rbp_mrr_t); \ - rbp_right_set(a_type, a_field, (r_node), rbp_mrr_t); \ - } else { \ - rbp_color_set(a_type, a_field, rbp_mrr_t, \ - rbp_red_get(a_type, a_field, (a_node))); \ - rbp_red_set(a_type, a_field, rbp_mrr_u); \ - rbp_rotate_right(a_type, a_field, (a_node), (r_node)); \ - rbp_rotate_left(a_type, a_field, (a_node), rbp_mrr_t); \ - rbp_right_set(a_type, a_field, (r_node), rbp_mrr_t); \ - } \ - rbp_red_set(a_type, a_field, (a_node)); \ - } else { \ - rbp_red_set(a_type, a_field, rbp_mrr_t); \ - rbp_mrr_t = rbp_left_get(a_type, a_field, rbp_mrr_t); \ - if (rbp_red_get(a_type, a_field, rbp_mrr_t)) { \ - rbp_black_set(a_type, a_field, rbp_mrr_t); \ - rbp_rotate_right(a_type, a_field, (a_node), (r_node)); \ - rbp_rotate_left(a_type, a_field, (a_node), rbp_mrr_t); \ - rbp_right_set(a_type, a_field, (r_node), rbp_mrr_t); \ - } else { \ - rbp_rotate_left(a_type, a_field, (a_node), (r_node)); \ - } \ - } \ -} while (0) - -#define rb_insert(a_type, a_field, a_cmp, a_tree, a_node) do { \ - a_type rbp_i_s; \ - a_type *rbp_i_g, *rbp_i_p, *rbp_i_c, *rbp_i_t, *rbp_i_u; \ - int rbp_i_cmp = 0; \ - rbp_i_g = &(a_tree)->rbt_nil; \ - rbp_left_set(a_type, a_field, &rbp_i_s, (a_tree)->rbt_root); \ - rbp_right_set(a_type, a_field, &rbp_i_s, &(a_tree)->rbt_nil); \ - rbp_black_set(a_type, a_field, &rbp_i_s); \ - rbp_i_p = &rbp_i_s; \ - rbp_i_c = (a_tree)->rbt_root; \ - /* Iteratively search down the tree for the insertion point, */\ - /* splitting 4-nodes as they are encountered. At the end of each */\ - /* iteration, rbp_i_g->rbp_i_p->rbp_i_c is a 3-level path down */\ - /* the tree, assuming a sufficiently deep tree. */\ - while (rbp_i_c != &(a_tree)->rbt_nil) { \ - rbp_i_t = rbp_left_get(a_type, a_field, rbp_i_c); \ - rbp_i_u = rbp_left_get(a_type, a_field, rbp_i_t); \ - if (rbp_red_get(a_type, a_field, rbp_i_t) \ - && rbp_red_get(a_type, a_field, rbp_i_u)) { \ - /* rbp_i_c is the top of a logical 4-node, so split it. */\ - /* This iteration does not move down the tree, due to the */\ - /* disruptiveness of node splitting. */\ - /* */\ - /* Rotate right. */\ - rbp_rotate_right(a_type, a_field, rbp_i_c, rbp_i_t); \ - /* Pass red links up one level. */\ - rbp_i_u = rbp_left_get(a_type, a_field, rbp_i_t); \ - rbp_black_set(a_type, a_field, rbp_i_u); \ - if (rbp_left_get(a_type, a_field, rbp_i_p) == rbp_i_c) { \ - rbp_left_set(a_type, a_field, rbp_i_p, rbp_i_t); \ - rbp_i_c = rbp_i_t; \ - } else { \ - /* rbp_i_c was the right child of rbp_i_p, so rotate */\ - /* left in order to maintain the left-leaning */\ - /* invariant. */\ - assert(rbp_right_get(a_type, a_field, rbp_i_p) \ - == rbp_i_c); \ - rbp_right_set(a_type, a_field, rbp_i_p, rbp_i_t); \ - rbp_lean_left(a_type, a_field, rbp_i_p, rbp_i_u); \ - if (rbp_left_get(a_type, a_field, rbp_i_g) == rbp_i_p) {\ - rbp_left_set(a_type, a_field, rbp_i_g, rbp_i_u); \ - } else { \ - assert(rbp_right_get(a_type, a_field, rbp_i_g) \ - == rbp_i_p); \ - rbp_right_set(a_type, a_field, rbp_i_g, rbp_i_u); \ - } \ - rbp_i_p = rbp_i_u; \ - rbp_i_cmp = (a_cmp)((a_node), rbp_i_p); \ - if (rbp_i_cmp < 0) { \ - rbp_i_c = rbp_left_get(a_type, a_field, rbp_i_p); \ - } else { \ - assert(rbp_i_cmp > 0); \ - rbp_i_c = rbp_right_get(a_type, a_field, rbp_i_p); \ - } \ - continue; \ - } \ - } \ - rbp_i_g = rbp_i_p; \ - rbp_i_p = rbp_i_c; \ - rbp_i_cmp = (a_cmp)((a_node), rbp_i_c); \ - if (rbp_i_cmp < 0) { \ - rbp_i_c = rbp_left_get(a_type, a_field, rbp_i_c); \ - } else { \ - assert(rbp_i_cmp > 0); \ - rbp_i_c = rbp_right_get(a_type, a_field, rbp_i_c); \ - } \ - } \ - /* rbp_i_p now refers to the node under which to insert. */\ - rbp_node_new(a_type, a_field, a_tree, (a_node)); \ - if (rbp_i_cmp > 0) { \ - rbp_right_set(a_type, a_field, rbp_i_p, (a_node)); \ - rbp_lean_left(a_type, a_field, rbp_i_p, rbp_i_t); \ - if (rbp_left_get(a_type, a_field, rbp_i_g) == rbp_i_p) { \ - rbp_left_set(a_type, a_field, rbp_i_g, rbp_i_t); \ - } else if (rbp_right_get(a_type, a_field, rbp_i_g) == rbp_i_p) {\ - rbp_right_set(a_type, a_field, rbp_i_g, rbp_i_t); \ - } \ - } else { \ - rbp_left_set(a_type, a_field, rbp_i_p, (a_node)); \ - } \ - /* Update the root and make sure that it is black. */\ - (a_tree)->rbt_root = rbp_left_get(a_type, a_field, &rbp_i_s); \ - rbp_black_set(a_type, a_field, (a_tree)->rbt_root); \ -} while (0) - -#define rb_remove(a_type, a_field, a_cmp, a_tree, a_node) do { \ - a_type rbp_r_s; \ - a_type *rbp_r_p, *rbp_r_c, *rbp_r_xp, *rbp_r_t, *rbp_r_u; \ - int rbp_r_cmp; \ - rbp_left_set(a_type, a_field, &rbp_r_s, (a_tree)->rbt_root); \ - rbp_right_set(a_type, a_field, &rbp_r_s, &(a_tree)->rbt_nil); \ - rbp_black_set(a_type, a_field, &rbp_r_s); \ - rbp_r_p = &rbp_r_s; \ - rbp_r_c = (a_tree)->rbt_root; \ - rbp_r_xp = &(a_tree)->rbt_nil; \ - /* Iterate down the tree, but always transform 2-nodes to 3- or */\ - /* 4-nodes in order to maintain the invariant that the current */\ - /* node is not a 2-node. This allows simple deletion once a leaf */\ - /* is reached. Handle the root specially though, since there may */\ - /* be no way to convert it from a 2-node to a 3-node. */\ - rbp_r_cmp = (a_cmp)((a_node), rbp_r_c); \ - if (rbp_r_cmp < 0) { \ - rbp_r_t = rbp_left_get(a_type, a_field, rbp_r_c); \ - rbp_r_u = rbp_left_get(a_type, a_field, rbp_r_t); \ - if (rbp_red_get(a_type, a_field, rbp_r_t) == false \ - && rbp_red_get(a_type, a_field, rbp_r_u) == false) { \ - /* Apply standard transform to prepare for left move. */\ - rbp_move_red_left(a_type, a_field, rbp_r_c, rbp_r_t); \ - rbp_black_set(a_type, a_field, rbp_r_t); \ - rbp_left_set(a_type, a_field, rbp_r_p, rbp_r_t); \ - rbp_r_c = rbp_r_t; \ - } else { \ - /* Move left. */\ - rbp_r_p = rbp_r_c; \ - rbp_r_c = rbp_left_get(a_type, a_field, rbp_r_c); \ - } \ - } else { \ - if (rbp_r_cmp == 0) { \ - assert((a_node) == rbp_r_c); \ - if (rbp_right_get(a_type, a_field, rbp_r_c) \ - == &(a_tree)->rbt_nil) { \ - /* Delete root node (which is also a leaf node). */\ - if (rbp_left_get(a_type, a_field, rbp_r_c) \ - != &(a_tree)->rbt_nil) { \ - rbp_lean_right(a_type, a_field, rbp_r_c, rbp_r_t); \ - rbp_right_set(a_type, a_field, rbp_r_t, \ - &(a_tree)->rbt_nil); \ - } else { \ - rbp_r_t = &(a_tree)->rbt_nil; \ - } \ - rbp_left_set(a_type, a_field, rbp_r_p, rbp_r_t); \ - } else { \ - /* This is the node we want to delete, but we will */\ - /* instead swap it with its successor and delete the */\ - /* successor. Record enough information to do the */\ - /* swap later. rbp_r_xp is the a_node's parent. */\ - rbp_r_xp = rbp_r_p; \ - rbp_r_cmp = 1; /* Note that deletion is incomplete. */\ - } \ - } \ - if (rbp_r_cmp == 1) { \ - if (rbp_red_get(a_type, a_field, rbp_left_get(a_type, \ - a_field, rbp_right_get(a_type, a_field, rbp_r_c))) \ - == false) { \ - rbp_r_t = rbp_left_get(a_type, a_field, rbp_r_c); \ - if (rbp_red_get(a_type, a_field, rbp_r_t)) { \ - /* Standard transform. */\ - rbp_move_red_right(a_type, a_field, rbp_r_c, \ - rbp_r_t); \ - } else { \ - /* Root-specific transform. */\ - rbp_red_set(a_type, a_field, rbp_r_c); \ - rbp_r_u = rbp_left_get(a_type, a_field, rbp_r_t); \ - if (rbp_red_get(a_type, a_field, rbp_r_u)) { \ - rbp_black_set(a_type, a_field, rbp_r_u); \ - rbp_rotate_right(a_type, a_field, rbp_r_c, \ - rbp_r_t); \ - rbp_rotate_left(a_type, a_field, rbp_r_c, \ - rbp_r_u); \ - rbp_right_set(a_type, a_field, rbp_r_t, \ - rbp_r_u); \ - } else { \ - rbp_red_set(a_type, a_field, rbp_r_t); \ - rbp_rotate_left(a_type, a_field, rbp_r_c, \ - rbp_r_t); \ - } \ - } \ - rbp_left_set(a_type, a_field, rbp_r_p, rbp_r_t); \ - rbp_r_c = rbp_r_t; \ - } else { \ - /* Move right. */\ - rbp_r_p = rbp_r_c; \ - rbp_r_c = rbp_right_get(a_type, a_field, rbp_r_c); \ - } \ - } \ - } \ - if (rbp_r_cmp != 0) { \ - while (true) { \ - assert(rbp_r_p != &(a_tree)->rbt_nil); \ - rbp_r_cmp = (a_cmp)((a_node), rbp_r_c); \ - if (rbp_r_cmp < 0) { \ - rbp_r_t = rbp_left_get(a_type, a_field, rbp_r_c); \ - if (rbp_r_t == &(a_tree)->rbt_nil) { \ - /* rbp_r_c now refers to the successor node to */\ - /* relocate, and rbp_r_xp/a_node refer to the */\ - /* context for the relocation. */\ - if (rbp_left_get(a_type, a_field, rbp_r_xp) \ - == (a_node)) { \ - rbp_left_set(a_type, a_field, rbp_r_xp, \ - rbp_r_c); \ - } else { \ - assert(rbp_right_get(a_type, a_field, \ - rbp_r_xp) == (a_node)); \ - rbp_right_set(a_type, a_field, rbp_r_xp, \ - rbp_r_c); \ - } \ - rbp_left_set(a_type, a_field, rbp_r_c, \ - rbp_left_get(a_type, a_field, (a_node))); \ - rbp_right_set(a_type, a_field, rbp_r_c, \ - rbp_right_get(a_type, a_field, (a_node))); \ - rbp_color_set(a_type, a_field, rbp_r_c, \ - rbp_red_get(a_type, a_field, (a_node))); \ - if (rbp_left_get(a_type, a_field, rbp_r_p) \ - == rbp_r_c) { \ - rbp_left_set(a_type, a_field, rbp_r_p, \ - &(a_tree)->rbt_nil); \ - } else { \ - assert(rbp_right_get(a_type, a_field, rbp_r_p) \ - == rbp_r_c); \ - rbp_right_set(a_type, a_field, rbp_r_p, \ - &(a_tree)->rbt_nil); \ - } \ - break; \ - } \ - rbp_r_u = rbp_left_get(a_type, a_field, rbp_r_t); \ - if (rbp_red_get(a_type, a_field, rbp_r_t) == false \ - && rbp_red_get(a_type, a_field, rbp_r_u) == false) { \ - rbp_move_red_left(a_type, a_field, rbp_r_c, \ - rbp_r_t); \ - if (rbp_left_get(a_type, a_field, rbp_r_p) \ - == rbp_r_c) { \ - rbp_left_set(a_type, a_field, rbp_r_p, rbp_r_t);\ - } else { \ - rbp_right_set(a_type, a_field, rbp_r_p, \ - rbp_r_t); \ - } \ - rbp_r_c = rbp_r_t; \ - } else { \ - rbp_r_p = rbp_r_c; \ - rbp_r_c = rbp_left_get(a_type, a_field, rbp_r_c); \ - } \ - } else { \ - /* Check whether to delete this node (it has to be */\ - /* the correct node and a leaf node). */\ - if (rbp_r_cmp == 0) { \ - assert((a_node) == rbp_r_c); \ - if (rbp_right_get(a_type, a_field, rbp_r_c) \ - == &(a_tree)->rbt_nil) { \ - /* Delete leaf node. */\ - if (rbp_left_get(a_type, a_field, rbp_r_c) \ - != &(a_tree)->rbt_nil) { \ - rbp_lean_right(a_type, a_field, rbp_r_c, \ - rbp_r_t); \ - rbp_right_set(a_type, a_field, rbp_r_t, \ - &(a_tree)->rbt_nil); \ - } else { \ - rbp_r_t = &(a_tree)->rbt_nil; \ - } \ - if (rbp_left_get(a_type, a_field, rbp_r_p) \ - == rbp_r_c) { \ - rbp_left_set(a_type, a_field, rbp_r_p, \ - rbp_r_t); \ - } else { \ - rbp_right_set(a_type, a_field, rbp_r_p, \ - rbp_r_t); \ - } \ - break; \ - } else { \ - /* This is the node we want to delete, but we */\ - /* will instead swap it with its successor */\ - /* and delete the successor. Record enough */\ - /* information to do the swap later. */\ - /* rbp_r_xp is a_node's parent. */\ - rbp_r_xp = rbp_r_p; \ - } \ - } \ - rbp_r_t = rbp_right_get(a_type, a_field, rbp_r_c); \ - rbp_r_u = rbp_left_get(a_type, a_field, rbp_r_t); \ - if (rbp_red_get(a_type, a_field, rbp_r_u) == false) { \ - rbp_move_red_right(a_type, a_field, rbp_r_c, \ - rbp_r_t); \ - if (rbp_left_get(a_type, a_field, rbp_r_p) \ - == rbp_r_c) { \ - rbp_left_set(a_type, a_field, rbp_r_p, rbp_r_t);\ - } else { \ - rbp_right_set(a_type, a_field, rbp_r_p, \ - rbp_r_t); \ - } \ - rbp_r_c = rbp_r_t; \ - } else { \ - rbp_r_p = rbp_r_c; \ - rbp_r_c = rbp_right_get(a_type, a_field, rbp_r_c); \ - } \ - } \ - } \ - } \ - /* Update root. */\ - (a_tree)->rbt_root = rbp_left_get(a_type, a_field, &rbp_r_s); \ -} while (0) - -/* - * The rb_proto() macro generates function prototypes that correspond to the - * functions generated by an equivalently parameterized call to rb_wrap(). - */ - -#define rb_proto(a_attr, a_prefix, a_tree_type, a_type) \ -a_attr void \ -a_prefix##new(a_tree_type *tree); \ -a_attr a_type * \ -a_prefix##first(a_tree_type *tree); \ -a_attr a_type * \ -a_prefix##last(a_tree_type *tree); \ -a_attr a_type * \ -a_prefix##next(a_tree_type *tree, a_type *node); \ -a_attr a_type * \ -a_prefix##prev(a_tree_type *tree, a_type *node); \ -a_attr a_type * \ -a_prefix##search(a_tree_type *tree, a_type *key); \ -a_attr a_type * \ -a_prefix##nsearch(a_tree_type *tree, a_type *key); \ -a_attr a_type * \ -a_prefix##psearch(a_tree_type *tree, a_type *key); \ -a_attr void \ -a_prefix##insert(a_tree_type *tree, a_type *node); \ -a_attr void \ -a_prefix##remove(a_tree_type *tree, a_type *node); - -/* - * The rb_wrap() macro provides a convenient way to wrap functions around the - * cpp macros. The main benefits of wrapping are that 1) repeated macro - * expansion can cause code bloat, especially for rb_{insert,remove)(), and - * 2) type, linkage, comparison functions, etc. need not be specified at every - * call point. - */ - -#define rb_wrap(a_attr, a_prefix, a_tree_type, a_type, a_field, a_cmp) \ -a_attr void \ -a_prefix##new(a_tree_type *tree) { \ - rb_new(a_type, a_field, tree); \ -} \ -a_attr a_type * \ -a_prefix##first(a_tree_type *tree) { \ - a_type *ret; \ - rb_first(a_type, a_field, tree, ret); \ - return (ret); \ -} \ -a_attr a_type * \ -a_prefix##last(a_tree_type *tree) { \ - a_type *ret; \ - rb_last(a_type, a_field, tree, ret); \ - return (ret); \ -} \ -a_attr a_type * \ -a_prefix##next(a_tree_type *tree, a_type *node) { \ - a_type *ret; \ - rb_next(a_type, a_field, a_cmp, tree, node, ret); \ - return (ret); \ -} \ -a_attr a_type * \ -a_prefix##prev(a_tree_type *tree, a_type *node) { \ - a_type *ret; \ - rb_prev(a_type, a_field, a_cmp, tree, node, ret); \ - return (ret); \ -} \ -a_attr a_type * \ -a_prefix##search(a_tree_type *tree, a_type *key) { \ - a_type *ret; \ - rb_search(a_type, a_field, a_cmp, tree, key, ret); \ - return (ret); \ -} \ -a_attr a_type * \ -a_prefix##nsearch(a_tree_type *tree, a_type *key) { \ - a_type *ret; \ - rb_nsearch(a_type, a_field, a_cmp, tree, key, ret); \ - return (ret); \ -} \ -a_attr a_type * \ -a_prefix##psearch(a_tree_type *tree, a_type *key) { \ - a_type *ret; \ - rb_psearch(a_type, a_field, a_cmp, tree, key, ret); \ - return (ret); \ -} \ -a_attr void \ -a_prefix##insert(a_tree_type *tree, a_type *node) { \ - rb_insert(a_type, a_field, a_cmp, tree, node); \ -} \ -a_attr void \ -a_prefix##remove(a_tree_type *tree, a_type *node) { \ - rb_remove(a_type, a_field, a_cmp, tree, node); \ -} - -/* - * The iterators simulate recursion via an array of pointers that store the - * current path. This is critical to performance, since a series of calls to - * rb_{next,prev}() would require time proportional to (n lg n), whereas this - * implementation only requires time proportional to (n). - * - * Since the iterators cache a path down the tree, any tree modification may - * cause the cached path to become invalid. In order to continue iteration, - * use something like the following sequence: - * - * { - * a_type *node, *tnode; - * - * rb_foreach_begin(a_type, a_field, a_tree, node) { - * ... - * rb_next(a_type, a_field, a_cmp, a_tree, node, tnode); - * rb_remove(a_type, a_field, a_cmp, a_tree, node); - * rb_foreach_next(a_type, a_field, a_cmp, a_tree, tnode); - * ... - * } rb_foreach_end(a_type, a_field, a_tree, node) - * } - * - * Note that this idiom is not advised if every iteration modifies the tree, - * since in that case there is no algorithmic complexity improvement over a - * series of rb_{next,prev}() calls, thus making the setup overhead wasted - * effort. - */ - -#define rb_foreach_begin(a_type, a_field, a_tree, a_var) { \ - /* Compute the maximum possible tree depth (3X the black height). */\ - unsigned rbp_f_height; \ - rbp_black_height(a_type, a_field, a_tree, rbp_f_height); \ - rbp_f_height *= 3; \ - { \ - /* Initialize the path to contain the left spine. */\ - a_type *rbp_f_path[rbp_f_height]; \ - a_type *rbp_f_node; \ - bool rbp_f_synced = false; \ - unsigned rbp_f_depth = 0; \ - if ((a_tree)->rbt_root != &(a_tree)->rbt_nil) { \ - rbp_f_path[rbp_f_depth] = (a_tree)->rbt_root; \ - rbp_f_depth++; \ - while ((rbp_f_node = rbp_left_get(a_type, a_field, \ - rbp_f_path[rbp_f_depth-1])) != &(a_tree)->rbt_nil) { \ - rbp_f_path[rbp_f_depth] = rbp_f_node; \ - rbp_f_depth++; \ - } \ - } \ - /* While the path is non-empty, iterate. */\ - while (rbp_f_depth > 0) { \ - (a_var) = rbp_f_path[rbp_f_depth-1]; - -/* Only use if modifying the tree during iteration. */ -#define rb_foreach_next(a_type, a_field, a_cmp, a_tree, a_node) \ - /* Re-initialize the path to contain the path to a_node. */\ - rbp_f_depth = 0; \ - if (a_node != NULL) { \ - if ((a_tree)->rbt_root != &(a_tree)->rbt_nil) { \ - rbp_f_path[rbp_f_depth] = (a_tree)->rbt_root; \ - rbp_f_depth++; \ - rbp_f_node = rbp_f_path[0]; \ - while (true) { \ - int rbp_f_cmp = (a_cmp)((a_node), \ - rbp_f_path[rbp_f_depth-1]); \ - if (rbp_f_cmp < 0) { \ - rbp_f_node = rbp_left_get(a_type, a_field, \ - rbp_f_path[rbp_f_depth-1]); \ - } else if (rbp_f_cmp > 0) { \ - rbp_f_node = rbp_right_get(a_type, a_field, \ - rbp_f_path[rbp_f_depth-1]); \ - } else { \ - break; \ - } \ - assert(rbp_f_node != &(a_tree)->rbt_nil); \ - rbp_f_path[rbp_f_depth] = rbp_f_node; \ - rbp_f_depth++; \ - } \ - } \ - } \ - rbp_f_synced = true; - -#define rb_foreach_end(a_type, a_field, a_tree, a_var) \ - if (rbp_f_synced) { \ - rbp_f_synced = false; \ - continue; \ - } \ - /* Find the successor. */\ - if ((rbp_f_node = rbp_right_get(a_type, a_field, \ - rbp_f_path[rbp_f_depth-1])) != &(a_tree)->rbt_nil) { \ - /* The successor is the left-most node in the right */\ - /* subtree. */\ - rbp_f_path[rbp_f_depth] = rbp_f_node; \ - rbp_f_depth++; \ - while ((rbp_f_node = rbp_left_get(a_type, a_field, \ - rbp_f_path[rbp_f_depth-1])) != &(a_tree)->rbt_nil) { \ - rbp_f_path[rbp_f_depth] = rbp_f_node; \ - rbp_f_depth++; \ - } \ - } else { \ - /* The successor is above the current node. Unwind */\ - /* until a left-leaning edge is removed from the */\ - /* path, or the path is empty. */\ - for (rbp_f_depth--; rbp_f_depth > 0; rbp_f_depth--) { \ - if (rbp_left_get(a_type, a_field, \ - rbp_f_path[rbp_f_depth-1]) \ - == rbp_f_path[rbp_f_depth]) { \ - break; \ - } \ - } \ - } \ - } \ - } \ -} - -#define rb_foreach_reverse_begin(a_type, a_field, a_tree, a_var) { \ - /* Compute the maximum possible tree depth (3X the black height). */\ - unsigned rbp_fr_height; \ - rbp_black_height(a_type, a_field, a_tree, rbp_fr_height); \ - rbp_fr_height *= 3; \ - { \ - /* Initialize the path to contain the right spine. */\ - a_type *rbp_fr_path[rbp_fr_height]; \ - a_type *rbp_fr_node; \ - bool rbp_fr_synced = false; \ - unsigned rbp_fr_depth = 0; \ - if ((a_tree)->rbt_root != &(a_tree)->rbt_nil) { \ - rbp_fr_path[rbp_fr_depth] = (a_tree)->rbt_root; \ - rbp_fr_depth++; \ - while ((rbp_fr_node = rbp_right_get(a_type, a_field, \ - rbp_fr_path[rbp_fr_depth-1])) != &(a_tree)->rbt_nil) { \ - rbp_fr_path[rbp_fr_depth] = rbp_fr_node; \ - rbp_fr_depth++; \ - } \ - } \ - /* While the path is non-empty, iterate. */\ - while (rbp_fr_depth > 0) { \ - (a_var) = rbp_fr_path[rbp_fr_depth-1]; - -/* Only use if modifying the tree during iteration. */ -#define rb_foreach_reverse_prev(a_type, a_field, a_cmp, a_tree, a_node) \ - /* Re-initialize the path to contain the path to a_node. */\ - rbp_fr_depth = 0; \ - if (a_node != NULL) { \ - if ((a_tree)->rbt_root != &(a_tree)->rbt_nil) { \ - rbp_fr_path[rbp_fr_depth] = (a_tree)->rbt_root; \ - rbp_fr_depth++; \ - rbp_fr_node = rbp_fr_path[0]; \ - while (true) { \ - int rbp_fr_cmp = (a_cmp)((a_node), \ - rbp_fr_path[rbp_fr_depth-1]); \ - if (rbp_fr_cmp < 0) { \ - rbp_fr_node = rbp_left_get(a_type, a_field, \ - rbp_fr_path[rbp_fr_depth-1]); \ - } else if (rbp_fr_cmp > 0) { \ - rbp_fr_node = rbp_right_get(a_type, a_field,\ - rbp_fr_path[rbp_fr_depth-1]); \ - } else { \ - break; \ - } \ - assert(rbp_fr_node != &(a_tree)->rbt_nil); \ - rbp_fr_path[rbp_fr_depth] = rbp_fr_node; \ - rbp_fr_depth++; \ - } \ - } \ - } \ - rbp_fr_synced = true; - -#define rb_foreach_reverse_end(a_type, a_field, a_tree, a_var) \ - if (rbp_fr_synced) { \ - rbp_fr_synced = false; \ - continue; \ - } \ - if (rbp_fr_depth == 0) { \ - /* rb_foreach_reverse_sync() was called with a NULL */\ - /* a_node. */\ - break; \ - } \ - /* Find the predecessor. */\ - if ((rbp_fr_node = rbp_left_get(a_type, a_field, \ - rbp_fr_path[rbp_fr_depth-1])) != &(a_tree)->rbt_nil) { \ - /* The predecessor is the right-most node in the left */\ - /* subtree. */\ - rbp_fr_path[rbp_fr_depth] = rbp_fr_node; \ - rbp_fr_depth++; \ - while ((rbp_fr_node = rbp_right_get(a_type, a_field, \ - rbp_fr_path[rbp_fr_depth-1])) != &(a_tree)->rbt_nil) {\ - rbp_fr_path[rbp_fr_depth] = rbp_fr_node; \ - rbp_fr_depth++; \ - } \ - } else { \ - /* The predecessor is above the current node. Unwind */\ - /* until a right-leaning edge is removed from the */\ - /* path, or the path is empty. */\ - for (rbp_fr_depth--; rbp_fr_depth > 0; rbp_fr_depth--) {\ - if (rbp_right_get(a_type, a_field, \ - rbp_fr_path[rbp_fr_depth-1]) \ - == rbp_fr_path[rbp_fr_depth]) { \ - break; \ - } \ - } \ - } \ - } \ - } \ -} - -#endif /* RB_H_ */ diff --git a/tommyds/benchmark/lib/rb/rb_newer.h b/tommyds/benchmark/lib/rb/rb_newer.h deleted file mode 100644 index eee3f53..0000000 --- a/tommyds/benchmark/lib/rb/rb_newer.h +++ /dev/null @@ -1,970 +0,0 @@ -/*- - ******************************************************************************* - * - * cpp macro implementation of left-leaning 2-3 red-black trees. Parent - * pointers are not used, and color bits are stored in the least significant - * bit of right-child pointers (if RB_COMPACT is defined), thus making node - * linkage as compact as is possible for red-black trees. - * - * Usage: - * - * #include - * #include - * #define NDEBUG // (Optional, see assert(3).) - * #include - * #define RB_COMPACT // (Optional, embed color bits in right-child pointers.) - * #include - * ... - * - ******************************************************************************* - */ - -#ifndef RB_H_ -#define RB_H_ - -#ifdef RB_COMPACT -/* Node structure. */ -#define rb_node(a_type) \ -struct { \ - a_type *rbn_left; \ - a_type *rbn_right_red; \ -} -#else -#define rb_node(a_type) \ -struct { \ - a_type *rbn_left; \ - a_type *rbn_right; \ - bool rbn_red; \ -} -#endif - -/* Root structure. */ -#define rbt(a_type) \ -struct { \ - a_type *rbt_root; \ - a_type rbt_nil; \ -} - -/* Left accessors. */ -#define rbtn_left_get(a_type, a_field, a_node) \ - ((a_node)->a_field.rbn_left) -#define rbtn_left_set(a_type, a_field, a_node, a_left) do { \ - (a_node)->a_field.rbn_left = a_left; \ -} while (0) - -#ifdef RB_COMPACT -/* Right accessors. */ -#define rbtn_right_get(a_type, a_field, a_node) \ - ((a_type *) (((intptr_t) (a_node)->a_field.rbn_right_red) \ - & ((ssize_t)-2))) -#define rbtn_right_set(a_type, a_field, a_node, a_right) do { \ - (a_node)->a_field.rbn_right_red = (a_type *) (((uintptr_t) a_right) \ - | (((uintptr_t) (a_node)->a_field.rbn_right_red) & ((size_t)1))); \ -} while (0) - -/* Color accessors. */ -#define rbtn_red_get(a_type, a_field, a_node) \ - ((bool) (((uintptr_t) (a_node)->a_field.rbn_right_red) \ - & ((size_t)1))) -#define rbtn_color_set(a_type, a_field, a_node, a_red) do { \ - (a_node)->a_field.rbn_right_red = (a_type *) ((((intptr_t) \ - (a_node)->a_field.rbn_right_red) & ((ssize_t)-2)) \ - | ((ssize_t)a_red)); \ -} while (0) -#define rbtn_red_set(a_type, a_field, a_node) do { \ - (a_node)->a_field.rbn_right_red = (a_type *) (((uintptr_t) \ - (a_node)->a_field.rbn_right_red) | ((size_t)1)); \ -} while (0) -#define rbtn_black_set(a_type, a_field, a_node) do { \ - (a_node)->a_field.rbn_right_red = (a_type *) (((intptr_t) \ - (a_node)->a_field.rbn_right_red) & ((ssize_t)-2)); \ -} while (0) -#else -/* Right accessors. */ -#define rbtn_right_get(a_type, a_field, a_node) \ - ((a_node)->a_field.rbn_right) -#define rbtn_right_set(a_type, a_field, a_node, a_right) do { \ - (a_node)->a_field.rbn_right = a_right; \ -} while (0) - -/* Color accessors. */ -#define rbtn_red_get(a_type, a_field, a_node) \ - ((a_node)->a_field.rbn_red) -#define rbtn_color_set(a_type, a_field, a_node, a_red) do { \ - (a_node)->a_field.rbn_red = (a_red); \ -} while (0) -#define rbtn_red_set(a_type, a_field, a_node) do { \ - (a_node)->a_field.rbn_red = true; \ -} while (0) -#define rbtn_black_set(a_type, a_field, a_node) do { \ - (a_node)->a_field.rbn_red = false; \ -} while (0) -#endif - -/* Node initializer. */ -#define rbt_node_new(a_type, a_field, a_rbt, a_node) do { \ - rbtn_left_set(a_type, a_field, (a_node), &(a_rbt)->rbt_nil); \ - rbtn_right_set(a_type, a_field, (a_node), &(a_rbt)->rbt_nil); \ - rbtn_red_set(a_type, a_field, (a_node)); \ -} while (0) - -/* Tree initializer. */ -#define rb_new(a_type, a_field, a_rbt) do { \ - (a_rbt)->rbt_root = &(a_rbt)->rbt_nil; \ - rbt_node_new(a_type, a_field, a_rbt, &(a_rbt)->rbt_nil); \ - rbtn_black_set(a_type, a_field, &(a_rbt)->rbt_nil); \ -} while (0) - -/* Internal utility macros. */ -#define rbtn_first(a_type, a_field, a_rbt, a_root, r_node) do { \ - (r_node) = (a_root); \ - if ((r_node) != &(a_rbt)->rbt_nil) { \ - for (; \ - rbtn_left_get(a_type, a_field, (r_node)) != &(a_rbt)->rbt_nil;\ - (r_node) = rbtn_left_get(a_type, a_field, (r_node))) { \ - } \ - } \ -} while (0) - -#define rbtn_last(a_type, a_field, a_rbt, a_root, r_node) do { \ - (r_node) = (a_root); \ - if ((r_node) != &(a_rbt)->rbt_nil) { \ - for (; rbtn_right_get(a_type, a_field, (r_node)) != \ - &(a_rbt)->rbt_nil; (r_node) = rbtn_right_get(a_type, a_field, \ - (r_node))) { \ - } \ - } \ -} while (0) - -#define rbtn_rotate_left(a_type, a_field, a_node, r_node) do { \ - (r_node) = rbtn_right_get(a_type, a_field, (a_node)); \ - rbtn_right_set(a_type, a_field, (a_node), \ - rbtn_left_get(a_type, a_field, (r_node))); \ - rbtn_left_set(a_type, a_field, (r_node), (a_node)); \ -} while (0) - -#define rbtn_rotate_right(a_type, a_field, a_node, r_node) do { \ - (r_node) = rbtn_left_get(a_type, a_field, (a_node)); \ - rbtn_left_set(a_type, a_field, (a_node), \ - rbtn_right_get(a_type, a_field, (r_node))); \ - rbtn_right_set(a_type, a_field, (r_node), (a_node)); \ -} while (0) - -/* - * The rb_proto() macro generates function prototypes that correspond to the - * functions generated by an equivalently parameterized call to rb_gen(). - */ - -#define rb_proto(a_attr, a_prefix, a_rbt_type, a_type) \ -a_attr void \ -a_prefix##new(a_rbt_type *rbtree); \ -a_attr a_type * \ -a_prefix##first(a_rbt_type *rbtree); \ -a_attr a_type * \ -a_prefix##last(a_rbt_type *rbtree); \ -a_attr a_type * \ -a_prefix##next(a_rbt_type *rbtree, a_type *node); \ -a_attr a_type * \ -a_prefix##prev(a_rbt_type *rbtree, a_type *node); \ -a_attr a_type * \ -a_prefix##search(a_rbt_type *rbtree, a_type *key); \ -a_attr a_type * \ -a_prefix##nsearch(a_rbt_type *rbtree, a_type *key); \ -a_attr a_type * \ -a_prefix##psearch(a_rbt_type *rbtree, a_type *key); \ -a_attr void \ -a_prefix##insert(a_rbt_type *rbtree, a_type *node); \ -a_attr void \ -a_prefix##remove(a_rbt_type *rbtree, a_type *node); \ -a_attr a_type * \ -a_prefix##iter(a_rbt_type *rbtree, a_type *start, a_type *(*cb)( \ - a_rbt_type *, a_type *, void *), void *arg); \ -a_attr a_type * \ -a_prefix##reverse_iter(a_rbt_type *rbtree, a_type *start, \ - a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg); - -/* - * The rb_gen() macro generates a type-specific red-black tree implementation, - * based on the above cpp macros. - * - * Arguments: - * - * a_attr : Function attribute for generated functions (ex: static). - * a_prefix : Prefix for generated functions (ex: extree_). - * a_rb_type : Type for red-black tree data structure (ex: extree_t). - * a_type : Type for red-black tree node data structure (ex: - * extree_node_t). - * a_field : Name of red-black tree node linkage (ex: extree_link). - * a_cmp : Node comparison function name, with the following prototype: - * int (a_cmp *)(a_type *a_node, a_type *a_other); - * ^^^^^^ - * or a_key - * Interpretation of comparision function return values: - * -1 : a_node < a_other - * 0 : a_node == a_other - * 1 : a_node > a_other - * In all cases, the a_node or a_key macro argument is the first - * argument to the comparison function, which makes it possible - * to write comparison functions that treat the first argument - * specially. - * - * Assuming the following setup: - * - * typedef struct ex_node_s ex_node_t; - * struct ex_node_s { - * rb_node(ex_node_t) ex_link; - * }; - * typedef rb(ex_node_t) ex_t; - * rb_gen(static, ex_, ex_t, ex_node_t, ex_link, ex_cmp, 1297, 1301) - * - * The following API is generated: - * - * static void - * ex_new(ex_t *extree); - * Description: Initialize a red-black tree structure. - * Args: - * extree: Pointer to an uninitialized red-black tree object. - * - * static ex_node_t * - * ex_first(ex_t *extree); - * static ex_node_t * - * ex_last(ex_t *extree); - * Description: Get the first/last node in extree. - * Args: - * extree: Pointer to an initialized red-black tree object. - * Ret: First/last node in extree, or NULL if extree is empty. - * - * static ex_node_t * - * ex_next(ex_t *extree, ex_node_t *node); - * static ex_node_t * - * ex_prev(ex_t *extree, ex_node_t *node); - * Description: Get node's successor/predecessor. - * Args: - * extree: Pointer to an initialized red-black tree object. - * node : A node in extree. - * Ret: node's successor/predecessor in extree, or NULL if node is - * last/first. - * - * static ex_node_t * - * ex_search(ex_t *extree, ex_node_t *key); - * Description: Search for node that matches key. - * Args: - * extree: Pointer to an initialized red-black tree object. - * key : Search key. - * Ret: Node in extree that matches key, or NULL if no match. - * - * static ex_node_t * - * ex_nsearch(ex_t *extree, ex_node_t *key); - * static ex_node_t * - * ex_psearch(ex_t *extree, ex_node_t *key); - * Description: Search for node that matches key. If no match is found, - * return what would be key's successor/predecessor, were - * key in extree. - * Args: - * extree: Pointer to an initialized red-black tree object. - * key : Search key. - * Ret: Node in extree that matches key, or if no match, hypothetical - * node's successor/predecessor (NULL if no successor/predecessor). - * - * static void - * ex_insert(ex_t *extree, ex_node_t *node); - * Description: Insert node into extree. - * Args: - * extree: Pointer to an initialized red-black tree object. - * node : Node to be inserted into extree. - * - * static void - * ex_remove(ex_t *extree, ex_node_t *node); - * Description: Remove node from extree. - * Args: - * extree: Pointer to an initialized red-black tree object. - * node : Node in extree to be removed. - * - * static ex_node_t * - * ex_iter(ex_t *extree, ex_node_t *start, ex_node_t *(*cb)(ex_t *, - * ex_node_t *, void *), void *arg); - * static ex_node_t * - * ex_reverse_iter(ex_t *extree, ex_node_t *start, ex_node *(*cb)(ex_t *, - * ex_node_t *, void *), void *arg); - * Description: Iterate forward/backward over extree, starting at node. - * If extree is modified, iteration must be immediately - * terminated by the callback function that causes the - * modification. - * Args: - * extree: Pointer to an initialized red-black tree object. - * start : Node at which to start iteration, or NULL to start at - * first/last node. - * cb : Callback function, which is called for each node during - * iteration. Under normal circumstances the callback function - * should return NULL, which causes iteration to continue. If a - * callback function returns non-NULL, iteration is immediately - * terminated and the non-NULL return value is returned by the - * iterator. This is useful for re-starting iteration after - * modifying extree. - * arg : Opaque pointer passed to cb(). - * Ret: NULL if iteration completed, or the non-NULL callback return value - * that caused termination of the iteration. - */ -#define rb_gen(a_attr, a_prefix, a_rbt_type, a_type, a_field, a_cmp) \ -a_attr void \ -a_prefix##new(a_rbt_type *rbtree) { \ - rb_new(a_type, a_field, rbtree); \ -} \ -a_attr a_type * \ -a_prefix##first(a_rbt_type *rbtree) { \ - a_type *ret; \ - rbtn_first(a_type, a_field, rbtree, rbtree->rbt_root, ret); \ - if (ret == &rbtree->rbt_nil) { \ - ret = NULL; \ - } \ - return (ret); \ -} \ -a_attr a_type * \ -a_prefix##last(a_rbt_type *rbtree) { \ - a_type *ret; \ - rbtn_last(a_type, a_field, rbtree, rbtree->rbt_root, ret); \ - if (ret == &rbtree->rbt_nil) { \ - ret = NULL; \ - } \ - return (ret); \ -} \ -a_attr a_type * \ -a_prefix##next(a_rbt_type *rbtree, a_type *node) { \ - a_type *ret; \ - if (rbtn_right_get(a_type, a_field, node) != &rbtree->rbt_nil) { \ - rbtn_first(a_type, a_field, rbtree, rbtn_right_get(a_type, \ - a_field, node), ret); \ - } else { \ - a_type *tnode = rbtree->rbt_root; \ - assert(tnode != &rbtree->rbt_nil); \ - ret = &rbtree->rbt_nil; \ - while (true) { \ - int cmp = (a_cmp)(node, tnode); \ - if (cmp < 0) { \ - ret = tnode; \ - tnode = rbtn_left_get(a_type, a_field, tnode); \ - } else if (cmp > 0) { \ - tnode = rbtn_right_get(a_type, a_field, tnode); \ - } else { \ - break; \ - } \ - assert(tnode != &rbtree->rbt_nil); \ - } \ - } \ - if (ret == &rbtree->rbt_nil) { \ - ret = (NULL); \ - } \ - return (ret); \ -} \ -a_attr a_type * \ -a_prefix##prev(a_rbt_type *rbtree, a_type *node) { \ - a_type *ret; \ - if (rbtn_left_get(a_type, a_field, node) != &rbtree->rbt_nil) { \ - rbtn_last(a_type, a_field, rbtree, rbtn_left_get(a_type, \ - a_field, node), ret); \ - } else { \ - a_type *tnode = rbtree->rbt_root; \ - assert(tnode != &rbtree->rbt_nil); \ - ret = &rbtree->rbt_nil; \ - while (true) { \ - int cmp = (a_cmp)(node, tnode); \ - if (cmp < 0) { \ - tnode = rbtn_left_get(a_type, a_field, tnode); \ - } else if (cmp > 0) { \ - ret = tnode; \ - tnode = rbtn_right_get(a_type, a_field, tnode); \ - } else { \ - break; \ - } \ - assert(tnode != &rbtree->rbt_nil); \ - } \ - } \ - if (ret == &rbtree->rbt_nil) { \ - ret = (NULL); \ - } \ - return (ret); \ -} \ -a_attr a_type * \ -a_prefix##search(a_rbt_type *rbtree, a_type *key) { \ - a_type *ret; \ - int cmp; \ - ret = rbtree->rbt_root; \ - while (ret != &rbtree->rbt_nil \ - && (cmp = (a_cmp)(key, ret)) != 0) { \ - if (cmp < 0) { \ - ret = rbtn_left_get(a_type, a_field, ret); \ - } else { \ - ret = rbtn_right_get(a_type, a_field, ret); \ - } \ - } \ - if (ret == &rbtree->rbt_nil) { \ - ret = (NULL); \ - } \ - return (ret); \ -} \ -a_attr a_type * \ -a_prefix##nsearch(a_rbt_type *rbtree, a_type *key) { \ - a_type *ret; \ - a_type *tnode = rbtree->rbt_root; \ - ret = &rbtree->rbt_nil; \ - while (tnode != &rbtree->rbt_nil) { \ - int cmp = (a_cmp)(key, tnode); \ - if (cmp < 0) { \ - ret = tnode; \ - tnode = rbtn_left_get(a_type, a_field, tnode); \ - } else if (cmp > 0) { \ - tnode = rbtn_right_get(a_type, a_field, tnode); \ - } else { \ - ret = tnode; \ - break; \ - } \ - } \ - if (ret == &rbtree->rbt_nil) { \ - ret = (NULL); \ - } \ - return (ret); \ -} \ -a_attr a_type * \ -a_prefix##psearch(a_rbt_type *rbtree, a_type *key) { \ - a_type *ret; \ - a_type *tnode = rbtree->rbt_root; \ - ret = &rbtree->rbt_nil; \ - while (tnode != &rbtree->rbt_nil) { \ - int cmp = (a_cmp)(key, tnode); \ - if (cmp < 0) { \ - tnode = rbtn_left_get(a_type, a_field, tnode); \ - } else if (cmp > 0) { \ - ret = tnode; \ - tnode = rbtn_right_get(a_type, a_field, tnode); \ - } else { \ - ret = tnode; \ - break; \ - } \ - } \ - if (ret == &rbtree->rbt_nil) { \ - ret = (NULL); \ - } \ - return (ret); \ -} \ -a_attr void \ -a_prefix##insert(a_rbt_type *rbtree, a_type *node) { \ - struct { \ - a_type *node; \ - int cmp; \ - } path[sizeof(void *) << 4], *pathp; \ - rbt_node_new(a_type, a_field, rbtree, node); \ - /* Wind. */ \ - path->node = rbtree->rbt_root; \ - for (pathp = path; pathp->node != &rbtree->rbt_nil; pathp++) { \ - int cmp = pathp->cmp = a_cmp(node, pathp->node); \ - assert(cmp != 0); \ - if (cmp < 0) { \ - pathp[1].node = rbtn_left_get(a_type, a_field, \ - pathp->node); \ - } else { \ - pathp[1].node = rbtn_right_get(a_type, a_field, \ - pathp->node); \ - } \ - } \ - pathp->node = node; \ - /* Unwind. */ \ - for (pathp--; (uintptr_t)pathp >= (uintptr_t)path; pathp--) { \ - a_type *cnode = pathp->node; \ - if (pathp->cmp < 0) { \ - a_type *left = pathp[1].node; \ - rbtn_left_set(a_type, a_field, cnode, left); \ - if (rbtn_red_get(a_type, a_field, left)) { \ - a_type *leftleft = rbtn_left_get(a_type, a_field, left);\ - if (rbtn_red_get(a_type, a_field, leftleft)) { \ - /* Fix up 4-node. */ \ - a_type *tnode; \ - rbtn_black_set(a_type, a_field, leftleft); \ - rbtn_rotate_right(a_type, a_field, cnode, tnode); \ - cnode = tnode; \ - } \ - } else { \ - return; \ - } \ - } else { \ - a_type *right = pathp[1].node; \ - rbtn_right_set(a_type, a_field, cnode, right); \ - if (rbtn_red_get(a_type, a_field, right)) { \ - a_type *left = rbtn_left_get(a_type, a_field, cnode); \ - if (rbtn_red_get(a_type, a_field, left)) { \ - /* Split 4-node. */ \ - rbtn_black_set(a_type, a_field, left); \ - rbtn_black_set(a_type, a_field, right); \ - rbtn_red_set(a_type, a_field, cnode); \ - } else { \ - /* Lean left. */ \ - a_type *tnode; \ - bool tred = rbtn_red_get(a_type, a_field, cnode); \ - rbtn_rotate_left(a_type, a_field, cnode, tnode); \ - rbtn_color_set(a_type, a_field, tnode, tred); \ - rbtn_red_set(a_type, a_field, cnode); \ - cnode = tnode; \ - } \ - } else { \ - return; \ - } \ - } \ - pathp->node = cnode; \ - } \ - /* Set root, and make it black. */ \ - rbtree->rbt_root = path->node; \ - rbtn_black_set(a_type, a_field, rbtree->rbt_root); \ -} \ -a_attr void \ -a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \ - struct { \ - a_type *node; \ - int cmp; \ - } *pathp, *nodep, path[sizeof(void *) << 4]; \ - /* Wind. */ \ - nodep = NULL; /* Silence compiler warning. */ \ - path->node = rbtree->rbt_root; \ - for (pathp = path; pathp->node != &rbtree->rbt_nil; pathp++) { \ - int cmp = pathp->cmp = a_cmp(node, pathp->node); \ - if (cmp < 0) { \ - pathp[1].node = rbtn_left_get(a_type, a_field, \ - pathp->node); \ - } else { \ - pathp[1].node = rbtn_right_get(a_type, a_field, \ - pathp->node); \ - if (cmp == 0) { \ - /* Find node's successor, in preparation for swap. */ \ - pathp->cmp = 1; \ - nodep = pathp; \ - for (pathp++; pathp->node != &rbtree->rbt_nil; \ - pathp++) { \ - pathp->cmp = -1; \ - pathp[1].node = rbtn_left_get(a_type, a_field, \ - pathp->node); \ - } \ - break; \ - } \ - } \ - } \ - assert(nodep->node == node); \ - pathp--; \ - if (pathp->node != node) { \ - /* Swap node with its successor. */ \ - bool tred = rbtn_red_get(a_type, a_field, pathp->node); \ - rbtn_color_set(a_type, a_field, pathp->node, \ - rbtn_red_get(a_type, a_field, node)); \ - rbtn_left_set(a_type, a_field, pathp->node, \ - rbtn_left_get(a_type, a_field, node)); \ - /* If node's successor is its right child, the following code */\ - /* will do the wrong thing for the right child pointer. */\ - /* However, it doesn't matter, because the pointer will be */\ - /* properly set when the successor is pruned. */\ - rbtn_right_set(a_type, a_field, pathp->node, \ - rbtn_right_get(a_type, a_field, node)); \ - rbtn_color_set(a_type, a_field, node, tred); \ - /* The pruned leaf node's child pointers are never accessed */\ - /* again, so don't bother setting them to nil. */\ - nodep->node = pathp->node; \ - pathp->node = node; \ - if (nodep == path) { \ - rbtree->rbt_root = nodep->node; \ - } else { \ - if (nodep[-1].cmp < 0) { \ - rbtn_left_set(a_type, a_field, nodep[-1].node, \ - nodep->node); \ - } else { \ - rbtn_right_set(a_type, a_field, nodep[-1].node, \ - nodep->node); \ - } \ - } \ - } else { \ - a_type *left = rbtn_left_get(a_type, a_field, node); \ - if (left != &rbtree->rbt_nil) { \ - /* node has no successor, but it has a left child. */\ - /* Splice node out, without losing the left child. */\ - assert(rbtn_red_get(a_type, a_field, node) == false); \ - assert(rbtn_red_get(a_type, a_field, left)); \ - rbtn_black_set(a_type, a_field, left); \ - if (pathp == path) { \ - rbtree->rbt_root = left; \ - } else { \ - if (pathp[-1].cmp < 0) { \ - rbtn_left_set(a_type, a_field, pathp[-1].node, \ - left); \ - } else { \ - rbtn_right_set(a_type, a_field, pathp[-1].node, \ - left); \ - } \ - } \ - return; \ - } else if (pathp == path) { \ - /* The tree only contained one node. */ \ - rbtree->rbt_root = &rbtree->rbt_nil; \ - return; \ - } \ - } \ - if (rbtn_red_get(a_type, a_field, pathp->node)) { \ - /* Prune red node, which requires no fixup. */ \ - assert(pathp[-1].cmp < 0); \ - rbtn_left_set(a_type, a_field, pathp[-1].node, \ - &rbtree->rbt_nil); \ - return; \ - } \ - /* The node to be pruned is black, so unwind until balance is */\ - /* restored. */\ - pathp->node = &rbtree->rbt_nil; \ - for (pathp--; (uintptr_t)pathp >= (uintptr_t)path; pathp--) { \ - assert(pathp->cmp != 0); \ - if (pathp->cmp < 0) { \ - rbtn_left_set(a_type, a_field, pathp->node, \ - pathp[1].node); \ - assert(rbtn_red_get(a_type, a_field, pathp[1].node) \ - == false); \ - if (rbtn_red_get(a_type, a_field, pathp->node)) { \ - a_type *right = rbtn_right_get(a_type, a_field, \ - pathp->node); \ - a_type *rightleft = rbtn_left_get(a_type, a_field, \ - right); \ - a_type *tnode; \ - if (rbtn_red_get(a_type, a_field, rightleft)) { \ - /* In the following diagrams, ||, //, and \\ */\ - /* indicate the path to the removed node. */\ - /* */\ - /* || */\ - /* pathp(r) */\ - /* // \ */\ - /* (b) (b) */\ - /* / */\ - /* (r) */\ - /* */\ - rbtn_black_set(a_type, a_field, pathp->node); \ - rbtn_rotate_right(a_type, a_field, right, tnode); \ - rbtn_right_set(a_type, a_field, pathp->node, tnode);\ - rbtn_rotate_left(a_type, a_field, pathp->node, \ - tnode); \ - } else { \ - /* || */\ - /* pathp(r) */\ - /* // \ */\ - /* (b) (b) */\ - /* / */\ - /* (b) */\ - /* */\ - rbtn_rotate_left(a_type, a_field, pathp->node, \ - tnode); \ - } \ - /* Balance restored, but rotation modified subtree */\ - /* root. */\ - assert((uintptr_t)pathp > (uintptr_t)path); \ - if (pathp[-1].cmp < 0) { \ - rbtn_left_set(a_type, a_field, pathp[-1].node, \ - tnode); \ - } else { \ - rbtn_right_set(a_type, a_field, pathp[-1].node, \ - tnode); \ - } \ - return; \ - } else { \ - a_type *right = rbtn_right_get(a_type, a_field, \ - pathp->node); \ - a_type *rightleft = rbtn_left_get(a_type, a_field, \ - right); \ - if (rbtn_red_get(a_type, a_field, rightleft)) { \ - /* || */\ - /* pathp(b) */\ - /* // \ */\ - /* (b) (b) */\ - /* / */\ - /* (r) */\ - a_type *tnode; \ - rbtn_black_set(a_type, a_field, rightleft); \ - rbtn_rotate_right(a_type, a_field, right, tnode); \ - rbtn_right_set(a_type, a_field, pathp->node, tnode);\ - rbtn_rotate_left(a_type, a_field, pathp->node, \ - tnode); \ - /* Balance restored, but rotation modified */\ - /* subree root, which may actually be the tree */\ - /* root. */\ - if (pathp == path) { \ - /* Set root. */ \ - rbtree->rbt_root = tnode; \ - } else { \ - if (pathp[-1].cmp < 0) { \ - rbtn_left_set(a_type, a_field, \ - pathp[-1].node, tnode); \ - } else { \ - rbtn_right_set(a_type, a_field, \ - pathp[-1].node, tnode); \ - } \ - } \ - return; \ - } else { \ - /* || */\ - /* pathp(b) */\ - /* // \ */\ - /* (b) (b) */\ - /* / */\ - /* (b) */\ - a_type *tnode; \ - rbtn_red_set(a_type, a_field, pathp->node); \ - rbtn_rotate_left(a_type, a_field, pathp->node, \ - tnode); \ - pathp->node = tnode; \ - } \ - } \ - } else { \ - a_type *left; \ - rbtn_right_set(a_type, a_field, pathp->node, \ - pathp[1].node); \ - left = rbtn_left_get(a_type, a_field, pathp->node); \ - if (rbtn_red_get(a_type, a_field, left)) { \ - a_type *tnode; \ - a_type *leftright = rbtn_right_get(a_type, a_field, \ - left); \ - a_type *leftrightleft = rbtn_left_get(a_type, a_field, \ - leftright); \ - if (rbtn_red_get(a_type, a_field, leftrightleft)) { \ - /* || */\ - /* pathp(b) */\ - /* / \\ */\ - /* (r) (b) */\ - /* \ */\ - /* (b) */\ - /* / */\ - /* (r) */\ - a_type *unode; \ - rbtn_black_set(a_type, a_field, leftrightleft); \ - rbtn_rotate_right(a_type, a_field, pathp->node, \ - unode); \ - rbtn_rotate_right(a_type, a_field, pathp->node, \ - tnode); \ - rbtn_right_set(a_type, a_field, unode, tnode); \ - rbtn_rotate_left(a_type, a_field, unode, tnode); \ - } else { \ - /* || */\ - /* pathp(b) */\ - /* / \\ */\ - /* (r) (b) */\ - /* \ */\ - /* (b) */\ - /* / */\ - /* (b) */\ - assert(leftright != &rbtree->rbt_nil); \ - rbtn_red_set(a_type, a_field, leftright); \ - rbtn_rotate_right(a_type, a_field, pathp->node, \ - tnode); \ - rbtn_black_set(a_type, a_field, tnode); \ - } \ - /* Balance restored, but rotation modified subtree */\ - /* root, which may actually be the tree root. */\ - if (pathp == path) { \ - /* Set root. */ \ - rbtree->rbt_root = tnode; \ - } else { \ - if (pathp[-1].cmp < 0) { \ - rbtn_left_set(a_type, a_field, pathp[-1].node, \ - tnode); \ - } else { \ - rbtn_right_set(a_type, a_field, pathp[-1].node, \ - tnode); \ - } \ - } \ - return; \ - } else if (rbtn_red_get(a_type, a_field, pathp->node)) { \ - a_type *leftleft = rbtn_left_get(a_type, a_field, left);\ - if (rbtn_red_get(a_type, a_field, leftleft)) { \ - /* || */\ - /* pathp(r) */\ - /* / \\ */\ - /* (b) (b) */\ - /* / */\ - /* (r) */\ - a_type *tnode; \ - rbtn_black_set(a_type, a_field, pathp->node); \ - rbtn_red_set(a_type, a_field, left); \ - rbtn_black_set(a_type, a_field, leftleft); \ - rbtn_rotate_right(a_type, a_field, pathp->node, \ - tnode); \ - /* Balance restored, but rotation modified */\ - /* subtree root. */\ - assert((uintptr_t)pathp > (uintptr_t)path); \ - if (pathp[-1].cmp < 0) { \ - rbtn_left_set(a_type, a_field, pathp[-1].node, \ - tnode); \ - } else { \ - rbtn_right_set(a_type, a_field, pathp[-1].node, \ - tnode); \ - } \ - return; \ - } else { \ - /* || */\ - /* pathp(r) */\ - /* / \\ */\ - /* (b) (b) */\ - /* / */\ - /* (b) */\ - rbtn_red_set(a_type, a_field, left); \ - rbtn_black_set(a_type, a_field, pathp->node); \ - /* Balance restored. */ \ - return; \ - } \ - } else { \ - a_type *leftleft = rbtn_left_get(a_type, a_field, left);\ - if (rbtn_red_get(a_type, a_field, leftleft)) { \ - /* || */\ - /* pathp(b) */\ - /* / \\ */\ - /* (b) (b) */\ - /* / */\ - /* (r) */\ - a_type *tnode; \ - rbtn_black_set(a_type, a_field, leftleft); \ - rbtn_rotate_right(a_type, a_field, pathp->node, \ - tnode); \ - /* Balance restored, but rotation modified */\ - /* subtree root, which may actually be the tree */\ - /* root. */\ - if (pathp == path) { \ - /* Set root. */ \ - rbtree->rbt_root = tnode; \ - } else { \ - if (pathp[-1].cmp < 0) { \ - rbtn_left_set(a_type, a_field, \ - pathp[-1].node, tnode); \ - } else { \ - rbtn_right_set(a_type, a_field, \ - pathp[-1].node, tnode); \ - } \ - } \ - return; \ - } else { \ - /* || */\ - /* pathp(b) */\ - /* / \\ */\ - /* (b) (b) */\ - /* / */\ - /* (b) */\ - rbtn_red_set(a_type, a_field, left); \ - } \ - } \ - } \ - } \ - /* Set root. */ \ - rbtree->rbt_root = path->node; \ - assert(rbtn_red_get(a_type, a_field, rbtree->rbt_root) == false); \ -} \ -a_attr a_type * \ -a_prefix##iter_recurse(a_rbt_type *rbtree, a_type *node, \ - a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg) { \ - if (node == &rbtree->rbt_nil) { \ - return (&rbtree->rbt_nil); \ - } else { \ - a_type *ret; \ - if ((ret = a_prefix##iter_recurse(rbtree, rbtn_left_get(a_type, \ - a_field, node), cb, arg)) != &rbtree->rbt_nil \ - || (ret = cb(rbtree, node, arg)) != NULL) { \ - return (ret); \ - } \ - return (a_prefix##iter_recurse(rbtree, rbtn_right_get(a_type, \ - a_field, node), cb, arg)); \ - } \ -} \ -a_attr a_type * \ -a_prefix##iter_start(a_rbt_type *rbtree, a_type *start, a_type *node, \ - a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg) { \ - int cmp = a_cmp(start, node); \ - if (cmp < 0) { \ - a_type *ret; \ - if ((ret = a_prefix##iter_start(rbtree, start, \ - rbtn_left_get(a_type, a_field, node), cb, arg)) != \ - &rbtree->rbt_nil || (ret = cb(rbtree, node, arg)) != NULL) { \ - return (ret); \ - } \ - return (a_prefix##iter_recurse(rbtree, rbtn_right_get(a_type, \ - a_field, node), cb, arg)); \ - } else if (cmp > 0) { \ - return (a_prefix##iter_start(rbtree, start, \ - rbtn_right_get(a_type, a_field, node), cb, arg)); \ - } else { \ - a_type *ret; \ - if ((ret = cb(rbtree, node, arg)) != NULL) { \ - return (ret); \ - } \ - return (a_prefix##iter_recurse(rbtree, rbtn_right_get(a_type, \ - a_field, node), cb, arg)); \ - } \ -} \ -a_attr a_type * \ -a_prefix##iter(a_rbt_type *rbtree, a_type *start, a_type *(*cb)( \ - a_rbt_type *, a_type *, void *), void *arg) { \ - a_type *ret; \ - if (start != NULL) { \ - ret = a_prefix##iter_start(rbtree, start, rbtree->rbt_root, \ - cb, arg); \ - } else { \ - ret = a_prefix##iter_recurse(rbtree, rbtree->rbt_root, cb, arg);\ - } \ - if (ret == &rbtree->rbt_nil) { \ - ret = NULL; \ - } \ - return (ret); \ -} \ -a_attr a_type * \ -a_prefix##reverse_iter_recurse(a_rbt_type *rbtree, a_type *node, \ - a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg) { \ - if (node == &rbtree->rbt_nil) { \ - return (&rbtree->rbt_nil); \ - } else { \ - a_type *ret; \ - if ((ret = a_prefix##reverse_iter_recurse(rbtree, \ - rbtn_right_get(a_type, a_field, node), cb, arg)) != \ - &rbtree->rbt_nil || (ret = cb(rbtree, node, arg)) != NULL) { \ - return (ret); \ - } \ - return (a_prefix##reverse_iter_recurse(rbtree, \ - rbtn_left_get(a_type, a_field, node), cb, arg)); \ - } \ -} \ -a_attr a_type * \ -a_prefix##reverse_iter_start(a_rbt_type *rbtree, a_type *start, \ - a_type *node, a_type *(*cb)(a_rbt_type *, a_type *, void *), \ - void *arg) { \ - int cmp = a_cmp(start, node); \ - if (cmp > 0) { \ - a_type *ret; \ - if ((ret = a_prefix##reverse_iter_start(rbtree, start, \ - rbtn_right_get(a_type, a_field, node), cb, arg)) != \ - &rbtree->rbt_nil || (ret = cb(rbtree, node, arg)) != NULL) { \ - return (ret); \ - } \ - return (a_prefix##reverse_iter_recurse(rbtree, \ - rbtn_left_get(a_type, a_field, node), cb, arg)); \ - } else if (cmp < 0) { \ - return (a_prefix##reverse_iter_start(rbtree, start, \ - rbtn_left_get(a_type, a_field, node), cb, arg)); \ - } else { \ - a_type *ret; \ - if ((ret = cb(rbtree, node, arg)) != NULL) { \ - return (ret); \ - } \ - return (a_prefix##reverse_iter_recurse(rbtree, \ - rbtn_left_get(a_type, a_field, node), cb, arg)); \ - } \ -} \ -a_attr a_type * \ -a_prefix##reverse_iter(a_rbt_type *rbtree, a_type *start, \ - a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg) { \ - a_type *ret; \ - if (start != NULL) { \ - ret = a_prefix##reverse_iter_start(rbtree, start, \ - rbtree->rbt_root, cb, arg); \ - } else { \ - ret = a_prefix##reverse_iter_recurse(rbtree, rbtree->rbt_root, \ - cb, arg); \ - } \ - if (ret == &rbtree->rbt_nil) { \ - ret = NULL; \ - } \ - return (ret); \ -} - -#endif /* RB_H_ */ diff --git a/tommyds/benchmark/lib/rb/rb_old.h b/tommyds/benchmark/lib/rb/rb_old.h deleted file mode 100644 index f1c9d33..0000000 --- a/tommyds/benchmark/lib/rb/rb_old.h +++ /dev/null @@ -1,532 +0,0 @@ -/****************************************************************************** - * - * Copyright (C) 2002 Jason Evans . - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice(s), this list of conditions and the following disclaimer - * unmodified other than the allowable addition of one or more - * copyright notices. - * 2. Redistributions in binary form must reproduce the above copyright - * notice(s), this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 HOLDER(S) 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. - * - ****************************************************************************** - * - * cpp macro implementation of red-black trees. Red-black trees are difficult - * to explain without lots of diagrams, so little attempt is made to document - * this code. However, an excellent discussion can be found in the following - * book, which was used as the reference for writing this implementation: - * - * Introduction to Algorithms - * Thomas H. Cormen, Charles E. Leiserson, and Ronald L. Rivest - * MIT Press (1990) - * ISBN 0-07-013143-0 - * - * Some macros use a comparison function pointer, which is expected to have the - * following prototype: - * - * int (compare *)( *a_a, *a_b); - * - * Interpretation of comparision function return values: - * - * -1 : a_a < a_b - * 0 : a_a == a_b - * 1 : a_a > a_b - * - * Some of the macros expand out to be quite a bit of code, so if they are - * called in a program in more than a couple of places for a particular type, it - * is probably a good idea to create functions that wrap the macros to keep code - * size down. - * - ******************************************************************************/ - -/* Node structure. */ -#define rb_node(a_type) \ -struct \ -{ \ - a_type *rbn_par; \ - a_type *rbn_left; \ - a_type *rbn_right; \ - bool rbn_red; \ -} - -#define rb_node_new(a_tree, a_node, a_field) \ - do \ - { \ - (a_node)->a_field.rbn_par = &(a_tree)->rbt_nil; \ - (a_node)->a_field.rbn_left = &(a_tree)->rbt_nil; \ - (a_node)->a_field.rbn_right = &(a_tree)->rbt_nil; \ - (a_node)->a_field.rbn_red = false; \ - } while (0) - -/* Root structure. */ -#define rb_tree(a_type) \ -struct \ -{ \ - a_type *rbt_root; \ - a_type rbt_nil; \ -} - -#define rb_tree_new(a_tree, a_field) \ - do \ - { \ - (a_tree)->rbt_root = &((a_tree)->rbt_nil); \ - rb_node_new(a_tree, &(a_tree)->rbt_nil, a_field); \ - } while (0) - -#define rb_tree_nil(a_tree) (&(a_tree)->rbt_nil) - -/* Operations. */ -#define rb_root(a_tree) (a_tree)->rbt_root - -#define rb_p_first(a_tree, a_root, a_field, r_node) \ - do \ - { \ - for ((r_node) = (a_root); \ - (r_node)->a_field.rbn_left != &(a_tree)->rbt_nil; \ - (r_node) = (r_node)->a_field.rbn_left) \ - { \ - } \ - } while (0) - -#define rb_p_last(a_tree, a_root, a_field, r_node) \ - do \ - { \ - for ((r_node) = (a_root); \ - (r_node)->a_field.rbn_right != &(a_tree)->rbt_nil; \ - (r_node) = (r_node)->a_field.rbn_right) \ - { \ - } \ - } while (0) - -#define rb_first(a_tree, a_field, r_node) \ - rb_p_first(a_tree, rb_root(a_tree), a_field, r_node) - -#define rb_last(a_tree, a_field, r_node) \ - rb_p_last(a_tree, rb_root(a_tree), a_field, r_node) - -#define rb_next(a_tree, a_node, a_type, a_field, r_node) \ - do \ - { \ - if ((a_node)->a_field.rbn_right != &(a_tree)->rbt_nil) \ - { \ - rb_p_first(a_tree, (a_node)->a_field.rbn_right, a_field, \ - r_node); \ - } \ - else \ - { \ - a_type *t = (a_node); \ - (r_node) = (a_node)->a_field.rbn_par; \ - while ((r_node) != &(a_tree)->rbt_nil \ - && t == (r_node)->a_field.rbn_right) \ - { \ - t = (r_node); \ - (r_node) = (r_node)->a_field.rbn_par; \ - } \ - } \ - } while (0) - -#define rb_prev(a_tree, a_node, a_type, a_field, r_node) \ - do \ - { \ - if ((a_node)->a_field.rbn_left != &(a_tree)->rbt_nil) \ - { \ - rb_p_last(a_tree, (a_node)->a_field.rbn_left, a_field, \ - r_node); \ - } \ - else \ - { \ - a_type *t = (a_node); \ - (r_node) = (a_node)->a_field.rbn_par; \ - while ((r_node) != &(a_tree)->rbt_nil \ - && t == (r_node)->a_field.rbn_left) \ - { \ - t = (r_node); \ - (r_node) = (r_node)->a_field.rbn_par; \ - } \ - } \ - } while (0) - -/* a_key is always the first argument to a_comp. */ -#define rb_search(a_tree, a_key, a_comp, a_field, r_node) \ - do \ - { \ - int t; \ - (r_node) = (a_tree)->rbt_root; \ - while ((r_node) != &(a_tree)->rbt_nil \ - && (t = (a_comp)((a_key), (r_node))) != 0) \ - { \ - if (t == -1) \ - { \ - (r_node) = (r_node)->a_field.rbn_left; \ - } \ - else \ - { \ - (r_node) = (r_node)->a_field.rbn_right; \ - } \ - } \ - } while (0) - -/* Find a match if it exists. Otherwise, find the next greater node, if one - * exists. */ -#define rb_nsearch(a_tree, a_key, a_comp, a_type, a_field, r_node) \ - do \ - { \ - int t; \ - (r_node) = (a_tree)->rbt_root; \ - while ((r_node) != &(a_tree)->rbt_nil \ - && (t = (a_comp)((a_key), (r_node))) != 0) \ - { \ - if (t == -1) \ - { \ - if ((r_node)->a_field.rbn_left == &(a_tree)->rbt_nil) \ - { \ - break; \ - } \ - (r_node) = (r_node)->a_field.rbn_left; \ - } \ - else \ - { \ - if ((r_node)->a_field.rbn_right == &(a_tree)->rbt_nil) \ - { \ - a_type *n = (r_node); \ - (r_node) = (r_node)->a_field.rbn_par; \ - while ((r_node) != &(a_tree)->rbt_nil \ - && n == (r_node)->a_field.rbn_right) \ - { \ - n = (r_node); \ - (r_node) = (r_node)->a_field.rbn_par; \ - } \ - break; \ - } \ - (r_node) = (r_node)->a_field.rbn_right; \ - } \ - } \ - } while (0) - -#define rb_p_left_rotate(a_tree, a_node, a_type, a_field) \ - do \ - { \ - a_type *t = (a_node)->a_field.rbn_right; \ - (a_node)->a_field.rbn_right = t->a_field.rbn_left; \ - if (t->a_field.rbn_left != &(a_tree)->rbt_nil) \ - { \ - t->a_field.rbn_left->a_field.rbn_par = (a_node); \ - } \ - t->a_field.rbn_par = (a_node)->a_field.rbn_par; \ - if ((a_node)->a_field.rbn_par == &(a_tree)->rbt_nil) \ - { \ - (a_tree)->rbt_root = t; \ - } \ - else if ((a_node) \ - == (a_node)->a_field.rbn_par->a_field.rbn_left) \ - { \ - (a_node)->a_field.rbn_par->a_field.rbn_left = t; \ - } \ - else \ - { \ - (a_node)->a_field.rbn_par->a_field.rbn_right = t; \ - } \ - t->a_field.rbn_left = (a_node); \ - (a_node)->a_field.rbn_par = t; \ - } while (0) - -#define rb_p_right_rotate(a_tree, a_node, a_type, a_field) \ - do \ - { \ - a_type *t = (a_node)->a_field.rbn_left; \ - (a_node)->a_field.rbn_left = t->a_field.rbn_right; \ - if (t->a_field.rbn_right != &(a_tree)->rbt_nil) \ - { \ - t->a_field.rbn_right->a_field.rbn_par = (a_node); \ - } \ - t->a_field.rbn_par = (a_node)->a_field.rbn_par; \ - if ((a_node)->a_field.rbn_par == &(a_tree)->rbt_nil) \ - { \ - (a_tree)->rbt_root = t; \ - } \ - else if ((a_node) \ - == (a_node)->a_field.rbn_par->a_field.rbn_right) \ - { \ - (a_node)->a_field.rbn_par->a_field.rbn_right = t; \ - } \ - else \ - { \ - (a_node)->a_field.rbn_par->a_field.rbn_left = t; \ - } \ - t->a_field.rbn_right = (a_node); \ - (a_node)->a_field.rbn_par = t; \ - } while (0) - -/* a_node is always the first argument to a_comp. */ -#define rb_insert(a_tree, a_node, a_comp, a_type, a_field) \ - do \ - { \ - /* Insert. */ \ - a_type *x = &(a_tree)->rbt_nil; \ - a_type *y = (a_tree)->rbt_root; \ - int c = 0; \ - while (y != &(a_tree)->rbt_nil) \ - { \ - x = y; \ - c = (a_comp)((a_node), y); \ - if (c == -1) \ - { \ - y = y->a_field.rbn_left; \ - } \ - else \ - { \ - y = y->a_field.rbn_right; \ - } \ - } \ - (a_node)->a_field.rbn_par = x; \ - if (x == &(a_tree)->rbt_nil) \ - { \ - (a_tree)->rbt_root = (a_node); \ - } \ - else if (c == -1) \ - { \ - x->a_field.rbn_left = (a_node); \ - } \ - else \ - { \ - x->a_field.rbn_right = (a_node); \ - } \ - /* Fix up. */ \ - x = (a_node); \ - x->a_field.rbn_red = true; \ - while (x != (a_tree)->rbt_root \ - && x->a_field.rbn_par->a_field.rbn_red) \ - { \ - y = x->a_field.rbn_par; \ - if (y == y->a_field.rbn_par->a_field.rbn_left) \ - { \ - y = y->a_field.rbn_par->a_field.rbn_right; \ - if (y->a_field.rbn_red) \ - { \ - x->a_field.rbn_par->a_field.rbn_red = false; \ - y->a_field.rbn_red = false; \ - x->a_field.rbn_par->a_field.rbn_par \ - ->a_field.rbn_red = true; \ - x = x->a_field.rbn_par->a_field.rbn_par; \ - } \ - else \ - { \ - if (x == x->a_field.rbn_par->a_field.rbn_right) \ - { \ - x = x->a_field.rbn_par; \ - rb_p_left_rotate(a_tree, x, a_type, a_field); \ - } \ - x->a_field.rbn_par->a_field.rbn_red = false; \ - x->a_field.rbn_par->a_field.rbn_par \ - ->a_field.rbn_red = true; \ - x = x->a_field.rbn_par->a_field.rbn_par; \ - rb_p_right_rotate(a_tree, x, a_type, a_field); \ - } \ - } \ - else \ - { \ - y = y->a_field.rbn_par->a_field.rbn_left; \ - if (y->a_field.rbn_red) \ - { \ - x->a_field.rbn_par->a_field.rbn_red = false; \ - y->a_field.rbn_red = false; \ - x->a_field.rbn_par->a_field.rbn_par \ - ->a_field.rbn_red = true; \ - x = x->a_field.rbn_par->a_field.rbn_par; \ - } \ - else \ - { \ - if (x == x->a_field.rbn_par->a_field.rbn_left) \ - { \ - x = x->a_field.rbn_par; \ - rb_p_right_rotate(a_tree, x, a_type, a_field); \ - } \ - x->a_field.rbn_par->a_field.rbn_red = false; \ - x->a_field.rbn_par->a_field.rbn_par \ - ->a_field.rbn_red = true; \ - x = x->a_field.rbn_par->a_field.rbn_par; \ - rb_p_left_rotate(a_tree, x, a_type, a_field); \ - } \ - } \ - } \ - (a_tree)->rbt_root->a_field.rbn_red = false; \ - } while (0) - -#define rb_remove(a_tree, a_node, a_type, a_field) \ - do \ - { \ - bool fixup; \ - a_type *x, *y; \ - if ((a_node)->a_field.rbn_left == &(a_tree)->rbt_nil \ - || (a_node)->a_field.rbn_right == &(a_tree)->rbt_nil) \ - { \ - y = (a_node); \ - } \ - else \ - { \ - rb_next(a_tree, a_node, a_type, a_field, y); \ - } \ - if (y->a_field.rbn_left != &(a_tree)->rbt_nil) \ - { \ - x = y->a_field.rbn_left; \ - } \ - else \ - { \ - x = y->a_field.rbn_right; \ - } \ - x->a_field.rbn_par = y->a_field.rbn_par; \ - if (y->a_field.rbn_par == &(a_tree)->rbt_nil) \ - { \ - (a_tree)->rbt_root = x; \ - } \ - else if (y == y->a_field.rbn_par->a_field.rbn_left) \ - { \ - y->a_field.rbn_par->a_field.rbn_left = x; \ - } \ - else \ - { \ - y->a_field.rbn_par->a_field.rbn_right = x; \ - } \ - if (y->a_field.rbn_red == false) \ - { \ - fixup = true; \ - } \ - else \ - { \ - fixup = false; \ - } \ - if (y != (a_node)) \ - { \ - /* Splice y into a_node's location. */ \ - y->a_field.rbn_par = (a_node)->a_field.rbn_par; \ - y->a_field.rbn_left = (a_node)->a_field.rbn_left; \ - y->a_field.rbn_right = (a_node)->a_field.rbn_right; \ - y->a_field.rbn_red = (a_node)->a_field.rbn_red; \ - if (y->a_field.rbn_par != &(a_tree)->rbt_nil) \ - { \ - if (y->a_field.rbn_par->a_field.rbn_left == (a_node)) \ - { \ - y->a_field.rbn_par->a_field.rbn_left = y; \ - } \ - else \ - { \ - y->a_field.rbn_par->a_field.rbn_right = y; \ - } \ - } \ - else \ - { \ - (a_tree)->rbt_root = y; \ - } \ - y->a_field.rbn_right->a_field.rbn_par = y; \ - y->a_field.rbn_left->a_field.rbn_par = y; \ - } \ - rb_node_new(a_tree, a_node, a_field); \ - if (fixup) \ - { \ - /* Fix up. */ \ - a_type *v, *w; \ - while (x != (a_tree)->rbt_root \ - && x->a_field.rbn_red == false) \ - { \ - if (x == x->a_field.rbn_par->a_field.rbn_left) \ - { \ - w = x->a_field.rbn_par->a_field.rbn_right; \ - if (w->a_field.rbn_red) \ - { \ - w->a_field.rbn_red = false; \ - v = x->a_field.rbn_par; \ - v->a_field.rbn_red = true; \ - rb_p_left_rotate(a_tree, v, a_type, a_field); \ - w = x->a_field.rbn_par->a_field.rbn_right; \ - } \ - if (w->a_field.rbn_left->a_field.rbn_red \ - == false \ - && w->a_field.rbn_right->a_field.rbn_red \ - == false) \ - { \ - w->a_field.rbn_red = true; \ - x = x->a_field.rbn_par; \ - } \ - else \ - { \ - if (w->a_field.rbn_right->a_field.rbn_red \ - == false) \ - { \ - w->a_field.rbn_left->a_field.rbn_red \ - = false; \ - w->a_field.rbn_red = true; \ - rb_p_right_rotate(a_tree, w, a_type, \ - a_field); \ - w = x->a_field.rbn_par->a_field.rbn_right; \ - } \ - w->a_field.rbn_red \ - = x->a_field.rbn_par->a_field.rbn_red; \ - x->a_field.rbn_par->a_field.rbn_red = false; \ - w->a_field.rbn_right->a_field.rbn_red = false; \ - v = x->a_field.rbn_par; \ - rb_p_left_rotate(a_tree, v, a_type, a_field); \ - break; \ - } \ - } \ - else \ - { \ - w = x->a_field.rbn_par->a_field.rbn_left; \ - if (w->a_field.rbn_red) \ - { \ - w->a_field.rbn_red = false; \ - v = x->a_field.rbn_par; \ - v->a_field.rbn_red = true; \ - rb_p_right_rotate(a_tree, v, a_type, a_field); \ - w = x->a_field.rbn_par->a_field.rbn_left; \ - } \ - if (w->a_field.rbn_right->a_field.rbn_red \ - == false \ - && w->a_field.rbn_left->a_field.rbn_red \ - == false) \ - { \ - w->a_field.rbn_red = true; \ - x = x->a_field.rbn_par; \ - } \ - else \ - { \ - if (w->a_field.rbn_left->a_field.rbn_red \ - == false) \ - { \ - w->a_field.rbn_right->a_field.rbn_red \ - = false; \ - w->a_field.rbn_red = true; \ - rb_p_left_rotate(a_tree, w, a_type, \ - a_field); \ - w = x->a_field.rbn_par->a_field.rbn_left; \ - } \ - w->a_field.rbn_red \ - = x->a_field.rbn_par->a_field.rbn_red; \ - x->a_field.rbn_par->a_field.rbn_red = false; \ - w->a_field.rbn_left->a_field.rbn_red = false; \ - v = x->a_field.rbn_par; \ - rb_p_right_rotate(a_tree, v, a_type, a_field); \ - break; \ - } \ - } \ - } \ - x->a_field.rbn_red = false; \ - } \ - } while (0) diff --git a/tommyds/benchmark/lib/stx/README b/tommyds/benchmark/lib/stx/README deleted file mode 100644 index cac780b..0000000 --- a/tommyds/benchmark/lib/stx/README +++ /dev/null @@ -1,202 +0,0 @@ - *** STX B+ Tree C++ Template Classes v0.9 *** - -Author: Timo Bingmann (Mail: tb a-with-circle panthema dot net) -Date: 2013-05-05 - ---- Summary --- - -The STX B+ Tree package is a set of C++ template classes implementing a B+ tree -key/data container in main memory. The classes are designed as drop-in -replacements of the STL containers set, map, multiset and multimap and follow -their interfaces very closely. By packing multiple value pairs into each node -of the tree the B+ tree reduces heap fragmentation and utilizes cache-line -effects better than the standard red-black binary tree. The tree algorithms are -based on the implementation in Cormen, Leiserson and Rivest's Introduction into -Algorithms, Jan Jannink's paper and other algorithm resources. The classes -contain extensive assertion and verification mechanisms to ensure the -implementation's correctness by testing the tree invariants. To illustrate the -B+ tree's structure a wxWidgets demo program is included in the source package. - ---- Website / API Docs / Bugs / License --- - -The current source package can be downloaded from -http://panthema.net/2007/stx-btree/ - -The include files are extensively documented using doxygen. The compiled -doxygen html documentation is included in the source package. It can also be -viewed online at -http://panthema.net/2007/stx-btree/stx-btree-0.9/doxygen-html/ - -The wxWidgets B+ tree demo program is located in the directory -wxbtreedemo. Compiled binary versions can be found on the package web page. - -If bugs should become known they will be posted on the above web page together -with patches or corrected versions. - -The B+ tree template source code is released under the Boost Software License, -Version 1.0, which can be found at the header of each include file. - -All auxiliary programs like the wxWidgets demo, test suite and speed tests are -licensed under the GNU General Public License v3 (GPLv3), which can be found in -the file COPYING.GPLv3. - ---- Original Idea --- - -The idea originally arose while coding a read-only database, which used a huge -map of millions of non-sequential integer keys to 8-byte file offsets. When -using the standard STL red-black tree implementation this would yield millions -of 20-byte heap allocations and very slow search times due to the tree's -height. So the original intension was to reduce memory fragmentation and -improve search times. The B+ tree solves this by packing multiple data pairs -into one node with a large number of descendant nodes. - -In computer science lectures it is often stated that using consecutive bytes in -memory would be more cache-efficient, because the CPU's cache levels always -fetch larger blocks from main memory. So it would be best to store the keys of -a node in one continuous array. This way the inner scanning loop would be -accelerated by benefiting from cache effects and pipelining speed-ups. Thus the -cost of scanning for a matching key would be lower than in a red-black tree, -even though the number of key comparisons are theoretically larger. This second -aspect aroused my academic interest and resulted in the speed test experiments. - -A third inspiration was that no working C++ template implementation of a B+ -tree could be found on the Internet. Now this one can be found. - ---- Implementation Overview --- - -This implementation contains five main classes within the stx namespace -(blandly named Some Template eXtensions). The base class btree implements the -B+ tree algorithms using inner and leaf nodes in main memory. Almost all -STL-required function calls are implemented (see below for the exceptions). The -asymptotic time requirements of the STL standard are theoretically not always -fulfilled. However in practice this B+ tree performs better than the STL's -red-black tree at the cost of using more memory. See the speed test results for -details. - -The base class is then specialized into btree_set, btree_multiset, btree_map -and btree_multimap using default template parameters and facade -functions. These classes are designed to be drop-in replacements for the -corresponding STL containers. - -The insertion function splits the nodes on recursion unroll. Erase is largely -based on Jannink's ideas. See http://dbpubs.stanford.edu:8090/pub/1995-19 for -his paper on "Implementing Deletion in B+-trees". - -The two set classes (btree_set and btree_multiset) are derived from the base -implementation class btree by specifying an empty struct as data_type. All -functions are adapted to provide the base class with empty placeholder -objects. Note that it is somewhat inefficient to implement a set or multiset -using a B+ tree: a plain B tree (without +) would hold no extra copies of the -keys. The main focus was on implementing the maps. - ---- Problem with Separated Key/Data Arrays --- - -The most noteworthy difference to the default red-black tree implementation of -std::map is that the B+ tree does not hold key/data pairs together in -memory. Instead each B+ tree node has two separate arrays containing keys and -data values. This design was chosen to utilize cache-line effects while -scanning the key array. - -However it also directly generates many problems in implementing the iterators' -operators. These return a (writable) reference or pointer to a value_type, -which is a std::pair composition. These data/key pairs however are not stored -together and thus a temporary copy must be constructed. This copy should not be -written to, because it is not stored back into the B+ tree. This effectively -prohibits use of many STL algorithms which writing to the B+ tree's -iterators. I would be grateful for hints on how to resolve this problem without -folding the key and data arrays. - ---- Test Suite --- - -The B+ tree distribution contains an extensive test suite. According to gcov -90.9% of the btree.h implementation is covered. - ---- STL Incompatibilities --- - -- Key and Data Type Requirements - - -The tree algorithms currently do not use copy-construction. All key/data items -are allocated in the nodes using the default-constructor and are subsequently -only assigned new data (using operator=). - -- Iterators' Operators - - -The most important incompatibility are the non-writable operator* and -operator-> of the iterator. See above for a discussion of the problem on -separated key/data arrays. Instead of *iter and iter-> use the new function -iter.data() which returns a writable reference to the data value in the tree. - -- Erase Functions - - -The B+ tree supports three erase functions: - -size_type erase(const key_type &key); // erase all data pairs matching key -bool erase_one(const key_type &key); // erase one data pair matching key -void erase(iterator iter); // erase pair referenced by iter - -The following STL-required function is not supported: - -void erase(iterator first, iterator last); - ---- Extensions --- - -Beyond the usual STL interface the B+ tree classes support some extra goodies. - -// Output the tree in a pseudo-hierarchical text dump to std::cout. This -// function requires that BTREE_DEBUG is defined prior to including the btree -// headers. Furthermore the key and data types must be std::ostream printable. -void print() const; - -// Run extensive checks of the tree invariants. If a corruption in found the -// program will abort via assert(). See below on enabling auto-verification. -void verify() const; - -// Serialize and restore the B+ tree nodes and data into/from a binary image. -// This requires that the key and data types are integral and contain no -// outside pointers or references. -void dump(std::ostream &os) const; -bool restore(std::istream &is); - ---- B+ Tree Traits --- - -All tree template classes take a template parameter structure which holds -important options of the implementation. The following structure shows which -static variables specify the options and the corresponding defaults: - -struct btree_default_map_traits -{ - // If true, the tree will self verify it's invariants after each insert() - // or erase(). The header must have been compiled with BTREE_DEBUG - // defined. - static const bool selfverify = false; - - // If true, the tree will print out debug information and a tree dump - // during insert() or erase() operation. The header must have been - // compiled with BTREE_DEBUG defined and key_type must be std::ostream - // printable. - static const bool debug = false; - - // Number of slots in each leaf of the tree. Estimated so that each node - // has a size of about 128 bytes. - static const int leafslots = - MAX( 8, 128 / (sizeof(_Key) + sizeof(_Data)) ); - - // Number of slots in each inner node of the tree. Estimated so that each - // node has a size of about 128 bytes. - static const int innerslots = - MAX( 8, 128 / (sizeof(_Key) + sizeof(void*)) ); - - // As of stx-btree-0.9, the code does linear search in find_lower() and - // find_upper() instead of binary_search, unless the node size is larger - // than this threshold. See notes at - // http://panthema.net/2013/0504-STX-B+Tree-Binary-vs-Linear-Search - static const size_t binsearch_threshold = 256; -}; - ---- Speed Tests --- - -The implementation was tested using the speed test sources contained in the -package. For a long discussion on results please see the web page - -http://panthema.net/2007/stx-btree/ - diff --git a/tommyds/benchmark/lib/stx/btree b/tommyds/benchmark/lib/stx/btree deleted file mode 100644 index 17a9ffe..0000000 --- a/tommyds/benchmark/lib/stx/btree +++ /dev/null @@ -1,39 +0,0 @@ -/** \file btree - * Forwarder header to btree.h - */ - -/* - * STX B+ Tree Template Classes v0.9 - * Copyright (C) 2008-2013 Timo Bingmann - * - * Boost Software License - Version 1.0 - August 17th, 2003 - * - * Permission is hereby granted, free of charge, to any person or organization - * obtaining a copy of the software and accompanying documentation covered by - * this license (the "Software") to use, reproduce, display, distribute, - * execute, and transmit the Software, and to prepare derivative works of the - * Software, and to permit third-parties to whom the Software is furnished to - * do so, all subject to the following: - * - * The copyright notices in the Software and this entire statement, including - * the above license grant, this restriction and the following disclaimer, must - * be included in all copies of the Software, in whole or in part, and all - * derivative works of the Software, unless such copies or derivative works are - * solely in the form of machine-executable object code generated by a source - * language processor. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT - * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE - * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#ifndef _STX_BTREE_ -#define _STX_BTREE_ - -#include - -#endif // _STX_BTREE_ diff --git a/tommyds/benchmark/lib/stx/btree.dox b/tommyds/benchmark/lib/stx/btree.dox deleted file mode 100644 index 6717b3a..0000000 --- a/tommyds/benchmark/lib/stx/btree.dox +++ /dev/null @@ -1,255 +0,0 @@ -/** \file btree.dox - * Contains the doxygen comments. This header is not needed to compile the B+ - * tree. - */ - -/* - * STX B+ Tree Template Classes v0.9 - * Copyright (C) 2008-2013 Timo Bingmann - * - * Boost Software License - Version 1.0 - August 17th, 2003 - * - * Permission is hereby granted, free of charge, to any person or organization - * obtaining a copy of the software and accompanying documentation covered by - * this license (the "Software") to use, reproduce, display, distribute, - * execute, and transmit the Software, and to prepare derivative works of the - * Software, and to permit third-parties to whom the Software is furnished to - * do so, all subject to the following: - * - * The copyright notices in the Software and this entire statement, including - * the above license grant, this restriction and the following disclaimer, must - * be included in all copies of the Software, in whole or in part, and all - * derivative works of the Software, unless such copies or derivative works are - * solely in the form of machine-executable object code generated by a source - * language processor. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT - * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE - * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -/** \mainpage STX B+ Tree Template Classes README - -\author Timo Bingmann (Mail: tb a-with-circle panthema dot net) -\date 2013-05-05, 2011-05-17 and 2008-09-07 - -\section sec1 Summary - -The STX B+ Tree package is a set of C++ template classes implementing a B+ tree -key/data container in main memory. The classes are designed as drop-in -replacements of the STL containers set, map, -multiset and multimap and follow their interfaces very -closely. By packing multiple value pairs into each node of the tree the B+ tree -reduces heap fragmentation and utilizes cache-line effects better than the -standard red-black binary tree. The tree algorithms are based on the -implementation in Cormen, Leiserson and Rivest's Introduction into Algorithms, -Jan Jannink's paper and other algorithm resources. The classes contain -extensive assertion and verification mechanisms to ensure the implementation's -correctness by testing the tree invariants. To illustrate the B+ tree's -structure a wxWidgets demo program is included in the source package. - -\section sec2 Website / API Docs / Bugs / License - -The current source package can be downloaded from -http://panthema.net/2007/stx-btree/ - -The include files are extensively documented using doxygen. The compiled -doxygen html documentation is included in the source package. It can also be -viewed online at -http://panthema.net/2007/stx-btree/stx-btree-0.9/doxygen-html/ (if you are not -reading it right now). - -The wxWidgets B+ tree demo program is located in the directory -wxbtreedemo. Compiled binary versions can be found on the package web page -mentioned above. - -If bugs should become known they will be posted on the above web page together -with patches or corrected versions. - -The B+ tree template source code is released under the Boost Software License, -Version 1.0, which can be found at the header of each include file. - -All auxiliary programs like the wxWidgets demo, test suite and speed tests are -licensed under the GNU General Public License v3 (GPLv3), which can be found in -the file COPYING.GPLv3. - -\section sec3 Original Idea - -The idea originally arose while coding a read-only database, which used a huge -map of millions of non-sequential integer keys to 8-byte file offsets. When -using the standard STL red-black tree implementation this would yield millions -of 20-byte heap allocations and very slow search times due to the tree's -height. So the original intention was to reduce memory fragmentation and -improve search times. The B+ tree solves this by packing multiple data pairs -into one node with a large number of descendant nodes. - -In computer science lectures it is often stated that using consecutive bytes in -memory would be more cache-efficient, because the CPU's cache levels always -fetch larger blocks from main memory. So it would be best to store the keys of -a node in one continuous array. This way the inner scanning loop would be -accelerated by benefiting from cache effects and pipelining speed-ups. Thus the -cost of scanning for a matching key would be lower than in a red-black tree, -even though the number of key comparisons are theoretically larger. This second -aspect aroused my academic interest and resulted in the -\ref sec10 "speed test experiments". - -A third inspiration was that no working C++ template implementation of a B+ -tree could be found on the Internet. Now this one can be found. - -\section sec4 Implementation Overview - -This implementation contains five main classes within the \ref stx namespace -(blandly named Some Template eXtensions). The base class \ref stx::btree -"btree" implements the B+ tree algorithms using inner and leaf nodes in main -memory. Almost all STL-required function calls are implemented (see below for -the exceptions). The asymptotic time requirements of the STL standard are -theoretically not always fulfilled. However in practice this B+ tree performs -better than the STL's red-black tree at the cost of using more memory. See the -\ref sec10 "speed test results" for details. - -The base class is then specialized into \ref stx::btree_set "btree_set", \ref -stx::btree_multiset "btree_multiset", \ref stx::btree_map "btree_map" and \ref -stx::btree_multimap "btree_multimap" using default template parameters and -facade functions. These classes are designed to be drop-in replacements for the -corresponding STL containers. - -The insertion function splits the nodes on recursion unroll. Erase is largely -based on Jannink's ideas. See http://dbpubs.stanford.edu:8090/pub/1995-19 for -his paper on "Implementing Deletion in B+-trees". - -The two set classes (\ref stx::btree_set "btree_set" and \ref -stx::btree_multiset "btree_multiset") are derived from the base implementation -\ref stx::btree "class btree" by specifying an empty struct as data_type. All -functions are adapted to provide the base class with empty placeholder -objects. Note that it is somewhat inefficient to implement a set or multiset -using a B+ tree: a plain B tree (without +) would hold no extra copies of the -keys. The main focus was on implementing the maps. - -\section sec5 Problem with Separated Key/Data Arrays - -The most noteworthy difference to the default red-black tree implementation of -std::map is that the B+ tree does not hold key/data pairs together in memory. -Instead each B+ tree node has two separate arrays containing keys and data -values. This design was chosen to utilize cache-line effects while scanning the -key array. - -However it also directly generates many problems in implementing the iterators' -operators. These return a (writable) reference or pointer to a value_type, -which is a std::pair composition. These data/key pairs however are not stored -together and thus a temporary copy must be constructed. This copy should not be -written to, because it is not stored back into the B+ tree. This effectively -prohibits use of many STL algorithms which writing to the B+ tree's -iterators. I would be grateful for hints on how to resolve this problem without -folding the key and data arrays. - -\section sec6 Test Suite - -The B+ tree distribution contains an extensive test suite. According to gcov -91.9% of the btree.h implementation is covered. - -\section sec7 STL Incompatibilities - -\subsection sec7-1 Key and Data Type Requirements - -The tree algorithms currently do not use copy-construction. All key/data items -are allocated in the nodes using the default-constructor and are subsequently -only assigned new data (using operator=). - -\subsection sec7-2 Iterators' Operators - -The most important incompatibility are the non-writable operator* and -operator-> of the \ref stx::btree::iterator "iterator". See above for -a discussion of the problem on separated key/data arrays. Instead of -*iter and iter-> use the new function iter.data() -which returns a writable reference to the data value in the tree. - -\subsection sec7-3 Erase Functions - -The B+ tree supports three erase functions: - -\code -size_type erase(const key_type &key); // erase all data pairs matching key -bool erase_one(const key_type &key); // erase one data pair matching key -void erase(iterator iter); // erase pair referenced by iter -\endcode - -The following STL-required function is currently not supported: - -\code -void erase(iterator first, iterator last); -\endcode - -\section sec8 Extensions - -Beyond the usual STL interface the B+ tree classes support some extra goodies. - -\code -// Bulk load a sorted range. Loads items into leaves and constructs a -// B-tree above them. The tree must be empty when calling this function. -template -void bulk_load(Iterator ibegin, Iterator iend); - -// Output the tree in a pseudo-hierarchical text dump to std::cout. This -// function requires that BTREE_DEBUG is defined prior to including the btree -// headers. Furthermore the key and data types must be std::ostream printable. -void print() const; - -// Run extensive checks of the tree invariants. If a corruption in found the -// program will abort via assert(). See below on enabling auto-verification. -void verify() const; - -// Serialize and restore the B+ tree nodes and data into/from a binary image. -// This requires that the key and data types are integral and contain no -// outside pointers or references. -void dump(std::ostream &os) const; -bool restore(std::istream &is); -\endcode - -\section sec9 B+ Tree Traits - -All tree template classes take a template parameter structure which holds -important options of the implementation. The following structure shows which -static variables specify the options and the corresponding defaults: - -\code -struct btree_default_map_traits -{ - // If true, the tree will self verify it's invariants after each insert() - // or erase(). The header must have been compiled with BTREE_DEBUG - // defined. - static const bool selfverify = false; - - // If true, the tree will print out debug information and a tree dump - // during insert() or erase() operation. The header must have been - // compiled with BTREE_DEBUG defined and key_type must be std::ostream - // printable. - static const bool debug = false; - - // Number of slots in each leaf of the tree. Estimated so that each node - // has a size of about 256 bytes. - static const int leafslots = - MAX( 8, 256 / (sizeof(_Key) + sizeof(_Data)) ); - - // Number of slots in each inner node of the tree. Estimated so that each - // node has a size of about 256 bytes. - static const int innerslots = - MAX( 8, 256 / (sizeof(_Key) + sizeof(void*)) ); - - // As of stx-btree-0.9, the code does linear search in find_lower() and - // find_upper() instead of binary_search, unless the node size is larger - // than this threshold. See notes at - // http://panthema.net/2013/0504-STX-B+Tree-Binary-vs-Linear-Search - static const size_t binsearch_threshold = 256; -}; -\endcode - -\section sec10 Speed Tests - -See the web page http://panthema.net/2007/stx-btree/speedtest/ for speed test -results and a discussion thereof. - -*/ diff --git a/tommyds/benchmark/lib/stx/btree.h b/tommyds/benchmark/lib/stx/btree.h deleted file mode 100644 index 86cf2ea..0000000 --- a/tommyds/benchmark/lib/stx/btree.h +++ /dev/null @@ -1,3983 +0,0 @@ -/** - * \file include/stx/btree.h - * Contains the main B+ tree implementation template class btree. - */ - -/* - * STX B+ Tree Template Classes v0.9 - * Copyright (C) 2008-2013 Timo Bingmann - * - * Boost Software License - Version 1.0 - August 17th, 2003 - * - * Permission is hereby granted, free of charge, to any person or organization - * obtaining a copy of the software and accompanying documentation covered by - * this license (the "Software") to use, reproduce, display, distribute, - * execute, and transmit the Software, and to prepare derivative works of the - * Software, and to permit third-parties to whom the Software is furnished to - * do so, all subject to the following: - * - * The copyright notices in the Software and this entire statement, including - * the above license grant, this restriction and the following disclaimer, must - * be included in all copies of the Software, in whole or in part, and all - * derivative works of the Software, unless such copies or derivative works are - * solely in the form of machine-executable object code generated by a source - * language processor. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT - * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE - * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#ifndef _STX_BTREE_H_ -#define _STX_BTREE_H_ - -// *** Required Headers from the STL - -#include -#include -#include -#include -#include -#include -#include - -// *** Debugging Macros - -#ifdef BTREE_DEBUG - -#include - -/// Print out debug information to std::cout if BTREE_DEBUG is defined. -#define BTREE_PRINT(x) do { if (debug) (std::cout << x << std::endl); } while(0) - -/// Assertion only if BTREE_DEBUG is defined. This is not used in verify(). -#define BTREE_ASSERT(x) do { assert(x); } while(0) - -#else - -/// Print out debug information to std::cout if BTREE_DEBUG is defined. -#define BTREE_PRINT(x) do { } while(0) - -/// Assertion only if BTREE_DEBUG is defined. This is not used in verify(). -#define BTREE_ASSERT(x) do { } while(0) - -#endif - -/// The maximum of a and b. Used in some compile-time formulas. -#define BTREE_MAX(a,b) ((a) < (b) ? (b) : (a)) - -#ifndef BTREE_FRIENDS -/// The macro BTREE_FRIENDS can be used by outside class to access the B+ -/// tree internals. This was added for wxBTreeDemo to be able to draw the -/// tree. -#define BTREE_FRIENDS friend class btree_friend; -#endif - -/// STX - Some Template Extensions namespace -namespace stx { - -/** Generates default traits for a B+ tree used as a set. It estimates leaf and - * inner node sizes by assuming a cache line size of 256 bytes. */ -template -struct btree_default_set_traits -{ - /// If true, the tree will self verify it's invariants after each insert() - /// or erase(). The header must have been compiled with BTREE_DEBUG defined. - static const bool selfverify = false; - - /// If true, the tree will print out debug information and a tree dump - /// during insert() or erase() operation. The header must have been - /// compiled with BTREE_DEBUG defined and key_type must be std::ostream - /// printable. - static const bool debug = false; - - /// Number of slots in each leaf of the tree. Estimated so that each node - /// has a size of about 256 bytes. - static const int leafslots = BTREE_MAX( 8, 256 / (sizeof(_Key)) ); - - /// Number of slots in each inner node of the tree. Estimated so that each node - /// has a size of about 256 bytes. - static const int innerslots = BTREE_MAX( 8, 256 / (sizeof(_Key) + sizeof(void*)) ); - - /// As of stx-btree-0.9, the code does linear search in find_lower() and - /// find_upper() instead of binary_search, unless the node size is larger - /// than this threshold. See notes at - /// http://panthema.net/2013/0504-STX-B+Tree-Binary-vs-Linear-Search - static const size_t binsearch_threshold = 256; -}; - -/** Generates default traits for a B+ tree used as a map. It estimates leaf and - * inner node sizes by assuming a cache line size of 256 bytes. */ -template -struct btree_default_map_traits -{ - /// If true, the tree will self verify it's invariants after each insert() - /// or erase(). The header must have been compiled with BTREE_DEBUG defined. - static const bool selfverify = false; - - /// If true, the tree will print out debug information and a tree dump - /// during insert() or erase() operation. The header must have been - /// compiled with BTREE_DEBUG defined and key_type must be std::ostream - /// printable. - static const bool debug = false; - - /// Number of slots in each leaf of the tree. Estimated so that each node - /// has a size of about 256 bytes. - static const int leafslots = BTREE_MAX( 8, 256 / (sizeof(_Key) + sizeof(_Data)) ); - - /// Number of slots in each inner node of the tree. Estimated so that each node - /// has a size of about 256 bytes. - static const int innerslots = BTREE_MAX( 8, 256 / (sizeof(_Key) + sizeof(void*)) ); - - /// As of stx-btree-0.9, the code does linear search in find_lower() and - /// find_upper() instead of binary_search, unless the node size is larger - /// than this threshold. See notes at - /// http://panthema.net/2013/0504-STX-B+Tree-Binary-vs-Linear-Search - static const size_t binsearch_threshold = 256; -}; - -/** @brief Basic class implementing a base B+ tree data structure in memory. - * - * The base implementation of a memory B+ tree. It is based on the - * implementation in Cormen's Introduction into Algorithms, Jan Jannink's paper - * and other algorithm resources. Almost all STL-required function calls are - * implemented. The asymptotic time requirements of the STL are not always - * fulfilled in theory, however in practice this B+ tree performs better than a - * red-black tree by using more memory. The insertion function splits the nodes - * on the recursion unroll. Erase is largely based on Jannink's ideas. - * - * This class is specialized into btree_set, btree_multiset, btree_map and - * btree_multimap using default template parameters and facade functions. - */ -template , - typename _Compare = std::less<_Key>, - typename _Traits = btree_default_map_traits<_Key, _Data>, - bool _Duplicates = false, - typename _Alloc = std::allocator<_Value>, - bool _UsedAsSet = false > -class btree -{ -public: - // *** Template Parameter Types - - /// First template parameter: The key type of the B+ tree. This is stored - /// in inner nodes and leaves - typedef _Key key_type; - - /// Second template parameter: The data type associated with each - /// key. Stored in the B+ tree's leaves - typedef _Data data_type; - - /// Third template parameter: Composition pair of key and data types, this - /// is required by the STL standard. The B+ tree does not store key and - /// data together. If value_type == key_type then the B+ tree implements a - /// set. - typedef _Value value_type; - - /// Fourth template parameter: Key comparison function object - typedef _Compare key_compare; - - /// Fifth template parameter: Traits object used to define more parameters - /// of the B+ tree - typedef _Traits traits; - - /// Sixth template parameter: Allow duplicate keys in the B+ tree. Used to - /// implement multiset and multimap. - static const bool allow_duplicates = _Duplicates; - - /// Seventh template parameter: STL allocator for tree nodes - typedef _Alloc allocator_type; - - /// Eighth template parameter: boolean indicator whether the btree is used - /// as a set. In this case all operations on the data arrays are - /// omitted. This flag is kind of hacky, but required because - /// sizeof(empty_struct) = 1 due to the C standard. Without the flag, lots - /// of superfluous copying would occur. - static const bool used_as_set = _UsedAsSet; - - // The macro BTREE_FRIENDS can be used by outside class to access the B+ - // tree internals. This was added for wxBTreeDemo to be able to draw the - // tree. - BTREE_FRIENDS - -public: - // *** Constructed Types - - /// Typedef of our own type - typedef btree btree_self; - - /// Size type used to count keys - typedef size_t size_type; - - /// The pair of key_type and data_type, this may be different from value_type. - typedef std::pair pair_type; - -public: - // *** Static Constant Options and Values of the B+ Tree - - /// Base B+ tree parameter: The number of key/data slots in each leaf - static const unsigned short leafslotmax = traits::leafslots; - - /// Base B+ tree parameter: The number of key slots in each inner node, - /// this can differ from slots in each leaf. - static const unsigned short innerslotmax = traits::innerslots; - - /// Computed B+ tree parameter: The minimum number of key/data slots used - /// in a leaf. If fewer slots are used, the leaf will be merged or slots - /// shifted from it's siblings. - static const unsigned short minleafslots = (leafslotmax / 2); - - /// Computed B+ tree parameter: The minimum number of key slots used - /// in an inner node. If fewer slots are used, the inner node will be - /// merged or slots shifted from it's siblings. - static const unsigned short mininnerslots = (innerslotmax / 2); - - /// Debug parameter: Enables expensive and thorough checking of the B+ tree - /// invariants after each insert/erase operation. - static const bool selfverify = traits::selfverify; - - /// Debug parameter: Prints out lots of debug information about how the - /// algorithms change the tree. Requires the header file to be compiled - /// with BTREE_DEBUG and the key type must be std::ostream printable. - static const bool debug = traits::debug; - -public: - // *** Node Classes for In-Memory Nodes - - /// The header structure of each node in-memory. This structure is extended - /// by inner_node or leaf_node. - struct node - { - /// Level in the b-tree, if level == 0 -> leaf node - unsigned short level; - - /// Number of key slotuse use, so number of valid children or data - /// pointers - unsigned short slotuse; - - /// Delayed initialisation of constructed node - inline void initialize(const unsigned short l) - { - level = l; - slotuse = 0; - } - - /// True if this is a leaf node - inline bool isleafnode() const - { - return (level == 0); - } - }; - - /// Extended structure of a inner node in-memory. Contains only keys and no - /// data items. - struct inner_node : public node - { - /// Define an related allocator for the inner_node structs. - typedef typename _Alloc::template rebind::other alloc_type; - - /// Keys of children or data pointers - key_type slotkey[innerslotmax]; - - /// Pointers to children - node* childid[innerslotmax+1]; - - /// Set variables to initial values - inline void initialize(const unsigned short l) - { - node::initialize(l); - } - - /// True if the node's slots are full - inline bool isfull() const - { - return (node::slotuse == innerslotmax); - } - - /// True if few used entries, less than half full - inline bool isfew() const - { - return (node::slotuse <= mininnerslots); - } - - /// True if node has too few entries - inline bool isunderflow() const - { - return (node::slotuse < mininnerslots); - } - }; - - /// Extended structure of a leaf node in memory. Contains pairs of keys and - /// data items. Key and data slots are kept in separate arrays, because the - /// key array is traversed very often compared to accessing the data items. - struct leaf_node : public node - { - /// Define an related allocator for the leaf_node structs. - typedef typename _Alloc::template rebind::other alloc_type; - - /// Double linked list pointers to traverse the leaves - leaf_node *prevleaf; - - /// Double linked list pointers to traverse the leaves - leaf_node *nextleaf; - - /// Keys of children or data pointers - key_type slotkey[leafslotmax]; - - /// Array of data - data_type slotdata[used_as_set ? 1 : leafslotmax]; - - /// Set variables to initial values - inline void initialize() - { - node::initialize(0); - prevleaf = nextleaf = NULL; - } - - /// True if the node's slots are full - inline bool isfull() const - { - return (node::slotuse == leafslotmax); - } - - /// True if few used entries, less than half full - inline bool isfew() const - { - return (node::slotuse <= minleafslots); - } - - /// True if node has too few entries - inline bool isunderflow() const - { - return (node::slotuse < minleafslots); - } - - /// Set the (key,data) pair in slot. Overloaded function used by - /// bulk_load(). - inline void set_slot(unsigned short slot, const pair_type& value) - { - BTREE_ASSERT(used_as_set == false); - BTREE_ASSERT(slot < node::slotuse); - slotkey[slot] = value.first; - slotdata[slot] = value.second; - } - - /// Set the key pair in slot. Overloaded function used by - /// bulk_load(). - inline void set_slot(unsigned short slot, const key_type& key) - { - BTREE_ASSERT(used_as_set == true); - BTREE_ASSERT(slot < node::slotuse); - slotkey[slot] = key; - } - }; - -private: - // *** Template Magic to Convert a pair or key/data types to a value_type - - /// For sets the second pair_type is an empty struct, so the value_type - /// should only be the first. - template - struct btree_pair_to_value - { - /// Convert a fake pair type to just the first component - inline value_type operator()(pair_type& p) const { - return p.first; - } - /// Convert a fake pair type to just the first component - inline value_type operator()(const pair_type& p) const { - return p.first; - } - }; - - /// For maps value_type is the same as the pair_type - template - struct btree_pair_to_value - { - /// Identity "convert" a real pair type to just the first component - inline value_type operator()(pair_type& p) const { - return p; - } - /// Identity "convert" a real pair type to just the first component - inline value_type operator()(const pair_type& p) const { - return p; - } - }; - - /// Using template specialization select the correct converter used by the - /// iterators - typedef btree_pair_to_value pair_to_value_type; - -public: - // *** Iterators and Reverse Iterators - - class iterator; - class const_iterator; - class reverse_iterator; - class const_reverse_iterator; - - /// STL-like iterator object for B+ tree items. The iterator points to a - /// specific slot number in a leaf. - class iterator - { - public: - // *** Types - - /// The key type of the btree. Returned by key(). - typedef typename btree::key_type key_type; - - /// The data type of the btree. Returned by data(). - typedef typename btree::data_type data_type; - - /// The value type of the btree. Returned by operator*(). - typedef typename btree::value_type value_type; - - /// The pair type of the btree. - typedef typename btree::pair_type pair_type; - - /// Reference to the value_type. STL required. - typedef value_type& reference; - - /// Pointer to the value_type. STL required. - typedef value_type* pointer; - - /// STL-magic iterator category - typedef std::bidirectional_iterator_tag iterator_category; - - /// STL-magic - typedef ptrdiff_t difference_type; - - /// Our own type - typedef iterator self; - - private: - // *** Members - - /// The currently referenced leaf node of the tree - typename btree::leaf_node* currnode; - - /// Current key/data slot referenced - unsigned short currslot; - - /// Friendly to the const_iterator, so it may access the two data items - /// directly. - friend class const_iterator; - - /// Also friendly to the reverse_iterator, so it may access the two - /// data items directly. - friend class reverse_iterator; - - /// Also friendly to the const_reverse_iterator, so it may access the - /// two data items directly. - friend class const_reverse_iterator; - - /// Also friendly to the base btree class, because erase_iter() needs - /// to read the currnode and currslot values directly. - friend class btree; - - /// Evil! A temporary value_type to STL-correctly deliver operator* and - /// operator-> - mutable value_type temp_value; - - // The macro BTREE_FRIENDS can be used by outside class to access the B+ - // tree internals. This was added for wxBTreeDemo to be able to draw the - // tree. - BTREE_FRIENDS - - public: - // *** Methods - - /// Default-Constructor of a mutable iterator - inline iterator() - : currnode(NULL), currslot(0) - { } - - /// Initializing-Constructor of a mutable iterator - inline iterator(typename btree::leaf_node *l, unsigned short s) - : currnode(l), currslot(s) - { } - - /// Copy-constructor from a reverse iterator - inline iterator(const reverse_iterator &it) - : currnode(it.currnode), currslot(it.currslot) - { } - - /// Dereference the iterator, this is not a value_type& because key and - /// value are not stored together - inline reference operator*() const - { - temp_value = pair_to_value_type()( pair_type(key(),data()) ); - return temp_value; - } - - /// Dereference the iterator. Do not use this if possible, use key() - /// and data() instead. The B+ tree does not stored key and data - /// together. - inline pointer operator->() const - { - temp_value = pair_to_value_type()( pair_type(key(),data()) ); - return &temp_value; - } - - /// Key of the current slot - inline const key_type& key() const - { - return currnode->slotkey[currslot]; - } - - /// Writable reference to the current data object - inline data_type& data() const - { - return currnode->slotdata[used_as_set ? 0 : currslot]; - } - - /// Prefix++ advance the iterator to the next slot - inline self& operator++() - { - if (currslot + 1 < currnode->slotuse) { - ++currslot; - } - else if (currnode->nextleaf != NULL) { - currnode = currnode->nextleaf; - currslot = 0; - } - else { - // this is end() - currslot = currnode->slotuse; - } - - return *this; - } - - /// Postfix++ advance the iterator to the next slot - inline self operator++(int) - { - self tmp = *this; // copy ourselves - - if (currslot + 1 < currnode->slotuse) { - ++currslot; - } - else if (currnode->nextleaf != NULL) { - currnode = currnode->nextleaf; - currslot = 0; - } - else { - // this is end() - currslot = currnode->slotuse; - } - - return tmp; - } - - /// Prefix-- backstep the iterator to the last slot - inline self& operator--() - { - if (currslot > 0) { - --currslot; - } - else if (currnode->prevleaf != NULL) { - currnode = currnode->prevleaf; - currslot = currnode->slotuse - 1; - } - else { - // this is begin() - currslot = 0; - } - - return *this; - } - - /// Postfix-- backstep the iterator to the last slot - inline self operator--(int) - { - self tmp = *this; // copy ourselves - - if (currslot > 0) { - --currslot; - } - else if (currnode->prevleaf != NULL) { - currnode = currnode->prevleaf; - currslot = currnode->slotuse - 1; - } - else { - // this is begin() - currslot = 0; - } - - return tmp; - } - - /// Equality of iterators - inline bool operator==(const self& x) const - { - return (x.currnode == currnode) && (x.currslot == currslot); - } - - /// Inequality of iterators - inline bool operator!=(const self& x) const - { - return (x.currnode != currnode) || (x.currslot != currslot); - } - }; - - /// STL-like read-only iterator object for B+ tree items. The iterator - /// points to a specific slot number in a leaf. - class const_iterator - { - public: - // *** Types - - /// The key type of the btree. Returned by key(). - typedef typename btree::key_type key_type; - - /// The data type of the btree. Returned by data(). - typedef typename btree::data_type data_type; - - /// The value type of the btree. Returned by operator*(). - typedef typename btree::value_type value_type; - - /// The pair type of the btree. - typedef typename btree::pair_type pair_type; - - /// Reference to the value_type. STL required. - typedef const value_type& reference; - - /// Pointer to the value_type. STL required. - typedef const value_type* pointer; - - /// STL-magic iterator category - typedef std::bidirectional_iterator_tag iterator_category; - - /// STL-magic - typedef ptrdiff_t difference_type; - - /// Our own type - typedef const_iterator self; - - private: - // *** Members - - /// The currently referenced leaf node of the tree - const typename btree::leaf_node* currnode; - - /// Current key/data slot referenced - unsigned short currslot; - - /// Friendly to the reverse_const_iterator, so it may access the two - /// data items directly - friend class const_reverse_iterator; - - /// Evil! A temporary value_type to STL-correctly deliver operator* and - /// operator-> - mutable value_type temp_value; - - // The macro BTREE_FRIENDS can be used by outside class to access the B+ - // tree internals. This was added for wxBTreeDemo to be able to draw the - // tree. - BTREE_FRIENDS - - public: - // *** Methods - - /// Default-Constructor of a const iterator - inline const_iterator() - : currnode(NULL), currslot(0) - { } - - /// Initializing-Constructor of a const iterator - inline const_iterator(const typename btree::leaf_node *l, unsigned short s) - : currnode(l), currslot(s) - { } - - /// Copy-constructor from a mutable iterator - inline const_iterator(const iterator &it) - : currnode(it.currnode), currslot(it.currslot) - { } - - /// Copy-constructor from a mutable reverse iterator - inline const_iterator(const reverse_iterator &it) - : currnode(it.currnode), currslot(it.currslot) - { } - - /// Copy-constructor from a const reverse iterator - inline const_iterator(const const_reverse_iterator &it) - : currnode(it.currnode), currslot(it.currslot) - { } - - /// Dereference the iterator. Do not use this if possible, use key() - /// and data() instead. The B+ tree does not stored key and data - /// together. - inline reference operator*() const - { - temp_value = pair_to_value_type()( pair_type(key(),data()) ); - return temp_value; - } - - /// Dereference the iterator. Do not use this if possible, use key() - /// and data() instead. The B+ tree does not stored key and data - /// together. - inline pointer operator->() const - { - temp_value = pair_to_value_type()( pair_type(key(),data()) ); - return &temp_value; - } - - /// Key of the current slot - inline const key_type& key() const - { - return currnode->slotkey[currslot]; - } - - /// Read-only reference to the current data object - inline const data_type& data() const - { - return currnode->slotdata[used_as_set ? 0 : currslot]; - } - - /// Prefix++ advance the iterator to the next slot - inline self& operator++() - { - if (currslot + 1 < currnode->slotuse) { - ++currslot; - } - else if (currnode->nextleaf != NULL) { - currnode = currnode->nextleaf; - currslot = 0; - } - else { - // this is end() - currslot = currnode->slotuse; - } - - return *this; - } - - /// Postfix++ advance the iterator to the next slot - inline self operator++(int) - { - self tmp = *this; // copy ourselves - - if (currslot + 1 < currnode->slotuse) { - ++currslot; - } - else if (currnode->nextleaf != NULL) { - currnode = currnode->nextleaf; - currslot = 0; - } - else { - // this is end() - currslot = currnode->slotuse; - } - - return tmp; - } - - /// Prefix-- backstep the iterator to the last slot - inline self& operator--() - { - if (currslot > 0) { - --currslot; - } - else if (currnode->prevleaf != NULL) { - currnode = currnode->prevleaf; - currslot = currnode->slotuse - 1; - } - else { - // this is begin() - currslot = 0; - } - - return *this; - } - - /// Postfix-- backstep the iterator to the last slot - inline self operator--(int) - { - self tmp = *this; // copy ourselves - - if (currslot > 0) { - --currslot; - } - else if (currnode->prevleaf != NULL) { - currnode = currnode->prevleaf; - currslot = currnode->slotuse - 1; - } - else { - // this is begin() - currslot = 0; - } - - return tmp; - } - - /// Equality of iterators - inline bool operator==(const self& x) const - { - return (x.currnode == currnode) && (x.currslot == currslot); - } - - /// Inequality of iterators - inline bool operator!=(const self& x) const - { - return (x.currnode != currnode) || (x.currslot != currslot); - } - }; - - /// STL-like mutable reverse iterator object for B+ tree items. The - /// iterator points to a specific slot number in a leaf. - class reverse_iterator - { - public: - // *** Types - - /// The key type of the btree. Returned by key(). - typedef typename btree::key_type key_type; - - /// The data type of the btree. Returned by data(). - typedef typename btree::data_type data_type; - - /// The value type of the btree. Returned by operator*(). - typedef typename btree::value_type value_type; - - /// The pair type of the btree. - typedef typename btree::pair_type pair_type; - - /// Reference to the value_type. STL required. - typedef value_type& reference; - - /// Pointer to the value_type. STL required. - typedef value_type* pointer; - - /// STL-magic iterator category - typedef std::bidirectional_iterator_tag iterator_category; - - /// STL-magic - typedef ptrdiff_t difference_type; - - /// Our own type - typedef reverse_iterator self; - - private: - // *** Members - - /// The currently referenced leaf node of the tree - typename btree::leaf_node* currnode; - - /// One slot past the current key/data slot referenced. - unsigned short currslot; - - /// Friendly to the const_iterator, so it may access the two data items - /// directly - friend class iterator; - - /// Also friendly to the const_iterator, so it may access the two data - /// items directly - friend class const_iterator; - - /// Also friendly to the const_iterator, so it may access the two data - /// items directly - friend class const_reverse_iterator; - - /// Evil! A temporary value_type to STL-correctly deliver operator* and - /// operator-> - mutable value_type temp_value; - - // The macro BTREE_FRIENDS can be used by outside class to access the B+ - // tree internals. This was added for wxBTreeDemo to be able to draw the - // tree. - BTREE_FRIENDS - - public: - // *** Methods - - /// Default-Constructor of a reverse iterator - inline reverse_iterator() - : currnode(NULL), currslot(0) - { } - - /// Initializing-Constructor of a mutable reverse iterator - inline reverse_iterator(typename btree::leaf_node *l, unsigned short s) - : currnode(l), currslot(s) - { } - - /// Copy-constructor from a mutable iterator - inline reverse_iterator(const iterator &it) - : currnode(it.currnode), currslot(it.currslot) - { } - - /// Dereference the iterator, this is not a value_type& because key and - /// value are not stored together - inline reference operator*() const - { - BTREE_ASSERT(currslot > 0); - temp_value = pair_to_value_type()( pair_type(key(),data()) ); - return temp_value; - } - - /// Dereference the iterator. Do not use this if possible, use key() - /// and data() instead. The B+ tree does not stored key and data - /// together. - inline pointer operator->() const - { - BTREE_ASSERT(currslot > 0); - temp_value = pair_to_value_type()( pair_type(key(),data()) ); - return &temp_value; - } - - /// Key of the current slot - inline const key_type& key() const - { - BTREE_ASSERT(currslot > 0); - return currnode->slotkey[currslot - 1]; - } - - /// Writable reference to the current data object - inline data_type& data() const - { - BTREE_ASSERT(currslot > 0); - return currnode->slotdata[used_as_set ? 0 : currslot-1]; - } - - /// Prefix++ advance the iterator to the next slot - inline self& operator++() - { - if (currslot > 1) { - --currslot; - } - else if (currnode->prevleaf != NULL) { - currnode = currnode->prevleaf; - currslot = currnode->slotuse; - } - else { - // this is begin() == rend() - currslot = 0; - } - - return *this; - } - - /// Postfix++ advance the iterator to the next slot - inline self operator++(int) - { - self tmp = *this; // copy ourselves - - if (currslot > 1) { - --currslot; - } - else if (currnode->prevleaf != NULL) { - currnode = currnode->prevleaf; - currslot = currnode->slotuse; - } - else { - // this is begin() == rend() - currslot = 0; - } - - return tmp; - } - - /// Prefix-- backstep the iterator to the last slot - inline self& operator--() - { - if (currslot < currnode->slotuse) { - ++currslot; - } - else if (currnode->nextleaf != NULL) { - currnode = currnode->nextleaf; - currslot = 1; - } - else { - // this is end() == rbegin() - currslot = currnode->slotuse; - } - - return *this; - } - - /// Postfix-- backstep the iterator to the last slot - inline self operator--(int) - { - self tmp = *this; // copy ourselves - - if (currslot < currnode->slotuse) { - ++currslot; - } - else if (currnode->nextleaf != NULL) { - currnode = currnode->nextleaf; - currslot = 1; - } - else { - // this is end() == rbegin() - currslot = currnode->slotuse; - } - - return tmp; - } - - /// Equality of iterators - inline bool operator==(const self& x) const - { - return (x.currnode == currnode) && (x.currslot == currslot); - } - - /// Inequality of iterators - inline bool operator!=(const self& x) const - { - return (x.currnode != currnode) || (x.currslot != currslot); - } - }; - - /// STL-like read-only reverse iterator object for B+ tree items. The - /// iterator points to a specific slot number in a leaf. - class const_reverse_iterator - { - public: - // *** Types - - /// The key type of the btree. Returned by key(). - typedef typename btree::key_type key_type; - - /// The data type of the btree. Returned by data(). - typedef typename btree::data_type data_type; - - /// The value type of the btree. Returned by operator*(). - typedef typename btree::value_type value_type; - - /// The pair type of the btree. - typedef typename btree::pair_type pair_type; - - /// Reference to the value_type. STL required. - typedef const value_type& reference; - - /// Pointer to the value_type. STL required. - typedef const value_type* pointer; - - /// STL-magic iterator category - typedef std::bidirectional_iterator_tag iterator_category; - - /// STL-magic - typedef ptrdiff_t difference_type; - - /// Our own type - typedef const_reverse_iterator self; - - private: - // *** Members - - /// The currently referenced leaf node of the tree - const typename btree::leaf_node* currnode; - - /// One slot past the current key/data slot referenced. - unsigned short currslot; - - /// Friendly to the const_iterator, so it may access the two data items - /// directly. - friend class reverse_iterator; - - /// Evil! A temporary value_type to STL-correctly deliver operator* and - /// operator-> - mutable value_type temp_value; - - // The macro BTREE_FRIENDS can be used by outside class to access the B+ - // tree internals. This was added for wxBTreeDemo to be able to draw the - // tree. - BTREE_FRIENDS - - public: - // *** Methods - - /// Default-Constructor of a const reverse iterator - inline const_reverse_iterator() - : currnode(NULL), currslot(0) - { } - - /// Initializing-Constructor of a const reverse iterator - inline const_reverse_iterator(const typename btree::leaf_node *l, unsigned short s) - : currnode(l), currslot(s) - { } - - /// Copy-constructor from a mutable iterator - inline const_reverse_iterator(const iterator &it) - : currnode(it.currnode), currslot(it.currslot) - { } - - /// Copy-constructor from a const iterator - inline const_reverse_iterator(const const_iterator &it) - : currnode(it.currnode), currslot(it.currslot) - { } - - /// Copy-constructor from a mutable reverse iterator - inline const_reverse_iterator(const reverse_iterator &it) - : currnode(it.currnode), currslot(it.currslot) - { } - - /// Dereference the iterator. Do not use this if possible, use key() - /// and data() instead. The B+ tree does not stored key and data - /// together. - inline reference operator*() const - { - BTREE_ASSERT(currslot > 0); - temp_value = pair_to_value_type()( pair_type(key(),data()) ); - return temp_value; - } - - /// Dereference the iterator. Do not use this if possible, use key() - /// and data() instead. The B+ tree does not stored key and data - /// together. - inline pointer operator->() const - { - BTREE_ASSERT(currslot > 0); - temp_value = pair_to_value_type()( pair_type(key(),data()) ); - return &temp_value; - } - - /// Key of the current slot - inline const key_type& key() const - { - BTREE_ASSERT(currslot > 0); - return currnode->slotkey[currslot - 1]; - } - - /// Read-only reference to the current data object - inline const data_type& data() const - { - BTREE_ASSERT(currslot > 0); - return currnode->slotdata[used_as_set ? 0 : currslot-1]; - } - - /// Prefix++ advance the iterator to the previous slot - inline self& operator++() - { - if (currslot > 1) { - --currslot; - } - else if (currnode->prevleaf != NULL) { - currnode = currnode->prevleaf; - currslot = currnode->slotuse; - } - else { - // this is begin() == rend() - currslot = 0; - } - - return *this; - } - - /// Postfix++ advance the iterator to the previous slot - inline self operator++(int) - { - self tmp = *this; // copy ourselves - - if (currslot > 1) { - --currslot; - } - else if (currnode->prevleaf != NULL) { - currnode = currnode->prevleaf; - currslot = currnode->slotuse; - } - else { - // this is begin() == rend() - currslot = 0; - } - - return tmp; - } - - /// Prefix-- backstep the iterator to the next slot - inline self& operator--() - { - if (currslot < currnode->slotuse) { - ++currslot; - } - else if (currnode->nextleaf != NULL) { - currnode = currnode->nextleaf; - currslot = 1; - } - else { - // this is end() == rbegin() - currslot = currnode->slotuse; - } - - return *this; - } - - /// Postfix-- backstep the iterator to the next slot - inline self operator--(int) - { - self tmp = *this; // copy ourselves - - if (currslot < currnode->slotuse) { - ++currslot; - } - else if (currnode->nextleaf != NULL) { - currnode = currnode->nextleaf; - currslot = 1; - } - else { - // this is end() == rbegin() - currslot = currnode->slotuse; - } - - return tmp; - } - - /// Equality of iterators - inline bool operator==(const self& x) const - { - return (x.currnode == currnode) && (x.currslot == currslot); - } - - /// Inequality of iterators - inline bool operator!=(const self& x) const - { - return (x.currnode != currnode) || (x.currslot != currslot); - } - }; - -public: - // *** Small Statistics Structure - - /** A small struct containing basic statistics about the B+ tree. It can be - * fetched using get_stats(). */ - struct tree_stats - { - /// Number of items in the B+ tree - size_type itemcount; - - /// Number of leaves in the B+ tree - size_type leaves; - - /// Number of inner nodes in the B+ tree - size_type innernodes; - - /// Base B+ tree parameter: The number of key/data slots in each leaf - static const unsigned short leafslots = btree_self::leafslotmax; - - /// Base B+ tree parameter: The number of key slots in each inner node. - static const unsigned short innerslots = btree_self::innerslotmax; - - /// Zero initialized - inline tree_stats() - : itemcount(0), - leaves(0), innernodes(0) - { - } - - /// Return the total number of nodes - inline size_type nodes() const - { - return innernodes + leaves; - } - - /// Return the average fill of leaves - inline double avgfill_leaves() const - { - return static_cast(itemcount) / (leaves * leafslots); - } - }; - -private: - // *** Tree Object Data Members - - /// Pointer to the B+ tree's root node, either leaf or inner node - node* m_root; - - /// Pointer to first leaf in the double linked leaf chain - leaf_node *m_headleaf; - - /// Pointer to last leaf in the double linked leaf chain - leaf_node *m_tailleaf; - - /// Other small statistics about the B+ tree - tree_stats m_stats; - - /// Key comparison object. More comparison functions are generated from - /// this < relation. - key_compare m_key_less; - - /// Memory allocator. - allocator_type m_allocator; - -public: - // *** Constructors and Destructor - - /// Default constructor initializing an empty B+ tree with the standard key - /// comparison function - explicit inline btree(const allocator_type &alloc = allocator_type()) - : m_root(NULL), m_headleaf(NULL), m_tailleaf(NULL), m_allocator(alloc) - { - } - - /// Constructor initializing an empty B+ tree with a special key - /// comparison object - explicit inline btree(const key_compare &kcf, - const allocator_type &alloc = allocator_type()) - : m_root(NULL), m_headleaf(NULL), m_tailleaf(NULL), - m_key_less(kcf), m_allocator(alloc) - { - } - - /// Constructor initializing a B+ tree with the range [first,last). The - /// range need not be sorted. To create a B+ tree from a sorted range, use - /// bulk_load(). - template - inline btree(InputIterator first, InputIterator last, - const allocator_type &alloc = allocator_type()) - : m_root(NULL), m_headleaf(NULL), m_tailleaf(NULL), m_allocator(alloc) - { - insert(first, last); - } - - /// Constructor initializing a B+ tree with the range [first,last) and a - /// special key comparison object. The range need not be sorted. To create - /// a B+ tree from a sorted range, use bulk_load(). - template - inline btree(InputIterator first, InputIterator last, const key_compare &kcf, - const allocator_type &alloc = allocator_type()) - : m_root(NULL), m_headleaf(NULL), m_tailleaf(NULL), - m_key_less(kcf), m_allocator(alloc) - { - insert(first, last); - } - - /// Frees up all used B+ tree memory pages - inline ~btree() - { - clear(); - } - - /// Fast swapping of two identical B+ tree objects. - void swap(btree_self& from) - { - std::swap(m_root, from.m_root); - std::swap(m_headleaf, from.m_headleaf); - std::swap(m_tailleaf, from.m_tailleaf); - std::swap(m_stats, from.m_stats); - std::swap(m_key_less, from.m_key_less); - std::swap(m_allocator, from.m_allocator); - } - -public: - // *** Key and Value Comparison Function Objects - - /// Function class to compare value_type objects. Required by the STL - class value_compare - { - protected: - /// Key comparison function from the template parameter - key_compare key_comp; - - /// Constructor called from btree::value_comp() - inline value_compare(key_compare kc) - : key_comp(kc) - { } - - /// Friendly to the btree class so it may call the constructor - friend class btree; - - public: - /// Function call "less"-operator resulting in true if x < y. - inline bool operator()(const value_type& x, const value_type& y) const - { - return key_comp(x.first, y.first); - } - }; - - /// Constant access to the key comparison object sorting the B+ tree - inline key_compare key_comp() const - { - return m_key_less; - } - - /// Constant access to a constructed value_type comparison object. Required - /// by the STL - inline value_compare value_comp() const - { - return value_compare(m_key_less); - } - -private: - // *** Convenient Key Comparison Functions Generated From key_less - - /// True if a < b ? "constructed" from m_key_less() - inline bool key_less(const key_type &a, const key_type b) const - { - return m_key_less(a, b); - } - - /// True if a <= b ? constructed from key_less() - inline bool key_lessequal(const key_type &a, const key_type b) const - { - return !m_key_less(b, a); - } - - /// True if a > b ? constructed from key_less() - inline bool key_greater(const key_type &a, const key_type &b) const - { - return m_key_less(b, a); - } - - /// True if a >= b ? constructed from key_less() - inline bool key_greaterequal(const key_type &a, const key_type b) const - { - return !m_key_less(a, b); - } - - /// True if a == b ? constructed from key_less(). This requires the < - /// relation to be a total order, otherwise the B+ tree cannot be sorted. - inline bool key_equal(const key_type &a, const key_type &b) const - { - return !m_key_less(a, b) && !m_key_less(b, a); - } - -public: - // *** Allocators - - /// Return the base node allocator provided during construction. - allocator_type get_allocator() const - { - return m_allocator; - } - -private: - // *** Node Object Allocation and Deallocation Functions - - /// Return an allocator for leaf_node objects - typename leaf_node::alloc_type leaf_node_allocator() - { - return typename leaf_node::alloc_type(m_allocator); - } - - /// Return an allocator for inner_node objects - typename inner_node::alloc_type inner_node_allocator() - { - return typename inner_node::alloc_type(m_allocator); - } - - /// Allocate and initialize a leaf node - inline leaf_node* allocate_leaf() - { - leaf_node *n = new (leaf_node_allocator().allocate(1)) leaf_node(); - n->initialize(); - m_stats.leaves++; - return n; - } - - /// Allocate and initialize an inner node - inline inner_node* allocate_inner(unsigned short level) - { - inner_node *n = new (inner_node_allocator().allocate(1)) inner_node(); - n->initialize(level); - m_stats.innernodes++; - return n; - } - - /// Correctly free either inner or leaf node, destructs all contained key - /// and value objects - inline void free_node(node *n) - { - if (n->isleafnode()) { - leaf_node *ln = static_cast(n); - typename leaf_node::alloc_type a(leaf_node_allocator()); - a.destroy(ln); - a.deallocate(ln, 1); - m_stats.leaves--; - } - else { - inner_node *in = static_cast(n); - typename inner_node::alloc_type a(inner_node_allocator()); - a.destroy(in); - a.deallocate(in, 1); - m_stats.innernodes--; - } - } - - /// Convenient template function for conditional copying of slotdata. This - /// should be used instead of std::copy for all slotdata manipulations. - template - static OutputIterator data_copy (InputIterator first, InputIterator last, - OutputIterator result) - { - if (used_as_set) return result; // no operation - else return std::copy(first, last, result); - } - - /// Convenient template function for conditional copying of slotdata. This - /// should be used instead of std::copy for all slotdata manipulations. - template - static OutputIterator data_copy_backward (InputIterator first, InputIterator last, - OutputIterator result) - { - if (used_as_set) return result; // no operation - else return std::copy_backward(first, last, result); - } - -public: - // *** Fast Destruction of the B+ Tree - - /// Frees all key/data pairs and all nodes of the tree - void clear() - { - if (m_root) - { - clear_recursive(m_root); - free_node(m_root); - - m_root = NULL; - m_headleaf = m_tailleaf = NULL; - - m_stats = tree_stats(); - } - - BTREE_ASSERT(m_stats.itemcount == 0); - } - -private: - /// Recursively free up nodes - void clear_recursive(node *n) - { - if (n->isleafnode()) - { - leaf_node *leafnode = static_cast(n); - - for (unsigned int slot = 0; slot < leafnode->slotuse; ++slot) - { - // data objects are deleted by leaf_node's destructor - } - } - else - { - inner_node *innernode = static_cast(n); - - for (unsigned short slot = 0; slot < innernode->slotuse + 1; ++slot) - { - clear_recursive(innernode->childid[slot]); - free_node(innernode->childid[slot]); - } - } - } - -public: - // *** STL Iterator Construction Functions - - /// Constructs a read/data-write iterator that points to the first slot in - /// the first leaf of the B+ tree. - inline iterator begin() - { - return iterator(m_headleaf, 0); - } - - /// Constructs a read/data-write iterator that points to the first invalid - /// slot in the last leaf of the B+ tree. - inline iterator end() - { - return iterator(m_tailleaf, m_tailleaf ? m_tailleaf->slotuse : 0); - } - - /// Constructs a read-only constant iterator that points to the first slot - /// in the first leaf of the B+ tree. - inline const_iterator begin() const - { - return const_iterator(m_headleaf, 0); - } - - /// Constructs a read-only constant iterator that points to the first - /// invalid slot in the last leaf of the B+ tree. - inline const_iterator end() const - { - return const_iterator(m_tailleaf, m_tailleaf ? m_tailleaf->slotuse : 0); - } - - /// Constructs a read/data-write reverse iterator that points to the first - /// invalid slot in the last leaf of the B+ tree. Uses STL magic. - inline reverse_iterator rbegin() - { - return reverse_iterator(end()); - } - - /// Constructs a read/data-write reverse iterator that points to the first - /// slot in the first leaf of the B+ tree. Uses STL magic. - inline reverse_iterator rend() - { - return reverse_iterator(begin()); - } - - /// Constructs a read-only reverse iterator that points to the first - /// invalid slot in the last leaf of the B+ tree. Uses STL magic. - inline const_reverse_iterator rbegin() const - { - return const_reverse_iterator(end()); - } - - /// Constructs a read-only reverse iterator that points to the first slot - /// in the first leaf of the B+ tree. Uses STL magic. - inline const_reverse_iterator rend() const - { - return const_reverse_iterator(begin()); - } - -private: - // *** B+ Tree Node Binary Search Functions - - /// Searches for the first key in the node n greater or equal to key. Uses - /// binary search with an optional linear self-verification. This is a - /// template function, because the slotkey array is located at different - /// places in leaf_node and inner_node. - template - inline int find_lower(const node_type *n, const key_type& key) const - { - if ( 0 && sizeof(n->slotkey) > traits::binsearch_threshold ) - { - if (n->slotuse == 0) return 0; - - int lo = 0, hi = n->slotuse; - - while (lo < hi) - { - int mid = (lo + hi) >> 1; - - if (key_lessequal(key, n->slotkey[mid])) { - hi = mid; // key <= mid - } - else { - lo = mid + 1; // key > mid - } - } - - BTREE_PRINT("btree::find_lower: on " << n << " key " << key << " -> " << lo << " / " << hi); - - // verify result using simple linear search - if (selfverify) - { - int i = 0; - while (i < n->slotuse && key_less(n->slotkey[i],key)) ++i; - - BTREE_PRINT("btree::find_lower: testfind: " << i); - BTREE_ASSERT(i == lo); - } - - return lo; - } - else // for nodes <= binsearch_threshold do linear search. - { - int lo = 0; - while (lo < n->slotuse && key_less(n->slotkey[lo],key)) ++lo; - return lo; - } - } - - /// Searches for the first key in the node n greater than key. Uses binary - /// search with an optional linear self-verification. This is a template - /// function, because the slotkey array is located at different places in - /// leaf_node and inner_node. - template - inline int find_upper(const node_type *n, const key_type& key) const - { - if ( 0 && sizeof(n->slotkey) > traits::binsearch_threshold ) - { - if (n->slotuse == 0) return 0; - - int lo = 0, hi = n->slotuse; - - while (lo < hi) - { - int mid = (lo + hi) >> 1; - - if (key_less(key, n->slotkey[mid])) { - hi = mid; // key < mid - } - else { - lo = mid + 1; // key >= mid - } - } - - BTREE_PRINT("btree::find_upper: on " << n << " key " << key << " -> " << lo << " / " << hi); - - // verify result using simple linear search - if (selfverify) - { - int i = 0; - while (i < n->slotuse && key_lessequal(n->slotkey[i],key)) ++i; - - BTREE_PRINT("btree::find_upper testfind: " << i); - BTREE_ASSERT(i == hi); - } - - return lo; - } - else // for nodes <= binsearch_threshold do linear search. - { - int lo = 0; - while (lo < n->slotuse && key_lessequal(n->slotkey[lo],key)) ++lo; - return lo; - } - } - -public: - // *** Access Functions to the Item Count - - /// Return the number of key/data pairs in the B+ tree - inline size_type size() const - { - return m_stats.itemcount; - } - - /// Returns true if there is at least one key/data pair in the B+ tree - inline bool empty() const - { - return (size() == size_type(0)); - } - - /// Returns the largest possible size of the B+ Tree. This is just a - /// function required by the STL standard, the B+ Tree can hold more items. - inline size_type max_size() const - { - return size_type(-1); - } - - /// Return a const reference to the current statistics. - inline const struct tree_stats& get_stats() const - { - return m_stats; - } - -public: - // *** Standard Access Functions Querying the Tree by Descending to a Leaf - - /// Non-STL function checking whether a key is in the B+ tree. The same as - /// (find(k) != end()) or (count() != 0). - bool exists(const key_type &key) const - { - const node *n = m_root; - if (!n) return false; - - while(!n->isleafnode()) - { - const inner_node *inner = static_cast(n); - int slot = find_lower(inner, key); - - n = inner->childid[slot]; - } - - const leaf_node *leaf = static_cast(n); - - int slot = find_lower(leaf, key); - return (slot < leaf->slotuse && key_equal(key, leaf->slotkey[slot])); - } - - /// Tries to locate a key in the B+ tree and returns an iterator to the - /// key/data slot if found. If unsuccessful it returns end(). - iterator find(const key_type &key) - { - node *n = m_root; - if (!n) return end(); - - while(!n->isleafnode()) - { - const inner_node *inner = static_cast(n); - int slot = find_lower(inner, key); - - n = inner->childid[slot]; - } - - leaf_node *leaf = static_cast(n); - - int slot = find_lower(leaf, key); - return (slot < leaf->slotuse && key_equal(key, leaf->slotkey[slot])) - ? iterator(leaf, slot) : end(); - } - - /// Tries to locate a key in the B+ tree and returns an constant iterator - /// to the key/data slot if found. If unsuccessful it returns end(). - const_iterator find(const key_type &key) const - { - const node *n = m_root; - if (!n) return end(); - - while(!n->isleafnode()) - { - const inner_node *inner = static_cast(n); - int slot = find_lower(inner, key); - - n = inner->childid[slot]; - } - - const leaf_node *leaf = static_cast(n); - - int slot = find_lower(leaf, key); - return (slot < leaf->slotuse && key_equal(key, leaf->slotkey[slot])) - ? const_iterator(leaf, slot) : end(); - } - - /// Tries to locate a key in the B+ tree and returns the number of - /// identical key entries found. - size_type count(const key_type &key) const - { - const node *n = m_root; - if (!n) return 0; - - while(!n->isleafnode()) - { - const inner_node *inner = static_cast(n); - int slot = find_lower(inner, key); - - n = inner->childid[slot]; - } - - const leaf_node *leaf = static_cast(n); - - int slot = find_lower(leaf, key); - size_type num = 0; - - while (leaf && slot < leaf->slotuse && key_equal(key, leaf->slotkey[slot])) - { - ++num; - if (++slot >= leaf->slotuse) - { - leaf = leaf->nextleaf; - slot = 0; - } - } - - return num; - } - - /// Searches the B+ tree and returns an iterator to the first pair - /// equal to or greater than key, or end() if all keys are smaller. - iterator lower_bound(const key_type& key) - { - node *n = m_root; - if (!n) return end(); - - while(!n->isleafnode()) - { - const inner_node *inner = static_cast(n); - int slot = find_lower(inner, key); - - n = inner->childid[slot]; - } - - leaf_node *leaf = static_cast(n); - - int slot = find_lower(leaf, key); - return iterator(leaf, slot); - } - - /// Searches the B+ tree and returns a constant iterator to the - /// first pair equal to or greater than key, or end() if all keys - /// are smaller. - const_iterator lower_bound(const key_type& key) const - { - const node *n = m_root; - if (!n) return end(); - - while(!n->isleafnode()) - { - const inner_node *inner = static_cast(n); - int slot = find_lower(inner, key); - - n = inner->childid[slot]; - } - - const leaf_node *leaf = static_cast(n); - - int slot = find_lower(leaf, key); - return const_iterator(leaf, slot); - } - - /// Searches the B+ tree and returns an iterator to the first pair - /// greater than key, or end() if all keys are smaller or equal. - iterator upper_bound(const key_type& key) - { - node *n = m_root; - if (!n) return end(); - - while(!n->isleafnode()) - { - const inner_node *inner = static_cast(n); - int slot = find_upper(inner, key); - - n = inner->childid[slot]; - } - - leaf_node *leaf = static_cast(n); - - int slot = find_upper(leaf, key); - return iterator(leaf, slot); - } - - /// Searches the B+ tree and returns a constant iterator to the - /// first pair greater than key, or end() if all keys are smaller - /// or equal. - const_iterator upper_bound(const key_type& key) const - { - const node *n = m_root; - if (!n) return end(); - - while(!n->isleafnode()) - { - const inner_node *inner = static_cast(n); - int slot = find_upper(inner, key); - - n = inner->childid[slot]; - } - - const leaf_node *leaf = static_cast(n); - - int slot = find_upper(leaf, key); - return const_iterator(leaf, slot); - } - - /// Searches the B+ tree and returns both lower_bound() and upper_bound(). - inline std::pair equal_range(const key_type& key) - { - return std::pair(lower_bound(key), upper_bound(key)); - } - - /// Searches the B+ tree and returns both lower_bound() and upper_bound(). - inline std::pair equal_range(const key_type& key) const - { - return std::pair(lower_bound(key), upper_bound(key)); - } - -public: - // *** B+ Tree Object Comparison Functions - - /// Equality relation of B+ trees of the same type. B+ trees of the same - /// size and equal elements (both key and data) are considered - /// equal. Beware of the random ordering of duplicate keys. - inline bool operator==(const btree_self &other) const - { - return (size() == other.size()) && std::equal(begin(), end(), other.begin()); - } - - /// Inequality relation. Based on operator==. - inline bool operator!=(const btree_self &other) const - { - return !(*this == other); - } - - /// Total ordering relation of B+ trees of the same type. It uses - /// std::lexicographical_compare() for the actual comparison of elements. - inline bool operator<(const btree_self &other) const - { - return std::lexicographical_compare(begin(), end(), other.begin(), other.end()); - } - - /// Greater relation. Based on operator<. - inline bool operator>(const btree_self &other) const - { - return other < *this; - } - - /// Less-equal relation. Based on operator<. - inline bool operator<=(const btree_self &other) const - { - return !(other < *this); - } - - /// Greater-equal relation. Based on operator<. - inline bool operator>=(const btree_self &other) const - { - return !(*this < other); - } - -public: - /// *** Fast Copy: Assign Operator and Copy Constructors - - /// Assignment operator. All the key/data pairs are copied - inline btree_self& operator= (const btree_self &other) - { - if (this != &other) - { - clear(); - - m_key_less = other.key_comp(); - m_allocator = other.get_allocator(); - - if (other.size() != 0) - { - m_stats.leaves = m_stats.innernodes = 0; - if (other.m_root) { - m_root = copy_recursive(other.m_root); - } - m_stats = other.m_stats; - } - - if (selfverify) verify(); - } - return *this; - } - - /// Copy constructor. The newly initialized B+ tree object will contain a - /// copy of all key/data pairs. - inline btree(const btree_self &other) - : m_root(NULL), m_headleaf(NULL), m_tailleaf(NULL), - m_stats( other.m_stats ), - m_key_less( other.key_comp() ), - m_allocator( other.get_allocator() ) - { - if (size() > 0) - { - m_stats.leaves = m_stats.innernodes = 0; - if (other.m_root) { - m_root = copy_recursive(other.m_root); - } - if (selfverify) verify(); - } - } - -private: - /// Recursively copy nodes from another B+ tree object - struct node* copy_recursive(const node *n) - { - if (n->isleafnode()) - { - const leaf_node *leaf = static_cast(n); - leaf_node *newleaf = allocate_leaf(); - - newleaf->slotuse = leaf->slotuse; - std::copy(leaf->slotkey, leaf->slotkey + leaf->slotuse, newleaf->slotkey); - data_copy(leaf->slotdata, leaf->slotdata + leaf->slotuse, newleaf->slotdata); - - if (m_headleaf == NULL) - { - m_headleaf = m_tailleaf = newleaf; - newleaf->prevleaf = newleaf->nextleaf = NULL; - } - else - { - newleaf->prevleaf = m_tailleaf; - m_tailleaf->nextleaf = newleaf; - m_tailleaf = newleaf; - } - - return newleaf; - } - else - { - const inner_node *inner = static_cast(n); - inner_node *newinner = allocate_inner(inner->level); - - newinner->slotuse = inner->slotuse; - std::copy(inner->slotkey, inner->slotkey + inner->slotuse, newinner->slotkey); - - for (unsigned short slot = 0; slot <= inner->slotuse; ++slot) - { - newinner->childid[slot] = copy_recursive(inner->childid[slot]); - } - - return newinner; - } - } - -public: - // *** Public Insertion Functions - - /// Attempt to insert a key/data pair into the B+ tree. If the tree does not - /// allow duplicate keys, then the insert may fail if it is already - /// present. - inline std::pair insert(const pair_type& x) - { - return insert_start(x.first, x.second); - } - - /// Attempt to insert a key/data pair into the B+ tree. Beware that if - /// key_type == data_type, then the template iterator insert() is called - /// instead. If the tree does not allow duplicate keys, then the insert may - /// fail if it is already present. - inline std::pair insert(const key_type& key, const data_type& data) - { - return insert_start(key, data); - } - - /// Attempt to insert a key/data pair into the B+ tree. This function is the - /// same as the other insert, however if key_type == data_type then the - /// non-template function cannot be called. If the tree does not allow - /// duplicate keys, then the insert may fail if it is already present. - inline std::pair insert2(const key_type& key, const data_type& data) - { - return insert_start(key, data); - } - - /// Attempt to insert a key/data pair into the B+ tree. The iterator hint - /// is currently ignored by the B+ tree insertion routine. - inline iterator insert(iterator /* hint */, const pair_type &x) - { - return insert_start(x.first, x.second).first; - } - - /// Attempt to insert a key/data pair into the B+ tree. The iterator hint is - /// currently ignored by the B+ tree insertion routine. - inline iterator insert2(iterator /* hint */, const key_type& key, const data_type& data) - { - return insert_start(key, data).first; - } - - /// Attempt to insert the range [first,last) of value_type pairs into the - /// B+ tree. Each key/data pair is inserted individually; to bulk load the - /// tree, use a constructor with range. - template - inline void insert(InputIterator first, InputIterator last) - { - InputIterator iter = first; - while(iter != last) - { - insert(*iter); - ++iter; - } - } - -private: - // *** Private Insertion Functions - - /// Start the insertion descent at the current root and handle root - /// splits. Returns true if the item was inserted - std::pair insert_start(const key_type& key, const data_type& value) - { - node *newchild = NULL; - key_type newkey = key_type(); - - if (m_root == NULL) { - m_root = m_headleaf = m_tailleaf = allocate_leaf(); - } - - std::pair r = insert_descend(m_root, key, value, &newkey, &newchild); - - if (newchild) - { - inner_node *newroot = allocate_inner(m_root->level + 1); - newroot->slotkey[0] = newkey; - - newroot->childid[0] = m_root; - newroot->childid[1] = newchild; - - newroot->slotuse = 1; - - m_root = newroot; - } - - // increment itemcount if the item was inserted - if (r.second) ++m_stats.itemcount; - -#ifdef BTREE_DEBUG - if (debug) print(std::cout); -#endif - - if (selfverify) { - verify(); - BTREE_ASSERT(exists(key)); - } - - return r; - } - - /** - * @brief Insert an item into the B+ tree. - * - * Descend down the nodes to a leaf, insert the key/data pair in a free - * slot. If the node overflows, then it must be split and the new split - * node inserted into the parent. Unroll / this splitting up to the root. - */ - std::pair insert_descend(node* n, - const key_type& key, const data_type& value, - key_type* splitkey, node** splitnode) - { - if (!n->isleafnode()) - { - inner_node *inner = static_cast(n); - - key_type newkey = key_type(); - node *newchild = NULL; - - int slot = find_lower(inner, key); - - BTREE_PRINT("btree::insert_descend into " << inner->childid[slot]); - - std::pair r = insert_descend(inner->childid[slot], - key, value, &newkey, &newchild); - - if (newchild) - { - BTREE_PRINT("btree::insert_descend newchild with key " << newkey << " node " << newchild << " at slot " << slot); - - if (inner->isfull()) - { - split_inner_node(inner, splitkey, splitnode, slot); - - BTREE_PRINT("btree::insert_descend done split_inner: putslot: " << slot << " putkey: " << newkey << " upkey: " << *splitkey); - -#ifdef BTREE_DEBUG - if (debug) - { - print_node(std::cout, inner); - print_node(std::cout, *splitnode); - } -#endif - - // check if insert slot is in the split sibling node - BTREE_PRINT("btree::insert_descend switch: " << slot << " > " << inner->slotuse+1); - - if (slot == inner->slotuse+1 && inner->slotuse < (*splitnode)->slotuse) - { - // special case when the insert slot matches the split - // place between the two nodes, then the insert key - // becomes the split key. - - BTREE_ASSERT(inner->slotuse + 1 < innerslotmax); - - inner_node *splitinner = static_cast(*splitnode); - - // move the split key and it's datum into the left node - inner->slotkey[inner->slotuse] = *splitkey; - inner->childid[inner->slotuse+1] = splitinner->childid[0]; - inner->slotuse++; - - // set new split key and move corresponding datum into right node - splitinner->childid[0] = newchild; - *splitkey = newkey; - - return r; - } - else if (slot >= inner->slotuse+1) - { - // in case the insert slot is in the newly create split - // node, we reuse the code below. - - slot -= inner->slotuse+1; - inner = static_cast(*splitnode); - BTREE_PRINT("btree::insert_descend switching to splitted node " << inner << " slot " << slot); - } - } - - // move items and put pointer to child node into correct slot - BTREE_ASSERT(slot >= 0 && slot <= inner->slotuse); - - std::copy_backward(inner->slotkey + slot, inner->slotkey + inner->slotuse, - inner->slotkey + inner->slotuse+1); - std::copy_backward(inner->childid + slot, inner->childid + inner->slotuse+1, - inner->childid + inner->slotuse+2); - - inner->slotkey[slot] = newkey; - inner->childid[slot + 1] = newchild; - inner->slotuse++; - } - - return r; - } - else // n->isleafnode() == true - { - leaf_node *leaf = static_cast(n); - - int slot = find_lower(leaf, key); - - if (!allow_duplicates && slot < leaf->slotuse && key_equal(key, leaf->slotkey[slot])) { - return std::pair(iterator(leaf, slot), false); - } - - if (leaf->isfull()) - { - split_leaf_node(leaf, splitkey, splitnode); - - // check if insert slot is in the split sibling node - if (slot >= leaf->slotuse) - { - slot -= leaf->slotuse; - leaf = static_cast(*splitnode); - } - } - - // move items and put data item into correct data slot - BTREE_ASSERT(slot >= 0 && slot <= leaf->slotuse); - - std::copy_backward(leaf->slotkey + slot, leaf->slotkey + leaf->slotuse, - leaf->slotkey + leaf->slotuse+1); - data_copy_backward(leaf->slotdata + slot, leaf->slotdata + leaf->slotuse, - leaf->slotdata + leaf->slotuse+1); - - leaf->slotkey[slot] = key; - if (!used_as_set) leaf->slotdata[slot] = value; - leaf->slotuse++; - - if (splitnode && leaf != *splitnode && slot == leaf->slotuse-1) - { - // special case: the node was split, and the insert is at the - // last slot of the old node. then the splitkey must be - // updated. - *splitkey = key; - } - - return std::pair(iterator(leaf, slot), true); - } - } - - /// Split up a leaf node into two equally-filled sibling leaves. Returns - /// the new nodes and it's insertion key in the two parameters. - void split_leaf_node(leaf_node* leaf, key_type* _newkey, node** _newleaf) - { - BTREE_ASSERT(leaf->isfull()); - - unsigned int mid = (leaf->slotuse >> 1); - - BTREE_PRINT("btree::split_leaf_node on " << leaf); - - leaf_node *newleaf = allocate_leaf(); - - newleaf->slotuse = leaf->slotuse - mid; - - newleaf->nextleaf = leaf->nextleaf; - if (newleaf->nextleaf == NULL) { - BTREE_ASSERT(leaf == m_tailleaf); - m_tailleaf = newleaf; - } - else { - newleaf->nextleaf->prevleaf = newleaf; - } - - std::copy(leaf->slotkey + mid, leaf->slotkey + leaf->slotuse, - newleaf->slotkey); - data_copy(leaf->slotdata + mid, leaf->slotdata + leaf->slotuse, - newleaf->slotdata); - - leaf->slotuse = mid; - leaf->nextleaf = newleaf; - newleaf->prevleaf = leaf; - - *_newkey = leaf->slotkey[leaf->slotuse-1]; - *_newleaf = newleaf; - } - - /// Split up an inner node into two equally-filled sibling nodes. Returns - /// the new nodes and it's insertion key in the two parameters. Requires - /// the slot of the item will be inserted, so the nodes will be the same - /// size after the insert. - void split_inner_node(inner_node* inner, key_type* _newkey, node** _newinner, unsigned int addslot) - { - BTREE_ASSERT(inner->isfull()); - - unsigned int mid = (inner->slotuse >> 1); - - BTREE_PRINT("btree::split_inner: mid " << mid << " addslot " << addslot); - - // if the split is uneven and the overflowing item will be put into the - // larger node, then the smaller split node may underflow - if (addslot <= mid && mid > inner->slotuse - (mid + 1)) - mid--; - - BTREE_PRINT("btree::split_inner: mid " << mid << " addslot " << addslot); - - BTREE_PRINT("btree::split_inner_node on " << inner << " into two nodes " << mid << " and " << inner->slotuse - (mid + 1) << " sized"); - - inner_node *newinner = allocate_inner(inner->level); - - newinner->slotuse = inner->slotuse - (mid + 1); - - std::copy(inner->slotkey + mid+1, inner->slotkey + inner->slotuse, - newinner->slotkey); - std::copy(inner->childid + mid+1, inner->childid + inner->slotuse+1, - newinner->childid); - - inner->slotuse = mid; - - *_newkey = inner->slotkey[mid]; - *_newinner = newinner; - } - -public: - - // *** Bulk Loader - Construct Tree from Sorted Sequence - - /// Bulk load a sorted range. Loads items into leaves and constructs a - /// B-tree above them. The tree must be empty when calling this function. - template - void bulk_load(Iterator ibegin, Iterator iend) - { - BTREE_ASSERT(empty()); - - m_stats.itemcount = iend - ibegin; - - // calculate number of leaves needed, round up. - size_t num_items = iend - ibegin; - size_t num_leaves = (num_items + leafslotmax-1) / leafslotmax; - - BTREE_PRINT("btree::bulk_load, level 0: " << m_stats.itemcount << " items into " << num_leaves << " leaves with up to " << ((iend - ibegin + num_leaves-1) / num_leaves) << " items per leaf."); - - Iterator it = ibegin; - for (size_t i = 0; i < num_leaves; ++i) - { - // allocate new leaf node - leaf_node* leaf = allocate_leaf(); - - // copy keys or (key,value) pairs into leaf nodes, uses template - // switch leaf->set_slot(). - leaf->slotuse = static_cast(num_items / (num_leaves-i)); - for (size_t s = 0; s < leaf->slotuse; ++s, ++it) - leaf->set_slot(s, *it); - - if (m_tailleaf != NULL) { - m_tailleaf->nextleaf = leaf; - leaf->prevleaf = m_tailleaf; - } - else { - m_headleaf = leaf; - } - m_tailleaf = leaf; - - num_items -= leaf->slotuse; - } - - BTREE_ASSERT( it == iend && num_items == 0 ); - - // if the btree is so small to fit into one leaf, then we're done. - if (m_headleaf == m_tailleaf) { - m_root = m_headleaf; - return; - } - - BTREE_ASSERT( m_stats.leaves == num_leaves ); - - // create first level of inner nodes, pointing to the leaves. - size_t num_parents = (num_leaves + (innerslotmax+1)-1) / (innerslotmax+1); - - BTREE_PRINT("btree::bulk_load, level 1: " << num_leaves << " leaves in " << num_parents << " inner nodes with up to " << ((num_leaves + num_parents-1) / num_parents) << " leaves per inner node."); - - // save inner nodes and maxkey for next level. - typedef std::pair nextlevel_type; - nextlevel_type* nextlevel = new nextlevel_type[num_parents]; - - leaf_node* leaf = m_headleaf; - for (size_t i = 0; i < num_parents; ++i) - { - // allocate new inner node at level 1 - inner_node* n = allocate_inner(1); - - n->slotuse = static_cast(num_leaves / (num_parents-i)); - BTREE_ASSERT(n->slotuse > 0); - --n->slotuse; // this counts keys, but an inner node has keys+1 children. - - // copy last key from each leaf and set child - for (unsigned short s = 0; s < n->slotuse; ++s) - { - n->slotkey[s] = leaf->slotkey[leaf->slotuse-1]; - n->childid[s] = leaf; - leaf = leaf->nextleaf; - } - n->childid[n->slotuse] = leaf; - - // track max key of any descendant. - nextlevel[i].first = n; - nextlevel[i].second = &leaf->slotkey[leaf->slotuse-1]; - - leaf = leaf->nextleaf; - num_leaves -= n->slotuse+1; - } - - BTREE_ASSERT( leaf == NULL && num_leaves == 0 ); - - // recursively build inner nodes pointing to inner nodes. - for (int level = 2; num_parents != 1; ++level) - { - size_t num_children = num_parents; - num_parents = (num_children + (innerslotmax+1)-1) / (innerslotmax+1); - - BTREE_PRINT("btree::bulk_load, level " << level << ": " << num_children << " children in " << num_parents << " inner nodes with up to " << ((num_children + num_parents-1) / num_parents) << " children per inner node."); - - size_t inner_index = 0; - for (size_t i = 0; i < num_parents; ++i) - { - // allocate new inner node at level - inner_node* n = allocate_inner(level); - - n->slotuse = static_cast(num_children / (num_parents-i)); - BTREE_ASSERT(n->slotuse > 0); - --n->slotuse; // this counts keys, but an inner node has keys+1 children. - - // copy children and maxkeys from nextlevel - for (unsigned short s = 0; s < n->slotuse; ++s) - { - n->slotkey[s] = *nextlevel[inner_index].second; - n->childid[s] = nextlevel[inner_index].first; - ++inner_index; - } - n->childid[n->slotuse] = nextlevel[inner_index].first; - - // reuse nextlevel array for parents, because we can overwrite - // slots we've already consumed. - nextlevel[i].first = n; - nextlevel[i].second = nextlevel[inner_index].second; - - ++inner_index; - num_children -= n->slotuse+1; - } - - BTREE_ASSERT( num_children == 0 ); - } - - m_root = nextlevel[0].first; - delete [] nextlevel; - - if (selfverify) verify(); - } - -private: - // *** Support Class Encapsulating Deletion Results - - /// Result flags of recursive deletion. - enum result_flags_t - { - /// Deletion successful and no fix-ups necessary. - btree_ok = 0, - - /// Deletion not successful because key was not found. - btree_not_found = 1, - - /// Deletion successful, the last key was updated so parent slotkeys - /// need updates. - btree_update_lastkey = 2, - - /// Deletion successful, children nodes were merged and the parent - /// needs to remove the empty node. - btree_fixmerge = 4 - }; - - /// B+ tree recursive deletion has much information which is needs to be - /// passed upward. - struct result_t - { - /// Merged result flags - result_flags_t flags; - - /// The key to be updated at the parent's slot - key_type lastkey; - - /// Constructor of a result with a specific flag, this can also be used - /// as for implicit conversion. - inline result_t(result_flags_t f = btree_ok) - : flags(f), lastkey() - { } - - /// Constructor with a lastkey value. - inline result_t(result_flags_t f, const key_type &k) - : flags(f), lastkey(k) - { } - - /// Test if this result object has a given flag set. - inline bool has(result_flags_t f) const - { - return (flags & f) != 0; - } - - /// Merge two results OR-ing the result flags and overwriting lastkeys. - inline result_t& operator|= (const result_t &other) - { - flags = result_flags_t(flags | other.flags); - - // we overwrite existing lastkeys on purpose - if (other.has(btree_update_lastkey)) - lastkey = other.lastkey; - - return *this; - } - }; - -public: - // *** Public Erase Functions - - /// Erases one (the first) of the key/data pairs associated with the given - /// key. - bool erase_one(const key_type &key) - { - BTREE_PRINT("btree::erase_one(" << key << ") on btree size " << size()); - - if (selfverify) verify(); - - if (!m_root) return false; - - result_t result = erase_one_descend(key, m_root, NULL, NULL, NULL, NULL, NULL, 0); - - if (!result.has(btree_not_found)) - --m_stats.itemcount; - -#ifdef BTREE_DEBUG - if (debug) print(std::cout); -#endif - if (selfverify) verify(); - - return !result.has(btree_not_found); - } - - /// Erases all the key/data pairs associated with the given key. This is - /// implemented using erase_one(). - size_type erase(const key_type &key) - { - size_type c = 0; - - while( erase_one(key) ) - { - ++c; - if (!allow_duplicates) break; - } - - return c; - } - - /// Erase the key/data pair referenced by the iterator. - void erase(iterator iter) - { - BTREE_PRINT("btree::erase_iter(" << iter.currnode << "," << iter.currslot << ") on btree size " << size()); - - if (selfverify) verify(); - - if (!m_root) return; - - result_t result = erase_iter_descend(iter, m_root, NULL, NULL, NULL, NULL, NULL, 0); - - if (!result.has(btree_not_found)) - --m_stats.itemcount; - -#ifdef BTREE_DEBUG - if (debug) print(std::cout); -#endif - if (selfverify) verify(); - } - -#ifdef BTREE_TODO - /// Erase all key/data pairs in the range [first,last). This function is - /// currently not implemented by the B+ Tree. - void erase(iterator /* first */, iterator /* last */) - { - abort(); - } -#endif - -private: - // *** Private Erase Functions - - /** @brief Erase one (the first) key/data pair in the B+ tree matching key. - * - * Descends down the tree in search of key. During the descent the parent, - * left and right siblings and their parents are computed and passed - * down. Once the key/data pair is found, it is removed from the leaf. If - * the leaf underflows 6 different cases are handled. These cases resolve - * the underflow by shifting key/data pairs from adjacent sibling nodes, - * merging two sibling nodes or trimming the tree. - */ - result_t erase_one_descend(const key_type& key, - node *curr, - node *left, node *right, - inner_node *leftparent, inner_node *rightparent, - inner_node *parent, unsigned int parentslot) - { - if (curr->isleafnode()) - { - leaf_node *leaf = static_cast(curr); - leaf_node *leftleaf = static_cast(left); - leaf_node *rightleaf = static_cast(right); - - int slot = find_lower(leaf, key); - - if (slot >= leaf->slotuse || !key_equal(key, leaf->slotkey[slot])) - { - BTREE_PRINT("Could not find key " << key << " to erase."); - - return btree_not_found; - } - - BTREE_PRINT("Found key in leaf " << curr << " at slot " << slot); - - std::copy(leaf->slotkey + slot+1, leaf->slotkey + leaf->slotuse, - leaf->slotkey + slot); - data_copy(leaf->slotdata + slot+1, leaf->slotdata + leaf->slotuse, - leaf->slotdata + slot); - - leaf->slotuse--; - - result_t myres = btree_ok; - - // if the last key of the leaf was changed, the parent is notified - // and updates the key of this leaf - if (slot == leaf->slotuse) - { - if (parent && parentslot < parent->slotuse) - { - BTREE_ASSERT(parent->childid[parentslot] == curr); - parent->slotkey[parentslot] = leaf->slotkey[leaf->slotuse - 1]; - } - else - { - if (leaf->slotuse >= 1) - { - BTREE_PRINT("Scheduling lastkeyupdate: key " << leaf->slotkey[leaf->slotuse - 1]); - myres |= result_t(btree_update_lastkey, leaf->slotkey[leaf->slotuse - 1]); - } - else - { - BTREE_ASSERT(leaf == m_root); - } - } - } - - if (leaf->isunderflow() && !(leaf == m_root && leaf->slotuse >= 1)) - { - // determine what to do about the underflow - - // case : if this empty leaf is the root, then delete all nodes - // and set root to NULL. - if (leftleaf == NULL && rightleaf == NULL) - { - BTREE_ASSERT(leaf == m_root); - BTREE_ASSERT(leaf->slotuse == 0); - - free_node(m_root); - - m_root = leaf = NULL; - m_headleaf = m_tailleaf = NULL; - - // will be decremented soon by insert_start() - BTREE_ASSERT(m_stats.itemcount == 1); - BTREE_ASSERT(m_stats.leaves == 0); - BTREE_ASSERT(m_stats.innernodes == 0); - - return btree_ok; - } - // case : if both left and right leaves would underflow in case of - // a shift, then merging is necessary. choose the more local merger - // with our parent - else if ( (leftleaf == NULL || leftleaf->isfew()) && (rightleaf == NULL || rightleaf->isfew()) ) - { - if (leftparent == parent) - myres |= merge_leaves(leftleaf, leaf, leftparent); - else - myres |= merge_leaves(leaf, rightleaf, rightparent); - } - // case : the right leaf has extra data, so balance right with current - else if ( (leftleaf != NULL && leftleaf->isfew()) && (rightleaf != NULL && !rightleaf->isfew()) ) - { - if (rightparent == parent) - myres |= shift_left_leaf(leaf, rightleaf, rightparent, parentslot); - else - myres |= merge_leaves(leftleaf, leaf, leftparent); - } - // case : the left leaf has extra data, so balance left with current - else if ( (leftleaf != NULL && !leftleaf->isfew()) && (rightleaf != NULL && rightleaf->isfew()) ) - { - if (leftparent == parent) - shift_right_leaf(leftleaf, leaf, leftparent, parentslot - 1); - else - myres |= merge_leaves(leaf, rightleaf, rightparent); - } - // case : both the leaf and right leaves have extra data and our - // parent, choose the leaf with more data - else if (leftparent == rightparent) - { - if (leftleaf->slotuse <= rightleaf->slotuse) - myres |= shift_left_leaf(leaf, rightleaf, rightparent, parentslot); - else - shift_right_leaf(leftleaf, leaf, leftparent, parentslot - 1); - } - else - { - if (leftparent == parent) - shift_right_leaf(leftleaf, leaf, leftparent, parentslot - 1); - else - myres |= shift_left_leaf(leaf, rightleaf, rightparent, parentslot); - } - } - - return myres; - } - else // !curr->isleafnode() - { - inner_node *inner = static_cast(curr); - inner_node *leftinner = static_cast(left); - inner_node *rightinner = static_cast(right); - - node *myleft, *myright; - inner_node *myleftparent, *myrightparent; - - int slot = find_lower(inner, key); - - if (slot == 0) { - myleft = (left == NULL) ? NULL : (static_cast(left))->childid[left->slotuse - 1]; - myleftparent = leftparent; - } - else { - myleft = inner->childid[slot - 1]; - myleftparent = inner; - } - - if (slot == inner->slotuse) { - myright = (right == NULL) ? NULL : (static_cast(right))->childid[0]; - myrightparent = rightparent; - } - else { - myright = inner->childid[slot + 1]; - myrightparent = inner; - } - - BTREE_PRINT("erase_one_descend into " << inner->childid[slot]); - - result_t result = erase_one_descend(key, - inner->childid[slot], - myleft, myright, - myleftparent, myrightparent, - inner, slot); - - result_t myres = btree_ok; - - if (result.has(btree_not_found)) - { - return result; - } - - if (result.has(btree_update_lastkey)) - { - if (parent && parentslot < parent->slotuse) - { - BTREE_PRINT("Fixing lastkeyupdate: key " << result.lastkey << " into parent " << parent << " at parentslot " << parentslot); - - BTREE_ASSERT(parent->childid[parentslot] == curr); - parent->slotkey[parentslot] = result.lastkey; - } - else - { - BTREE_PRINT("Forwarding lastkeyupdate: key " << result.lastkey); - myres |= result_t(btree_update_lastkey, result.lastkey); - } - } - - if (result.has(btree_fixmerge)) - { - // either the current node or the next is empty and should be removed - if (inner->childid[slot]->slotuse != 0) - slot++; - - // this is the child slot invalidated by the merge - BTREE_ASSERT(inner->childid[slot]->slotuse == 0); - - free_node(inner->childid[slot]); - - std::copy(inner->slotkey + slot, inner->slotkey + inner->slotuse, - inner->slotkey + slot-1); - std::copy(inner->childid + slot+1, inner->childid + inner->slotuse+1, - inner->childid + slot); - - inner->slotuse--; - - if (inner->level == 1) - { - // fix split key for children leaves - slot--; - leaf_node *child = static_cast(inner->childid[slot]); - inner->slotkey[slot] = child->slotkey[ child->slotuse-1 ]; - } - } - - if (inner->isunderflow() && !(inner == m_root && inner->slotuse >= 1)) - { - // case: the inner node is the root and has just one child. that child becomes the new root - if (leftinner == NULL && rightinner == NULL) - { - BTREE_ASSERT(inner == m_root); - BTREE_ASSERT(inner->slotuse == 0); - - m_root = inner->childid[0]; - - inner->slotuse = 0; - free_node(inner); - - return btree_ok; - } - // case : if both left and right leaves would underflow in case of - // a shift, then merging is necessary. choose the more local merger - // with our parent - else if ( (leftinner == NULL || leftinner->isfew()) && (rightinner == NULL || rightinner->isfew()) ) - { - if (leftparent == parent) - myres |= merge_inner(leftinner, inner, leftparent, parentslot - 1); - else - myres |= merge_inner(inner, rightinner, rightparent, parentslot); - } - // case : the right leaf has extra data, so balance right with current - else if ( (leftinner != NULL && leftinner->isfew()) && (rightinner != NULL && !rightinner->isfew()) ) - { - if (rightparent == parent) - shift_left_inner(inner, rightinner, rightparent, parentslot); - else - myres |= merge_inner(leftinner, inner, leftparent, parentslot - 1); - } - // case : the left leaf has extra data, so balance left with current - else if ( (leftinner != NULL && !leftinner->isfew()) && (rightinner != NULL && rightinner->isfew()) ) - { - if (leftparent == parent) - shift_right_inner(leftinner, inner, leftparent, parentslot - 1); - else - myres |= merge_inner(inner, rightinner, rightparent, parentslot); - } - // case : both the leaf and right leaves have extra data and our - // parent, choose the leaf with more data - else if (leftparent == rightparent) - { - if (leftinner->slotuse <= rightinner->slotuse) - shift_left_inner(inner, rightinner, rightparent, parentslot); - else - shift_right_inner(leftinner, inner, leftparent, parentslot - 1); - } - else - { - if (leftparent == parent) - shift_right_inner(leftinner, inner, leftparent, parentslot - 1); - else - shift_left_inner(inner, rightinner, rightparent, parentslot); - } - } - - return myres; - } - } - - /** @brief Erase one key/data pair referenced by an iterator in the B+ - * tree. - * - * Descends down the tree in search of an iterator. During the descent the - * parent, left and right siblings and their parents are computed and - * passed down. The difficulty is that the iterator contains only a pointer - * to a leaf_node, which means that this function must do a recursive depth - * first search for that leaf node in the subtree containing all pairs of - * the same key. This subtree can be very large, even the whole tree, - * though in practice it would not make sense to have so many duplicate - * keys. - * - * Once the referenced key/data pair is found, it is removed from the leaf - * and the same underflow cases are handled as in erase_one_descend. - */ - result_t erase_iter_descend(const iterator& iter, - node *curr, - node *left, node *right, - inner_node *leftparent, inner_node *rightparent, - inner_node *parent, unsigned int parentslot) - { - if (curr->isleafnode()) - { - leaf_node *leaf = static_cast(curr); - leaf_node *leftleaf = static_cast(left); - leaf_node *rightleaf = static_cast(right); - - // if this is not the correct leaf, get next step in recursive - // search - if (leaf != iter.currnode) - { - return btree_not_found; - } - - if (iter.currslot >= leaf->slotuse) - { - BTREE_PRINT("Could not find iterator (" << iter.currnode << "," << iter.currslot << ") to erase. Invalid leaf node?"); - - return btree_not_found; - } - - int slot = iter.currslot; - - BTREE_PRINT("Found iterator in leaf " << curr << " at slot " << slot); - - std::copy(leaf->slotkey + slot+1, leaf->slotkey + leaf->slotuse, - leaf->slotkey + slot); - data_copy(leaf->slotdata + slot+1, leaf->slotdata + leaf->slotuse, - leaf->slotdata + slot); - - leaf->slotuse--; - - result_t myres = btree_ok; - - // if the last key of the leaf was changed, the parent is notified - // and updates the key of this leaf - if (slot == leaf->slotuse) - { - if (parent && parentslot < parent->slotuse) - { - BTREE_ASSERT(parent->childid[parentslot] == curr); - parent->slotkey[parentslot] = leaf->slotkey[leaf->slotuse - 1]; - } - else - { - if (leaf->slotuse >= 1) - { - BTREE_PRINT("Scheduling lastkeyupdate: key " << leaf->slotkey[leaf->slotuse - 1]); - myres |= result_t(btree_update_lastkey, leaf->slotkey[leaf->slotuse - 1]); - } - else - { - BTREE_ASSERT(leaf == m_root); - } - } - } - - if (leaf->isunderflow() && !(leaf == m_root && leaf->slotuse >= 1)) - { - // determine what to do about the underflow - - // case : if this empty leaf is the root, then delete all nodes - // and set root to NULL. - if (leftleaf == NULL && rightleaf == NULL) - { - BTREE_ASSERT(leaf == m_root); - BTREE_ASSERT(leaf->slotuse == 0); - - free_node(m_root); - - m_root = leaf = NULL; - m_headleaf = m_tailleaf = NULL; - - // will be decremented soon by insert_start() - BTREE_ASSERT(m_stats.itemcount == 1); - BTREE_ASSERT(m_stats.leaves == 0); - BTREE_ASSERT(m_stats.innernodes == 0); - - return btree_ok; - } - // case : if both left and right leaves would underflow in case of - // a shift, then merging is necessary. choose the more local merger - // with our parent - else if ( (leftleaf == NULL || leftleaf->isfew()) && (rightleaf == NULL || rightleaf->isfew()) ) - { - if (leftparent == parent) - myres |= merge_leaves(leftleaf, leaf, leftparent); - else - myres |= merge_leaves(leaf, rightleaf, rightparent); - } - // case : the right leaf has extra data, so balance right with current - else if ( (leftleaf != NULL && leftleaf->isfew()) && (rightleaf != NULL && !rightleaf->isfew()) ) - { - if (rightparent == parent) - myres |= shift_left_leaf(leaf, rightleaf, rightparent, parentslot); - else - myres |= merge_leaves(leftleaf, leaf, leftparent); - } - // case : the left leaf has extra data, so balance left with current - else if ( (leftleaf != NULL && !leftleaf->isfew()) && (rightleaf != NULL && rightleaf->isfew()) ) - { - if (leftparent == parent) - shift_right_leaf(leftleaf, leaf, leftparent, parentslot - 1); - else - myres |= merge_leaves(leaf, rightleaf, rightparent); - } - // case : both the leaf and right leaves have extra data and our - // parent, choose the leaf with more data - else if (leftparent == rightparent) - { - if (leftleaf->slotuse <= rightleaf->slotuse) - myres |= shift_left_leaf(leaf, rightleaf, rightparent, parentslot); - else - shift_right_leaf(leftleaf, leaf, leftparent, parentslot - 1); - } - else - { - if (leftparent == parent) - shift_right_leaf(leftleaf, leaf, leftparent, parentslot - 1); - else - myres |= shift_left_leaf(leaf, rightleaf, rightparent, parentslot); - } - } - - return myres; - } - else // !curr->isleafnode() - { - inner_node *inner = static_cast(curr); - inner_node *leftinner = static_cast(left); - inner_node *rightinner = static_cast(right); - - // find first slot below which the searched iterator might be - // located. - - result_t result; - int slot = find_lower(inner, iter.key()); - - while (slot <= inner->slotuse) - { - node *myleft, *myright; - inner_node *myleftparent, *myrightparent; - - if (slot == 0) { - myleft = (left == NULL) ? NULL : (static_cast(left))->childid[left->slotuse - 1]; - myleftparent = leftparent; - } - else { - myleft = inner->childid[slot - 1]; - myleftparent = inner; - } - - if (slot == inner->slotuse) { - myright = (right == NULL) ? NULL : (static_cast(right))->childid[0]; - myrightparent = rightparent; - } - else { - myright = inner->childid[slot + 1]; - myrightparent = inner; - } - - BTREE_PRINT("erase_iter_descend into " << inner->childid[slot]); - - result = erase_iter_descend(iter, - inner->childid[slot], - myleft, myright, - myleftparent, myrightparent, - inner, slot); - - if (!result.has(btree_not_found)) - break; - - // continue recursive search for leaf on next slot - - if (slot < inner->slotuse && key_less(inner->slotkey[slot],iter.key())) - return btree_not_found; - - ++slot; - } - - if (slot > inner->slotuse) - return btree_not_found; - - result_t myres = btree_ok; - - if (result.has(btree_update_lastkey)) - { - if (parent && parentslot < parent->slotuse) - { - BTREE_PRINT("Fixing lastkeyupdate: key " << result.lastkey << " into parent " << parent << " at parentslot " << parentslot); - - BTREE_ASSERT(parent->childid[parentslot] == curr); - parent->slotkey[parentslot] = result.lastkey; - } - else - { - BTREE_PRINT("Forwarding lastkeyupdate: key " << result.lastkey); - myres |= result_t(btree_update_lastkey, result.lastkey); - } - } - - if (result.has(btree_fixmerge)) - { - // either the current node or the next is empty and should be removed - if (inner->childid[slot]->slotuse != 0) - slot++; - - // this is the child slot invalidated by the merge - BTREE_ASSERT(inner->childid[slot]->slotuse == 0); - - free_node(inner->childid[slot]); - - std::copy(inner->slotkey + slot, inner->slotkey + inner->slotuse, - inner->slotkey + slot-1); - std::copy(inner->childid + slot+1, inner->childid + inner->slotuse+1, - inner->childid + slot); - - inner->slotuse--; - - if (inner->level == 1) - { - // fix split key for children leaves - slot--; - leaf_node *child = static_cast(inner->childid[slot]); - inner->slotkey[slot] = child->slotkey[ child->slotuse-1 ]; - } - } - - if (inner->isunderflow() && !(inner == m_root && inner->slotuse >= 1)) - { - // case: the inner node is the root and has just one - // child. that child becomes the new root - if (leftinner == NULL && rightinner == NULL) - { - BTREE_ASSERT(inner == m_root); - BTREE_ASSERT(inner->slotuse == 0); - - m_root = inner->childid[0]; - - inner->slotuse = 0; - free_node(inner); - - return btree_ok; - } - // case : if both left and right leaves would underflow in case of - // a shift, then merging is necessary. choose the more local merger - // with our parent - else if ( (leftinner == NULL || leftinner->isfew()) && (rightinner == NULL || rightinner->isfew()) ) - { - if (leftparent == parent) - myres |= merge_inner(leftinner, inner, leftparent, parentslot - 1); - else - myres |= merge_inner(inner, rightinner, rightparent, parentslot); - } - // case : the right leaf has extra data, so balance right with current - else if ( (leftinner != NULL && leftinner->isfew()) && (rightinner != NULL && !rightinner->isfew()) ) - { - if (rightparent == parent) - shift_left_inner(inner, rightinner, rightparent, parentslot); - else - myres |= merge_inner(leftinner, inner, leftparent, parentslot - 1); - } - // case : the left leaf has extra data, so balance left with current - else if ( (leftinner != NULL && !leftinner->isfew()) && (rightinner != NULL && rightinner->isfew()) ) - { - if (leftparent == parent) - shift_right_inner(leftinner, inner, leftparent, parentslot - 1); - else - myres |= merge_inner(inner, rightinner, rightparent, parentslot); - } - // case : both the leaf and right leaves have extra data and our - // parent, choose the leaf with more data - else if (leftparent == rightparent) - { - if (leftinner->slotuse <= rightinner->slotuse) - shift_left_inner(inner, rightinner, rightparent, parentslot); - else - shift_right_inner(leftinner, inner, leftparent, parentslot - 1); - } - else - { - if (leftparent == parent) - shift_right_inner(leftinner, inner, leftparent, parentslot - 1); - else - shift_left_inner(inner, rightinner, rightparent, parentslot); - } - } - - return myres; - } - } - - /// Merge two leaf nodes. The function moves all key/data pairs from right - /// to left and sets right's slotuse to zero. The right slot is then - /// removed by the calling parent node. - result_t merge_leaves(leaf_node* left, leaf_node* right, inner_node* parent) - { - BTREE_PRINT("Merge leaf nodes " << left << " and " << right << " with common parent " << parent << "."); - (void)parent; - - BTREE_ASSERT(left->isleafnode() && right->isleafnode()); - BTREE_ASSERT(parent->level == 1); - - BTREE_ASSERT(left->slotuse + right->slotuse < leafslotmax); - - std::copy(right->slotkey, right->slotkey + right->slotuse, - left->slotkey + left->slotuse); - data_copy(right->slotdata, right->slotdata + right->slotuse, - left->slotdata + left->slotuse); - - left->slotuse += right->slotuse; - - left->nextleaf = right->nextleaf; - if (left->nextleaf) - left->nextleaf->prevleaf = left; - else - m_tailleaf = left; - - right->slotuse = 0; - - return btree_fixmerge; - } - - /// Merge two inner nodes. The function moves all key/childid pairs from - /// right to left and sets right's slotuse to zero. The right slot is then - /// removed by the calling parent node. - static result_t merge_inner(inner_node* left, inner_node* right, inner_node* parent, unsigned int parentslot) - { - BTREE_PRINT("Merge inner nodes " << left << " and " << right << " with common parent " << parent << "."); - - BTREE_ASSERT(left->level == right->level); - BTREE_ASSERT(parent->level == left->level + 1); - - BTREE_ASSERT(parent->childid[parentslot] == left); - - BTREE_ASSERT(left->slotuse + right->slotuse < innerslotmax); - - if (selfverify) - { - // find the left node's slot in the parent's children - unsigned int leftslot = 0; - while(leftslot <= parent->slotuse && parent->childid[leftslot] != left) - ++leftslot; - - BTREE_ASSERT(leftslot < parent->slotuse); - BTREE_ASSERT(parent->childid[leftslot] == left); - BTREE_ASSERT(parent->childid[leftslot+1] == right); - - BTREE_ASSERT(parentslot == leftslot); - } - - // retrieve the decision key from parent - left->slotkey[left->slotuse] = parent->slotkey[parentslot]; - left->slotuse++; - - // copy over keys and children from right - std::copy(right->slotkey, right->slotkey + right->slotuse, - left->slotkey + left->slotuse); - std::copy(right->childid, right->childid + right->slotuse+1, - left->childid + left->slotuse); - - left->slotuse += right->slotuse; - right->slotuse = 0; - - return btree_fixmerge; - } - - /// Balance two leaf nodes. The function moves key/data pairs from right to - /// left so that both nodes are equally filled. The parent node is updated - /// if possible. - static result_t shift_left_leaf(leaf_node *left, leaf_node *right, inner_node *parent, unsigned int parentslot) - { - BTREE_ASSERT(left->isleafnode() && right->isleafnode()); - BTREE_ASSERT(parent->level == 1); - - BTREE_ASSERT(left->nextleaf == right); - BTREE_ASSERT(left == right->prevleaf); - - BTREE_ASSERT(left->slotuse < right->slotuse); - BTREE_ASSERT(parent->childid[parentslot] == left); - - unsigned int shiftnum = (right->slotuse - left->slotuse) >> 1; - - BTREE_PRINT("Shifting (leaf) " << shiftnum << " entries to left " << left << " from right " << right << " with common parent " << parent << "."); - - BTREE_ASSERT(left->slotuse + shiftnum < leafslotmax); - - // copy the first items from the right node to the last slot in the left node. - - std::copy(right->slotkey, right->slotkey + shiftnum, - left->slotkey + left->slotuse); - data_copy(right->slotdata, right->slotdata + shiftnum, - left->slotdata + left->slotuse); - - left->slotuse += shiftnum; - - // shift all slots in the right node to the left - - std::copy(right->slotkey + shiftnum, right->slotkey + right->slotuse, - right->slotkey); - data_copy(right->slotdata + shiftnum, right->slotdata + right->slotuse, - right->slotdata); - - right->slotuse -= shiftnum; - - // fixup parent - if (parentslot < parent->slotuse) { - parent->slotkey[parentslot] = left->slotkey[left->slotuse - 1]; - return btree_ok; - } - else { // the update is further up the tree - return result_t(btree_update_lastkey, left->slotkey[left->slotuse - 1]); - } - } - - /// Balance two inner nodes. The function moves key/data pairs from right - /// to left so that both nodes are equally filled. The parent node is - /// updated if possible. - static void shift_left_inner(inner_node *left, inner_node *right, inner_node *parent, unsigned int parentslot) - { - BTREE_ASSERT(left->level == right->level); - BTREE_ASSERT(parent->level == left->level + 1); - - BTREE_ASSERT(left->slotuse < right->slotuse); - BTREE_ASSERT(parent->childid[parentslot] == left); - - unsigned int shiftnum = (right->slotuse - left->slotuse) >> 1; - - BTREE_PRINT("Shifting (inner) " << shiftnum << " entries to left " << left << " from right " << right << " with common parent " << parent << "."); - - BTREE_ASSERT(left->slotuse + shiftnum < innerslotmax); - - if (selfverify) - { - // find the left node's slot in the parent's children and compare to parentslot - - unsigned int leftslot = 0; - while(leftslot <= parent->slotuse && parent->childid[leftslot] != left) - ++leftslot; - - BTREE_ASSERT(leftslot < parent->slotuse); - BTREE_ASSERT(parent->childid[leftslot] == left); - BTREE_ASSERT(parent->childid[leftslot+1] == right); - - BTREE_ASSERT(leftslot == parentslot); - } - - // copy the parent's decision slotkey and childid to the first new key on the left - left->slotkey[left->slotuse] = parent->slotkey[parentslot]; - left->slotuse++; - - // copy the other items from the right node to the last slots in the left node. - - std::copy(right->slotkey, right->slotkey + shiftnum-1, - left->slotkey + left->slotuse); - std::copy(right->childid, right->childid + shiftnum, - left->childid + left->slotuse); - - left->slotuse += shiftnum - 1; - - // fixup parent - parent->slotkey[parentslot] = right->slotkey[shiftnum - 1]; - - // shift all slots in the right node - - std::copy(right->slotkey + shiftnum, right->slotkey + right->slotuse, - right->slotkey); - std::copy(right->childid + shiftnum, right->childid + right->slotuse+1, - right->childid); - - right->slotuse -= shiftnum; - } - - /// Balance two leaf nodes. The function moves key/data pairs from left to - /// right so that both nodes are equally filled. The parent node is updated - /// if possible. - static void shift_right_leaf(leaf_node *left, leaf_node *right, inner_node *parent, unsigned int parentslot) - { - BTREE_ASSERT(left->isleafnode() && right->isleafnode()); - BTREE_ASSERT(parent->level == 1); - - BTREE_ASSERT(left->nextleaf == right); - BTREE_ASSERT(left == right->prevleaf); - BTREE_ASSERT(parent->childid[parentslot] == left); - - BTREE_ASSERT(left->slotuse > right->slotuse); - - unsigned int shiftnum = (left->slotuse - right->slotuse) >> 1; - - BTREE_PRINT("Shifting (leaf) " << shiftnum << " entries to right " << right << " from left " << left << " with common parent " << parent << "."); - - if (selfverify) - { - // find the left node's slot in the parent's children - unsigned int leftslot = 0; - while(leftslot <= parent->slotuse && parent->childid[leftslot] != left) - ++leftslot; - - BTREE_ASSERT(leftslot < parent->slotuse); - BTREE_ASSERT(parent->childid[leftslot] == left); - BTREE_ASSERT(parent->childid[leftslot+1] == right); - - BTREE_ASSERT(leftslot == parentslot); - } - - // shift all slots in the right node - - BTREE_ASSERT(right->slotuse + shiftnum < leafslotmax); - - std::copy_backward(right->slotkey, right->slotkey + right->slotuse, - right->slotkey + right->slotuse + shiftnum); - data_copy_backward(right->slotdata, right->slotdata + right->slotuse, - right->slotdata + right->slotuse + shiftnum); - - right->slotuse += shiftnum; - - // copy the last items from the left node to the first slot in the right node. - std::copy(left->slotkey + left->slotuse - shiftnum, left->slotkey + left->slotuse, - right->slotkey); - data_copy(left->slotdata + left->slotuse - shiftnum, left->slotdata + left->slotuse, - right->slotdata); - - left->slotuse -= shiftnum; - - parent->slotkey[parentslot] = left->slotkey[left->slotuse-1]; - } - - /// Balance two inner nodes. The function moves key/data pairs from left to - /// right so that both nodes are equally filled. The parent node is updated - /// if possible. - static void shift_right_inner(inner_node *left, inner_node *right, inner_node *parent, unsigned int parentslot) - { - BTREE_ASSERT(left->level == right->level); - BTREE_ASSERT(parent->level == left->level + 1); - - BTREE_ASSERT(left->slotuse > right->slotuse); - BTREE_ASSERT(parent->childid[parentslot] == left); - - unsigned int shiftnum = (left->slotuse - right->slotuse) >> 1; - - BTREE_PRINT("Shifting (leaf) " << shiftnum << " entries to right " << right << " from left " << left << " with common parent " << parent << "."); - - if (selfverify) - { - // find the left node's slot in the parent's children - unsigned int leftslot = 0; - while(leftslot <= parent->slotuse && parent->childid[leftslot] != left) - ++leftslot; - - BTREE_ASSERT(leftslot < parent->slotuse); - BTREE_ASSERT(parent->childid[leftslot] == left); - BTREE_ASSERT(parent->childid[leftslot+1] == right); - - BTREE_ASSERT(leftslot == parentslot); - } - - // shift all slots in the right node - - BTREE_ASSERT(right->slotuse + shiftnum < innerslotmax); - - std::copy_backward(right->slotkey, right->slotkey + right->slotuse, - right->slotkey + right->slotuse + shiftnum); - std::copy_backward(right->childid, right->childid + right->slotuse+1, - right->childid + right->slotuse+1 + shiftnum); - - right->slotuse += shiftnum; - - // copy the parent's decision slotkey and childid to the last new key on the right - right->slotkey[shiftnum - 1] = parent->slotkey[parentslot]; - - // copy the remaining last items from the left node to the first slot in the right node. - std::copy(left->slotkey + left->slotuse - shiftnum+1, left->slotkey + left->slotuse, - right->slotkey); - std::copy(left->childid + left->slotuse - shiftnum+1, left->childid + left->slotuse+1, - right->childid); - - // copy the first to-be-removed key from the left node to the parent's decision slot - parent->slotkey[parentslot] = left->slotkey[left->slotuse - shiftnum]; - - left->slotuse -= shiftnum; - } - -#ifdef BTREE_DEBUG -public: - // *** Debug Printing - - /// Print out the B+ tree structure with keys onto the given ostream. This - /// function requires that the header is compiled with BTREE_DEBUG and that - /// key_type is printable via std::ostream. - void print(std::ostream &os) const - { - if (m_root) { - print_node(os, m_root, 0, true); - } - } - - /// Print out only the leaves via the double linked list. - void print_leaves(std::ostream &os) const - { - os << "leaves:" << std::endl; - - const leaf_node *n = m_headleaf; - - while(n) - { - os << " " << n << std::endl; - - n = n->nextleaf; - } - } - -private: - - /// Recursively descend down the tree and print out nodes. - static void print_node(std::ostream &os, const node* node, unsigned int depth=0, bool recursive=false) - { - for(unsigned int i = 0; i < depth; i++) os << " "; - - os << "node " << node << " level " << node->level << " slotuse " << node->slotuse << std::endl; - - if (node->isleafnode()) - { - const leaf_node *leafnode = static_cast(node); - - for(unsigned int i = 0; i < depth; i++) os << " "; - os << " leaf prev " << leafnode->prevleaf << " next " << leafnode->nextleaf << std::endl; - - for(unsigned int i = 0; i < depth; i++) os << " "; - - for (unsigned int slot = 0; slot < leafnode->slotuse; ++slot) - { - os << leafnode->slotkey[slot] << " "; // << "(data: " << leafnode->slotdata[slot] << ") "; - } - os << std::endl; - } - else - { - const inner_node *innernode = static_cast(node); - - for(unsigned int i = 0; i < depth; i++) os << " "; - - for (unsigned short slot = 0; slot < innernode->slotuse; ++slot) - { - os << "(" << innernode->childid[slot] << ") " << innernode->slotkey[slot] << " "; - } - os << "(" << innernode->childid[innernode->slotuse] << ")" << std::endl; - - if (recursive) - { - for (unsigned short slot = 0; slot < innernode->slotuse + 1; ++slot) - { - print_node(os, innernode->childid[slot], depth + 1, recursive); - } - } - } - } -#endif - -public: - // *** Verification of B+ Tree Invariants - - /// Run a thorough verification of all B+ tree invariants. The program - /// aborts via assert() if something is wrong. - void verify() const - { - key_type minkey, maxkey; - tree_stats vstats; - - if (m_root) - { - verify_node(m_root, &minkey, &maxkey, vstats); - - assert( vstats.itemcount == m_stats.itemcount ); - assert( vstats.leaves == m_stats.leaves ); - assert( vstats.innernodes == m_stats.innernodes ); - - verify_leaflinks(); - } - } - -private: - - /// Recursively descend down the tree and verify each node - void verify_node(const node* n, key_type* minkey, key_type* maxkey, tree_stats &vstats) const - { - BTREE_PRINT("verifynode " << n); - - if (n->isleafnode()) - { - const leaf_node *leaf = static_cast(n); - - assert( leaf == m_root || !leaf->isunderflow() ); - assert( leaf->slotuse > 0 ); - - for(unsigned short slot = 0; slot < leaf->slotuse - 1; ++slot) - { - assert(key_lessequal(leaf->slotkey[slot], leaf->slotkey[slot + 1])); - } - - *minkey = leaf->slotkey[0]; - *maxkey = leaf->slotkey[leaf->slotuse - 1]; - - vstats.leaves++; - vstats.itemcount += leaf->slotuse; - } - else // !n->isleafnode() - { - const inner_node *inner = static_cast(n); - vstats.innernodes++; - - assert( inner == m_root || !inner->isunderflow() ); - assert( inner->slotuse > 0 ); - - for(unsigned short slot = 0; slot < inner->slotuse - 1; ++slot) - { - assert(key_lessequal(inner->slotkey[slot], inner->slotkey[slot + 1])); - } - - for(unsigned short slot = 0; slot <= inner->slotuse; ++slot) - { - const node *subnode = inner->childid[slot]; - key_type subminkey = key_type(); - key_type submaxkey = key_type(); - - assert(subnode->level + 1 == inner->level); - verify_node(subnode, &subminkey, &submaxkey, vstats); - - BTREE_PRINT("verify subnode " << subnode << ": " << subminkey << " - " << submaxkey); - - if (slot == 0) - *minkey = subminkey; - else - assert(key_greaterequal(subminkey, inner->slotkey[slot-1])); - - if (slot == inner->slotuse) - *maxkey = submaxkey; - else - assert(key_equal(inner->slotkey[slot], submaxkey)); - - if (inner->level == 1 && slot < inner->slotuse) - { - // children are leaves and must be linked together in the - // correct order - const leaf_node *leafa = static_cast(inner->childid[slot]); - const leaf_node *leafb = static_cast(inner->childid[slot + 1]); - - assert(leafa->nextleaf == leafb); - assert(leafa == leafb->prevleaf); - (void)leafa; (void)leafb; - } - if (inner->level == 2 && slot < inner->slotuse) - { - // verify leaf links between the adjacent inner nodes - const inner_node *parenta = static_cast(inner->childid[slot]); - const inner_node *parentb = static_cast(inner->childid[slot+1]); - - const leaf_node *leafa = static_cast(parenta->childid[parenta->slotuse]); - const leaf_node *leafb = static_cast(parentb->childid[0]); - - assert(leafa->nextleaf == leafb); - assert(leafa == leafb->prevleaf); - (void)leafa; (void)leafb; - } - } - } - } - - /// Verify the double linked list of leaves. - void verify_leaflinks() const - { - const leaf_node *n = m_headleaf; - - assert(n->level == 0); - assert(!n || n->prevleaf == NULL); - - unsigned int testcount = 0; - - while(n) - { - assert(n->level == 0); - assert(n->slotuse > 0); - - for(unsigned short slot = 0; slot < n->slotuse - 1; ++slot) - { - assert(key_lessequal(n->slotkey[slot], n->slotkey[slot + 1])); - } - - testcount += n->slotuse; - - if (n->nextleaf) - { - assert(key_lessequal(n->slotkey[n->slotuse-1], n->nextleaf->slotkey[0])); - - assert(n == n->nextleaf->prevleaf); - } - else - { - assert(m_tailleaf == n); - } - - n = n->nextleaf; - } - - assert(testcount == size()); - } - -private: - // *** Dump and Restore of B+ Trees - - /// A header for the binary image containing the base properties of the B+ - /// tree. These properties have to match the current template - /// instantiation. - struct dump_header - { - /// "stx-btree", just to stop the restore() function from loading garbage - char signature[12]; - - /// Currently 0 - unsigned short version; - - /// sizeof(key_type) - unsigned short key_type_size; - - /// sizeof(data_type) - unsigned short data_type_size; - - /// Number of slots in the leaves - unsigned short leafslots; - - /// Number of slots in the inner nodes - unsigned short innerslots; - - /// Allow duplicates - bool allow_duplicates; - - /// The item count of the tree - size_type itemcount; - - /// Fill the struct with the current B+ tree's properties, itemcount is - /// not filled. - inline void fill() - { - // don't want to include string.h just for this signature - signature[0] = 's'; signature[1] = 't'; signature[2] = 'x'; signature[3] = '-'; - signature[4] = 'b'; signature[5] = 't'; signature[6] = 'r'; signature[7] = 'e'; - signature[8] = 'e'; signature[9] = 0; signature[10] = 0; signature[11] = 0; - - version = 0; - key_type_size = sizeof(typename btree_self::key_type); - data_type_size = sizeof(typename btree_self::data_type); - leafslots = btree_self::leafslotmax; - innerslots = btree_self::innerslotmax; - allow_duplicates = btree_self::allow_duplicates; - } - - /// Returns true if the headers have the same vital properties - inline bool same(const struct dump_header &o) const - { - return (signature[0] == 's' && signature[1] == 't' && signature[2] == 'x' && signature[3] == '-' && - signature[4] == 'b' && signature[5] == 't' && signature[6] == 'r' && signature[7] == 'e' && - signature[8] == 'e' && signature[9] == 0 && signature[10] == 0 && signature[11] == 0) - && (version == o.version) - && (key_type_size == o.key_type_size) - && (data_type_size == o.data_type_size) - && (leafslots == o.leafslots) - && (innerslots == o.innerslots) - && (allow_duplicates == o.allow_duplicates); - } - }; - -public: - - /// Dump the contents of the B+ tree out onto an ostream as a binary - /// image. The image contains memory pointers which will be fixed when the - /// image is restored. For this to work your key_type and data_type must be - /// integral types and contain no pointers or references. - void dump(std::ostream &os) const - { - struct dump_header header; - header.fill(); - header.itemcount = size(); - - os.write(reinterpret_cast(&header), sizeof(header)); - - if (m_root) { - dump_node(os, m_root); - } - } - - /// Restore a binary image of a dumped B+ tree from an istream. The B+ tree - /// pointers are fixed using the dump order. For dump and restore to work - /// your key_type and data_type must be integral types and contain no - /// pointers or references. Returns true if the restore was successful. - bool restore(std::istream &is) - { - struct dump_header fileheader; - is.read(reinterpret_cast(&fileheader), sizeof(fileheader)); - if (!is.good()) return false; - - struct dump_header myheader; - myheader.fill(); - myheader.itemcount = fileheader.itemcount; - - if (!myheader.same(fileheader)) - { - BTREE_PRINT("btree::restore: file header does not match instantiation signature."); - return false; - } - - clear(); - - if (fileheader.itemcount > 0) - { - m_root = restore_node(is); - if (m_root == NULL) return false; - - m_stats.itemcount = fileheader.itemcount; - } - -#ifdef BTREE_DEBUG - if (debug) print(std::cout); -#endif - if (selfverify) verify(); - - return true; - } - -private: - - /// Recursively descend down the tree and dump each node in a precise order - void dump_node(std::ostream &os, const node* n) const - { - BTREE_PRINT("dump_node " << n << std::endl); - - if (n->isleafnode()) - { - const leaf_node *leaf = static_cast(n); - - os.write(reinterpret_cast(leaf), sizeof(*leaf)); - } - else // !n->isleafnode() - { - const inner_node *inner = static_cast(n); - - os.write(reinterpret_cast(inner), sizeof(*inner)); - - for(unsigned short slot = 0; slot <= inner->slotuse; ++slot) - { - const node *subnode = inner->childid[slot]; - - dump_node(os, subnode); - } - } - } - - /// Read the dump image and construct a tree from the node order in the - /// serialization. - node* restore_node(std::istream &is) - { - union { - node top; - leaf_node leaf; - inner_node inner; - } nu; - - // first read only the top of the node - is.read(reinterpret_cast(&nu.top), sizeof(nu.top)); - if (!is.good()) return NULL; - - if (nu.top.isleafnode()) - { - // read remaining data of leaf node - is.read(reinterpret_cast(&nu.leaf) + sizeof(nu.top), sizeof(nu.leaf) - sizeof(nu.top)); - if (!is.good()) return NULL; - - leaf_node *newleaf = allocate_leaf(); - - // copy over all data, the leaf nodes contain only their double linked list pointers - *newleaf = nu.leaf; - - // reconstruct the linked list from the order in the file - if (m_headleaf == NULL) { - BTREE_ASSERT(newleaf->prevleaf == NULL); - m_headleaf = m_tailleaf = newleaf; - } - else { - newleaf->prevleaf = m_tailleaf; - m_tailleaf->nextleaf = newleaf; - m_tailleaf = newleaf; - } - - return newleaf; - } - else - { - // read remaining data of inner node - is.read(reinterpret_cast(&nu.inner) + sizeof(nu.top), sizeof(nu.inner) - sizeof(nu.top)); - if (!is.good()) return NULL; - - inner_node *newinner = allocate_inner(0); - - // copy over all data, the inner nodes contain only pointers to their children - *newinner = nu.inner; - - for(unsigned short slot = 0; slot <= newinner->slotuse; ++slot) - { - newinner->childid[slot] = restore_node(is); - } - - return newinner; - } - } -}; - -} // namespace stx - -#endif // _STX_BTREE_H_ diff --git a/tommyds/benchmark/lib/stx/btree_map b/tommyds/benchmark/lib/stx/btree_map deleted file mode 100644 index 86d6d67..0000000 --- a/tommyds/benchmark/lib/stx/btree_map +++ /dev/null @@ -1,39 +0,0 @@ -/** \file btree_map - * Forwarder header to btree_map.h - */ - -/* - * STX B+ Tree Template Classes v0.9 - * Copyright (C) 2008-2013 Timo Bingmann - * - * Boost Software License - Version 1.0 - August 17th, 2003 - * - * Permission is hereby granted, free of charge, to any person or organization - * obtaining a copy of the software and accompanying documentation covered by - * this license (the "Software") to use, reproduce, display, distribute, - * execute, and transmit the Software, and to prepare derivative works of the - * Software, and to permit third-parties to whom the Software is furnished to - * do so, all subject to the following: - * - * The copyright notices in the Software and this entire statement, including - * the above license grant, this restriction and the following disclaimer, must - * be included in all copies of the Software, in whole or in part, and all - * derivative works of the Software, unless such copies or derivative works are - * solely in the form of machine-executable object code generated by a source - * language processor. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT - * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE - * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#ifndef _STX_BTREE_MAP_ -#define _STX_BTREE_MAP_ - -#include - -#endif // _STX_BTREE_MAP_ diff --git a/tommyds/benchmark/lib/stx/btree_map.h b/tommyds/benchmark/lib/stx/btree_map.h deleted file mode 100644 index 4b9c0a6..0000000 --- a/tommyds/benchmark/lib/stx/btree_map.h +++ /dev/null @@ -1,619 +0,0 @@ -/** \file btree_map.h - * Contains the specialized B+ tree template class btree_map - */ - -/* - * STX B+ Tree Template Classes v0.9 - * Copyright (C) 2008-2013 Timo Bingmann - * - * Boost Software License - Version 1.0 - August 17th, 2003 - * - * Permission is hereby granted, free of charge, to any person or organization - * obtaining a copy of the software and accompanying documentation covered by - * this license (the "Software") to use, reproduce, display, distribute, - * execute, and transmit the Software, and to prepare derivative works of the - * Software, and to permit third-parties to whom the Software is furnished to - * do so, all subject to the following: - * - * The copyright notices in the Software and this entire statement, including - * the above license grant, this restriction and the following disclaimer, must - * be included in all copies of the Software, in whole or in part, and all - * derivative works of the Software, unless such copies or derivative works are - * solely in the form of machine-executable object code generated by a source - * language processor. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT - * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE - * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#ifndef _STX_BTREE_MAP_H_ -#define _STX_BTREE_MAP_H_ - -#include "btree.h" - -namespace stx { - -/** @brief Specialized B+ tree template class implementing STL's map container. - * - * Implements the STL map using a B+ tree. It can be used as a drop-in - * replacement for std::map. Not all asymptotic time requirements are met in - * theory. The class has a traits class defining B+ tree properties like slots - * and self-verification. Furthermore an allocator can be specified for tree - * nodes. - * - * Most noteworthy difference to the default red-black implementation of - * std::map is that the B+ tree does not hold key and data pair together in - * memory. Instead each B+ tree node has two arrays of keys and data - * values. This design directly generates many problems in implementing the - * iterator's operator's which return value_type composition pairs. - */ -template , - typename _Traits = btree_default_map_traits<_Key, _Data>, - typename _Alloc = std::allocator > > -class btree_map -{ -public: - // *** Template Parameter Types - - /// First template parameter: The key type of the btree. This is stored in - /// inner nodes and leaves - typedef _Key key_type; - - /// Second template parameter: The data type associated with each - /// key. Stored in the B+ tree's leaves - typedef _Data data_type; - - /// Third template parameter: Key comparison function object - typedef _Compare key_compare; - - /// Fourth template parameter: Traits object used to define more parameters - /// of the B+ tree - typedef _Traits traits; - - /// Fifth template parameter: STL allocator - typedef _Alloc allocator_type; - - // The macro BTREE_FRIENDS can be used by outside class to access the B+ - // tree internals. This was added for wxBTreeDemo to be able to draw the - // tree. - BTREE_FRIENDS - -public: - // *** Constructed Types - - /// Typedef of our own type - typedef btree_map self; - - /// Construct the STL-required value_type as a composition pair of key and - /// data types - typedef std::pair value_type; - - /// Implementation type of the btree_base - typedef stx::btree btree_impl; - - /// Function class comparing two value_type pairs. - typedef typename btree_impl::value_compare value_compare; - - /// Size type used to count keys - typedef typename btree_impl::size_type size_type; - - /// Small structure containing statistics about the tree - typedef typename btree_impl::tree_stats tree_stats; - -public: - // *** Static Constant Options and Values of the B+ Tree - - /// Base B+ tree parameter: The number of key/data slots in each leaf - static const unsigned short leafslotmax = btree_impl::leafslotmax; - - /// Base B+ tree parameter: The number of key slots in each inner node, - /// this can differ from slots in each leaf. - static const unsigned short innerslotmax = btree_impl::innerslotmax; - - /// Computed B+ tree parameter: The minimum number of key/data slots used - /// in a leaf. If fewer slots are used, the leaf will be merged or slots - /// shifted from it's siblings. - static const unsigned short minleafslots = btree_impl::minleafslots; - - /// Computed B+ tree parameter: The minimum number of key slots used - /// in an inner node. If fewer slots are used, the inner node will be - /// merged or slots shifted from it's siblings. - static const unsigned short mininnerslots = btree_impl::mininnerslots; - - /// Debug parameter: Enables expensive and thorough checking of the B+ tree - /// invariants after each insert/erase operation. - static const bool selfverify = btree_impl::selfverify; - - /// Debug parameter: Prints out lots of debug information about how the - /// algorithms change the tree. Requires the header file to be compiled - /// with BTREE_DEBUG and the key type must be std::ostream printable. - static const bool debug = btree_impl::debug; - - /// Operational parameter: Allow duplicate keys in the btree. - static const bool allow_duplicates = btree_impl::allow_duplicates; - -public: - // *** Iterators and Reverse Iterators - - /// STL-like iterator object for B+ tree items. The iterator points to a - /// specific slot number in a leaf. - typedef typename btree_impl::iterator iterator; - - /// STL-like iterator object for B+ tree items. The iterator points to a - /// specific slot number in a leaf. - typedef typename btree_impl::const_iterator const_iterator; - - /// create mutable reverse iterator by using STL magic - typedef typename btree_impl::reverse_iterator reverse_iterator; - - /// create constant reverse iterator by using STL magic - typedef typename btree_impl::const_reverse_iterator const_reverse_iterator; - -private: - // *** Tree Implementation Object - - /// The contained implementation object - btree_impl tree; - -public: - // *** Constructors and Destructor - - /// Default constructor initializing an empty B+ tree with the standard key - /// comparison function - explicit inline btree_map(const allocator_type &alloc = allocator_type()) - : tree(alloc) - { - } - - /// Constructor initializing an empty B+ tree with a special key - /// comparison object - explicit inline btree_map(const key_compare &kcf, - const allocator_type &alloc = allocator_type()) - : tree(kcf, alloc) - { - } - - /// Constructor initializing a B+ tree with the range [first,last) - template - inline btree_map(InputIterator first, InputIterator last, - const allocator_type &alloc = allocator_type()) - : tree(first, last, alloc) - { - } - - /// Constructor initializing a B+ tree with the range [first,last) and a - /// special key comparison object - template - inline btree_map(InputIterator first, InputIterator last, const key_compare &kcf, - const allocator_type &alloc = allocator_type()) - : tree(first, last, kcf, alloc) - { - } - - /// Frees up all used B+ tree memory pages - inline ~btree_map() - { - } - - /// Fast swapping of two identical B+ tree objects. - void swap(self& from) - { - std::swap(tree, from.tree); - } - -public: - // *** Key and Value Comparison Function Objects - - /// Constant access to the key comparison object sorting the B+ tree - inline key_compare key_comp() const - { - return tree.key_comp(); - } - - /// Constant access to a constructed value_type comparison object. required - /// by the STL - inline value_compare value_comp() const - { - return tree.value_comp(); - } - -public: - // *** Allocators - - /// Return the base node allocator provided during construction. - allocator_type get_allocator() const - { - return tree.get_allocator(); - } - -public: - // *** Fast Destruction of the B+ Tree - - /// Frees all key/data pairs and all nodes of the tree - void clear() - { - tree.clear(); - } - -public: - // *** STL Iterator Construction Functions - - /// Constructs a read/data-write iterator that points to the first slot in - /// the first leaf of the B+ tree. - inline iterator begin() - { - return tree.begin(); - } - - /// Constructs a read/data-write iterator that points to the first invalid - /// slot in the last leaf of the B+ tree. - inline iterator end() - { - return tree.end(); - } - - /// Constructs a read-only constant iterator that points to the first slot - /// in the first leaf of the B+ tree. - inline const_iterator begin() const - { - return tree.begin(); - } - - /// Constructs a read-only constant iterator that points to the first - /// invalid slot in the last leaf of the B+ tree. - inline const_iterator end() const - { - return tree.end(); - } - - /// Constructs a read/data-write reverse iterator that points to the first - /// invalid slot in the last leaf of the B+ tree. Uses STL magic. - inline reverse_iterator rbegin() - { - return tree.rbegin(); - } - - /// Constructs a read/data-write reverse iterator that points to the first - /// slot in the first leaf of the B+ tree. Uses STL magic. - inline reverse_iterator rend() - { - return tree.rend(); - } - - /// Constructs a read-only reverse iterator that points to the first - /// invalid slot in the last leaf of the B+ tree. Uses STL magic. - inline const_reverse_iterator rbegin() const - { - return tree.rbegin(); - } - - /// Constructs a read-only reverse iterator that points to the first slot - /// in the first leaf of the B+ tree. Uses STL magic. - inline const_reverse_iterator rend() const - { - return tree.rend(); - } - -public: - // *** Access Functions to the Item Count - - /// Return the number of key/data pairs in the B+ tree - inline size_type size() const - { - return tree.size(); - } - - /// Returns true if there is at least one key/data pair in the B+ tree - inline bool empty() const - { - return tree.empty(); - } - - /// Returns the largest possible size of the B+ Tree. This is just a - /// function required by the STL standard, the B+ Tree can hold more items. - inline size_type max_size() const - { - return tree.max_size(); - } - - /// Return a const reference to the current statistics. - inline const tree_stats& get_stats() const - { - return tree.get_stats(); - } - -public: - // *** Standard Access Functions Querying the Tree by Descending to a Leaf - - /// Non-STL function checking whether a key is in the B+ tree. The same as - /// (find(k) != end()) or (count() != 0). - bool exists(const key_type &key) const - { - return tree.exists(key); - } - - /// Tries to locate a key in the B+ tree and returns an iterator to the - /// key/data slot if found. If unsuccessful it returns end(). - iterator find(const key_type &key) - { - return tree.find(key); - } - - /// Tries to locate a key in the B+ tree and returns an constant iterator - /// to the key/data slot if found. If unsuccessful it returns end(). - const_iterator find(const key_type &key) const - { - return tree.find(key); - } - - /// Tries to locate a key in the B+ tree and returns the number of - /// identical key entries found. Since this is a unique map, count() - /// returns either 0 or 1. - size_type count(const key_type &key) const - { - return tree.count(key); - } - - /// Searches the B+ tree and returns an iterator to the first pair - /// equal to or greater than key, or end() if all keys are smaller. - iterator lower_bound(const key_type& key) - { - return tree.lower_bound(key); - } - - /// Searches the B+ tree and returns a constant iterator to the - /// first pair equal to or greater than key, or end() if all keys - /// are smaller. - const_iterator lower_bound(const key_type& key) const - { - return tree.lower_bound(key); - } - - /// Searches the B+ tree and returns an iterator to the first pair - /// greater than key, or end() if all keys are smaller or equal. - iterator upper_bound(const key_type& key) - { - return tree.upper_bound(key); - } - - /// Searches the B+ tree and returns a constant iterator to the - /// first pair greater than key, or end() if all keys are smaller - /// or equal. - const_iterator upper_bound(const key_type& key) const - { - return tree.upper_bound(key); - } - - /// Searches the B+ tree and returns both lower_bound() and upper_bound(). - inline std::pair equal_range(const key_type& key) - { - return tree.equal_range(key); - } - - /// Searches the B+ tree and returns both lower_bound() and upper_bound(). - inline std::pair equal_range(const key_type& key) const - { - return tree.equal_range(key); - } - -public: - // *** B+ Tree Object Comparison Functions - - /// Equality relation of B+ trees of the same type. B+ trees of the same - /// size and equal elements (both key and data) are considered - /// equal. - inline bool operator==(const self &other) const - { - return (tree == other.tree); - } - - /// Inequality relation. Based on operator==. - inline bool operator!=(const self &other) const - { - return (tree != other.tree); - } - - /// Total ordering relation of B+ trees of the same type. It uses - /// std::lexicographical_compare() for the actual comparison of elements. - inline bool operator<(const self &other) const - { - return (tree < other.tree); - } - - /// Greater relation. Based on operator<. - inline bool operator>(const self &other) const - { - return (tree > other.tree); - } - - /// Less-equal relation. Based on operator<. - inline bool operator<=(const self &other) const - { - return (tree <= other.tree); - } - - /// Greater-equal relation. Based on operator<. - inline bool operator>=(const self &other) const - { - return (tree >= other.tree); - } - -public: - /// *** Fast Copy: Assign Operator and Copy Constructors - - /// Assignment operator. All the key/data pairs are copied - inline self& operator= (const self &other) - { - if (this != &other) - { - tree = other.tree; - } - return *this; - } - - /// Copy constructor. The newly initialized B+ tree object will contain a - /// copy of all key/data pairs. - inline btree_map(const self &other) - : tree(other.tree) - { - } - -public: - // *** Public Insertion Functions - - /// Attempt to insert a key/data pair into the B+ tree. Fails if the pair - /// is already present. - inline std::pair insert(const value_type& x) - { - return tree.insert2(x.first, x.second); - } - - /// Attempt to insert a key/data pair into the B+ tree. Beware that if - /// key_type == data_type, then the template iterator insert() is called - /// instead. Fails if the inserted pair is already present. - inline std::pair insert(const key_type& key, const data_type& data) - { - return tree.insert2(key, data); - } - - /// Attempt to insert a key/data pair into the B+ tree. This function is the - /// same as the other insert, however if key_type == data_type then the - /// non-template function cannot be called. Fails if the inserted pair is - /// already present. - inline std::pair insert2(const key_type& key, const data_type& data) - { - return tree.insert2(key, data); - } - - /// Attempt to insert a key/data pair into the B+ tree. The iterator hint - /// is currently ignored by the B+ tree insertion routine. - inline iterator insert(iterator hint, const value_type &x) - { - return tree.insert2(hint, x.first, x.second); - } - - /// Attempt to insert a key/data pair into the B+ tree. The iterator hint is - /// currently ignored by the B+ tree insertion routine. - inline iterator insert2(iterator hint, const key_type& key, const data_type& data) - { - return tree.insert2(hint, key, data); - } - - /// Returns a reference to the object that is associated with a particular - /// key. If the map does not already contain such an object, operator[] - /// inserts the default object data_type(). - inline data_type& operator[](const key_type& key) - { - iterator i = insert( value_type(key, data_type()) ).first; - return i.data(); - } - - /// Attempt to insert the range [first,last) of value_type pairs into the B+ - /// tree. Each key/data pair is inserted individually. - template - inline void insert(InputIterator first, InputIterator last) - { - return tree.insert(first, last); - } - - /// Bulk load a sorted range [first,last). Loads items into leaves and - /// constructs a B-tree above them. The tree must be empty when calling - /// this function. - template - inline void bulk_load(Iterator first, Iterator last) - { - return tree.bulk_load(first, last); - } - -public: - // *** Public Erase Functions - - /// Erases the key/data pairs associated with the given key. For this - /// unique-associative map there is no difference to erase(). - bool erase_one(const key_type &key) - { - return tree.erase_one(key); - } - - /// Erases all the key/data pairs associated with the given key. This is - /// implemented using erase_one(). - size_type erase(const key_type &key) - { - return tree.erase(key); - } - - /// Erase the key/data pair referenced by the iterator. - void erase(iterator iter) - { - return tree.erase(iter); - } - -#ifdef BTREE_TODO - /// Erase all key/data pairs in the range [first,last). This function is - /// currently not implemented by the B+ Tree. - void erase(iterator /* first */, iterator /* last */) - { - abort(); - } -#endif - -#ifdef BTREE_DEBUG -public: - // *** Debug Printing - - /// Print out the B+ tree structure with keys onto the given ostream. This function - /// requires that the header is compiled with BTREE_DEBUG and that key_type - /// is printable via std::ostream. - void print(std::ostream &os) const - { - tree.print(os); - } - - /// Print out only the leaves via the double linked list. - void print_leaves(std::ostream &os) const - { - tree.print_leaves(os); - } -#endif - -public: - // *** Verification of B+ Tree Invariants - - /// Run a thorough verification of all B+ tree invariants. The program - /// aborts via BTREE_ASSERT() if something is wrong. - void verify() const - { - tree.verify(); - } - -public: - - /// Dump the contents of the B+ tree out onto an ostream as a binary - /// image. The image contains memory pointers which will be fixed when the - /// image is restored. For this to work your key_type and data_type must be - /// integral types and contain no pointers or references. - void dump(std::ostream &os) const - { - tree.dump(os); - } - - /// Restore a binary image of a dumped B+ tree from an istream. The B+ tree - /// pointers are fixed using the dump order. For dump and restore to work - /// your key_type and data_type must be integral types and contain no - /// pointers or references. Returns true if the restore was successful. - bool restore(std::istream &is) - { - return tree.restore(is); - } -}; - -} // namespace stx - -#endif // _STX_BTREE_MAP_H_ diff --git a/tommyds/benchmark/lib/stx/btree_multimap b/tommyds/benchmark/lib/stx/btree_multimap deleted file mode 100644 index 4021e94..0000000 --- a/tommyds/benchmark/lib/stx/btree_multimap +++ /dev/null @@ -1,39 +0,0 @@ -/** \file btree_multimap - * Forwarder header to btree_multimap.h - */ - -/* - * STX B+ Tree Template Classes v0.9 - * Copyright (C) 2008-2013 Timo Bingmann - * - * Boost Software License - Version 1.0 - August 17th, 2003 - * - * Permission is hereby granted, free of charge, to any person or organization - * obtaining a copy of the software and accompanying documentation covered by - * this license (the "Software") to use, reproduce, display, distribute, - * execute, and transmit the Software, and to prepare derivative works of the - * Software, and to permit third-parties to whom the Software is furnished to - * do so, all subject to the following: - * - * The copyright notices in the Software and this entire statement, including - * the above license grant, this restriction and the following disclaimer, must - * be included in all copies of the Software, in whole or in part, and all - * derivative works of the Software, unless such copies or derivative works are - * solely in the form of machine-executable object code generated by a source - * language processor. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT - * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE - * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#ifndef _STX_BTREE_MULTIMAP_ -#define _STX_BTREE_MULTIMAP_ - -#include - -#endif // _STX_BTREE_MULTIMAP_ diff --git a/tommyds/benchmark/lib/stx/btree_multimap.h b/tommyds/benchmark/lib/stx/btree_multimap.h deleted file mode 100644 index ed9403b..0000000 --- a/tommyds/benchmark/lib/stx/btree_multimap.h +++ /dev/null @@ -1,610 +0,0 @@ -/** \file btree_multimap.h - * Contains the specialized B+ tree template class btree_multimap - */ - -/* - * STX B+ Tree Template Classes v0.9 - * Copyright (C) 2008-2013 Timo Bingmann - * - * Boost Software License - Version 1.0 - August 17th, 2003 - * - * Permission is hereby granted, free of charge, to any person or organization - * obtaining a copy of the software and accompanying documentation covered by - * this license (the "Software") to use, reproduce, display, distribute, - * execute, and transmit the Software, and to prepare derivative works of the - * Software, and to permit third-parties to whom the Software is furnished to - * do so, all subject to the following: - * - * The copyright notices in the Software and this entire statement, including - * the above license grant, this restriction and the following disclaimer, must - * be included in all copies of the Software, in whole or in part, and all - * derivative works of the Software, unless such copies or derivative works are - * solely in the form of machine-executable object code generated by a source - * language processor. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT - * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE - * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#ifndef _STX_BTREE_MULTIMAP_H_ -#define _STX_BTREE_MULTIMAP_H_ - -#include - -namespace stx { - -/** @brief Specialized B+ tree template class implementing STL's multimap - * container. - * - * Implements the STL multimap using a B+ tree. It can be used as a drop-in - * replacement for std::multimap. Not all asymptotic time requirements are met - * in theory. The class has a traits class defining B+ tree properties like - * slots and self-verification. Furthermore an allocator can be specified for - * tree nodes. - * - * Most noteworthy difference to the default red-black implementation of - * std::multimap is that the B+ tree does not hold key and data pair together - * in memory. Instead each B+ tree node has two arrays of keys and data - * values. This design directly generates many problems in implementing the - * iterator's operator's which return value_type composition pairs. - */ -template , - typename _Traits = btree_default_map_traits<_Key, _Data>, - typename _Alloc = std::allocator > > -class btree_multimap -{ -public: - // *** Template Parameter Types - - /// First template parameter: The key type of the btree. This is stored in - /// inner nodes and leaves - typedef _Key key_type; - - /// Second template parameter: The data type associated with each - /// key. Stored in the B+ tree's leaves - typedef _Data data_type; - - /// Third template parameter: Key comparison function object - typedef _Compare key_compare; - - /// Fourth template parameter: Traits object used to define more parameters - /// of the B+ tree - typedef _Traits traits; - - /// Fifth template parameter: STL allocator - typedef _Alloc allocator_type; - - // The macro BTREE_FRIENDS can be used by outside class to access the B+ - // tree internals. This was added for wxBTreeDemo to be able to draw the - // tree. - BTREE_FRIENDS - -public: - // *** Constructed Types - - /// Typedef of our own type - typedef btree_multimap self; - - /// Construct the STL-required value_type as a composition pair of key and - /// data types - typedef std::pair value_type; - - /// Implementation type of the btree_base - typedef stx::btree btree_impl; - - /// Function class comparing two value_type pairs. - typedef typename btree_impl::value_compare value_compare; - - /// Size type used to count keys - typedef typename btree_impl::size_type size_type; - - /// Small structure containing statistics about the tree - typedef typename btree_impl::tree_stats tree_stats; - -public: - // *** Static Constant Options and Values of the B+ Tree - - /// Base B+ tree parameter: The number of key/data slots in each leaf - static const unsigned short leafslotmax = btree_impl::leafslotmax; - - /// Base B+ tree parameter: The number of key slots in each inner node, - /// this can differ from slots in each leaf. - static const unsigned short innerslotmax = btree_impl::innerslotmax; - - /// Computed B+ tree parameter: The minimum number of key/data slots used - /// in a leaf. If fewer slots are used, the leaf will be merged or slots - /// shifted from it's siblings. - static const unsigned short minleafslots = btree_impl::minleafslots; - - /// Computed B+ tree parameter: The minimum number of key slots used - /// in an inner node. If fewer slots are used, the inner node will be - /// merged or slots shifted from it's siblings. - static const unsigned short mininnerslots = btree_impl::mininnerslots; - - /// Debug parameter: Enables expensive and thorough checking of the B+ tree - /// invariants after each insert/erase operation. - static const bool selfverify = btree_impl::selfverify; - - /// Debug parameter: Prints out lots of debug information about how the - /// algorithms change the tree. Requires the header file to be compiled - /// with BTREE_DEBUG and the key type must be std::ostream printable. - static const bool debug = btree_impl::debug; - - /// Operational parameter: Allow duplicate keys in the btree. - static const bool allow_duplicates = btree_impl::allow_duplicates; - -public: - // *** Iterators and Reverse Iterators - - /// STL-like iterator object for B+ tree items. The iterator points to a - /// specific slot number in a leaf. - typedef typename btree_impl::iterator iterator; - - /// STL-like iterator object for B+ tree items. The iterator points to a - /// specific slot number in a leaf. - typedef typename btree_impl::const_iterator const_iterator; - - /// create mutable reverse iterator by using STL magic - typedef typename btree_impl::reverse_iterator reverse_iterator; - - /// create constant reverse iterator by using STL magic - typedef typename btree_impl::const_reverse_iterator const_reverse_iterator; - -private: - // *** Tree Implementation Object - - /// The contained implementation object - btree_impl tree; - -public: - // *** Constructors and Destructor - - /// Default constructor initializing an empty B+ tree with the standard key - /// comparison function - explicit inline btree_multimap(const allocator_type &alloc = allocator_type()) - : tree(alloc) - { - } - - /// Constructor initializing an empty B+ tree with a special key - /// comparison object - explicit inline btree_multimap(const key_compare &kcf, - const allocator_type &alloc = allocator_type()) - : tree(kcf, alloc) - { - } - - /// Constructor initializing a B+ tree with the range [first,last) - template - inline btree_multimap(InputIterator first, InputIterator last, - const allocator_type &alloc = allocator_type()) - : tree(first, last, alloc) - { - } - - /// Constructor initializing a B+ tree with the range [first,last) and a - /// special key comparison object - template - inline btree_multimap(InputIterator first, InputIterator last, const key_compare &kcf, - const allocator_type &alloc = allocator_type()) - : tree(first, last, kcf, alloc) - { - } - - /// Frees up all used B+ tree memory pages - inline ~btree_multimap() - { - } - - /// Fast swapping of two identical B+ tree objects. - void swap(self& from) - { - std::swap(tree, from.tree); - } - -public: - // *** Key and Value Comparison Function Objects - - /// Constant access to the key comparison object sorting the B+ tree - inline key_compare key_comp() const - { - return tree.key_comp(); - } - - /// Constant access to a constructed value_type comparison object. required - /// by the STL - inline value_compare value_comp() const - { - return tree.value_comp(); - } - -public: - // *** Allocators - - /// Return the base node allocator provided during construction. - allocator_type get_allocator() const - { - return tree.get_allocator(); - } - -public: - // *** Fast Destruction of the B+ Tree - - /// Frees all key/data pairs and all nodes of the tree - void clear() - { - tree.clear(); - } - -public: - // *** STL Iterator Construction Functions - - /// Constructs a read/data-write iterator that points to the first slot in - /// the first leaf of the B+ tree. - inline iterator begin() - { - return tree.begin(); - } - - /// Constructs a read/data-write iterator that points to the first invalid - /// slot in the last leaf of the B+ tree. - inline iterator end() - { - return tree.end(); - } - - /// Constructs a read-only constant iterator that points to the first slot - /// in the first leaf of the B+ tree. - inline const_iterator begin() const - { - return tree.begin(); - } - - /// Constructs a read-only constant iterator that points to the first - /// invalid slot in the last leaf of the B+ tree. - inline const_iterator end() const - { - return tree.end(); - } - - /// Constructs a read/data-write reverse iterator that points to the first - /// invalid slot in the last leaf of the B+ tree. Uses STL magic. - inline reverse_iterator rbegin() - { - return tree.rbegin(); - } - - /// Constructs a read/data-write reverse iterator that points to the first - /// slot in the first leaf of the B+ tree. Uses STL magic. - inline reverse_iterator rend() - { - return tree.rend(); - } - - /// Constructs a read-only reverse iterator that points to the first - /// invalid slot in the last leaf of the B+ tree. Uses STL magic. - inline const_reverse_iterator rbegin() const - { - return tree.rbegin(); - } - - /// Constructs a read-only reverse iterator that points to the first slot - /// in the first leaf of the B+ tree. Uses STL magic. - inline const_reverse_iterator rend() const - { - return tree.rend(); - } - -public: - // *** Access Functions to the Item Count - - /// Return the number of key/data pairs in the B+ tree - inline size_type size() const - { - return tree.size(); - } - - /// Returns true if there is at least one key/data pair in the B+ tree - inline bool empty() const - { - return tree.empty(); - } - - /// Returns the largest possible size of the B+ Tree. This is just a - /// function required by the STL standard, the B+ Tree can hold more items. - inline size_type max_size() const - { - return tree.max_size(); - } - - /// Return a const reference to the current statistics. - inline const tree_stats& get_stats() const - { - return tree.get_stats(); - } - -public: - // *** Standard Access Functions Querying the Tree by Descending to a Leaf - - /// Non-STL function checking whether a key is in the B+ tree. The same as - /// (find(k) != end()) or (count() != 0). - bool exists(const key_type &key) const - { - return tree.exists(key); - } - - /// Tries to locate a key in the B+ tree and returns an iterator to the - /// key/data slot if found. If unsuccessful it returns end(). - iterator find(const key_type &key) - { - return tree.find(key); - } - - /// Tries to locate a key in the B+ tree and returns an constant iterator - /// to the key/data slot if found. If unsuccessful it returns end(). - const_iterator find(const key_type &key) const - { - return tree.find(key); - } - - /// Tries to locate a key in the B+ tree and returns the number of - /// identical key entries found. - size_type count(const key_type &key) const - { - return tree.count(key); - } - - /// Searches the B+ tree and returns an iterator to the first pair - /// equal to or greater than key, or end() if all keys are smaller. - iterator lower_bound(const key_type& key) - { - return tree.lower_bound(key); - } - - /// Searches the B+ tree and returns a constant iterator to the - /// first pair equal to or greater than key, or end() if all keys - /// are smaller. - const_iterator lower_bound(const key_type& key) const - { - return tree.lower_bound(key); - } - - /// Searches the B+ tree and returns an iterator to the first pair - /// greater than key, or end() if all keys are smaller or equal. - iterator upper_bound(const key_type& key) - { - return tree.upper_bound(key); - } - - /// Searches the B+ tree and returns a constant iterator to the - /// first pair greater than key, or end() if all keys are smaller - /// or equal. - const_iterator upper_bound(const key_type& key) const - { - return tree.upper_bound(key); - } - - /// Searches the B+ tree and returns both lower_bound() and upper_bound(). - inline std::pair equal_range(const key_type& key) - { - return tree.equal_range(key); - } - - /// Searches the B+ tree and returns both lower_bound() and upper_bound(). - inline std::pair equal_range(const key_type& key) const - { - return tree.equal_range(key); - } - -public: - // *** B+ Tree Object Comparison Functions - - /// Equality relation of B+ trees of the same type. B+ trees of the same - /// size and equal elements (both key and data) are considered - /// equal. Beware of the random ordering of duplicate keys. - inline bool operator==(const self &other) const - { - return (tree == other.tree); - } - - /// Inequality relation. Based on operator==. - inline bool operator!=(const self &other) const - { - return (tree != other.tree); - } - - /// Total ordering relation of B+ trees of the same type. It uses - /// std::lexicographical_compare() for the actual comparison of elements. - inline bool operator<(const self &other) const - { - return (tree < other.tree); - } - - /// Greater relation. Based on operator<. - inline bool operator>(const self &other) const - { - return (tree > other.tree); - } - - /// Less-equal relation. Based on operator<. - inline bool operator<=(const self &other) const - { - return (tree <= other.tree); - } - - /// Greater-equal relation. Based on operator<. - inline bool operator>=(const self &other) const - { - return (tree >= other.tree); - } - -public: - /// *** Fast Copy: Assign Operator and Copy Constructors - - /// Assignment operator. All the key/data pairs are copied - inline self& operator= (const self &other) - { - if (this != &other) - { - tree = other.tree; - } - return *this; - } - - /// Copy constructor. The newly initialized B+ tree object will contain a - /// copy or all key/data pairs. - inline btree_multimap(const self &other) - : tree(other.tree) - { - } - -public: - // *** Public Insertion Functions - - /// Attempt to insert a key/data pair into the B+ tree. As this tree allows - /// duplicates insertion never fails. - inline iterator insert(const value_type& x) - { - return tree.insert2(x.first, x.second).first; - } - - /// Attempt to insert a key/data pair into the B+ tree. Beware that if - /// key_type == data_type, then the template iterator insert() is called - /// instead. As this tree allows duplicates insertion never fails. - inline iterator insert(const key_type& key, const data_type& data) - { - return tree.insert2(key, data).first; - } - - /// Attempt to insert a key/data pair into the B+ tree. This function is the - /// same as the other insert, however if key_type == data_type then the - /// non-template function cannot be called. As this tree allows duplicates - /// insertion never fails. - inline iterator insert2(const key_type& key, const data_type& data) - { - return tree.insert2(key, data).first; - } - - /// Attempt to insert a key/data pair into the B+ tree. The iterator hint - /// is currently ignored by the B+ tree insertion routine. - inline iterator insert(iterator hint, const value_type &x) - { - return tree.insert2(hint, x.first, x.second); - } - - /// Attempt to insert a key/data pair into the B+ tree. The iterator hint is - /// currently ignored by the B+ tree insertion routine. - inline iterator insert2(iterator hint, const key_type& key, const data_type& data) - { - return tree.insert2(hint, key, data); - } - - /// Attempt to insert the range [first,last) of value_type pairs into the B+ - /// tree. Each key/data pair is inserted individually. - template - inline void insert(InputIterator first, InputIterator last) - { - return tree.insert(first, last); - } - - /// Bulk load a sorted range [first,last). Loads items into leaves and - /// constructs a B-tree above them. The tree must be empty when calling - /// this function. - template - inline void bulk_load(Iterator first, Iterator last) - { - return tree.bulk_load(first, last); - } - -public: - // *** Public Erase Functions - - /// Erases one (the first) of the key/data pairs associated with the given - /// key. - bool erase_one(const key_type &key) - { - return tree.erase_one(key); - } - - /// Erases all the key/data pairs associated with the given key. This is - /// implemented using erase_one() and thus not very efficient. - size_type erase(const key_type &key) - { - return tree.erase(key); - } - - /// Erase the key/data pair referenced by the iterator. - void erase(iterator iter) - { - return tree.erase(iter); - } - -#ifdef BTREE_TODO - /// Erase all key/data pairs in the range [first,last). This function is - /// currently not implemented by the B+ Tree. - void erase(iterator /* first */, iterator /* last */) - { - abort(); - } -#endif - -#ifdef BTREE_DEBUG -public: - // *** Debug Printing - - /// Print out the B+ tree structure with keys onto the given ostream. This function - /// requires that the header is compiled with BTREE_DEBUG and that key_type - /// is printable via std::ostream. - void print(std::ostream &os) const - { - tree.print(os); - } - - /// Print out only the leaves via the double linked list. - void print_leaves(std::ostream &os) const - { - tree.print_leaves(os); - } -#endif - -public: - // *** Verification of B+ Tree Invariants - - /// Run a thorough verification of all B+ tree invariants. The program - /// aborts via BTREE_ASSERT() if something is wrong. - void verify() const - { - tree.verify(); - } - -public: - - /// Dump the contents of the B+ tree out onto an ostream as a binary - /// image. The image contains memory pointers which will be fixed when the - /// image is restored. For this to work your key_type and data_type must be - /// integral types and contain no pointers or references. - void dump(std::ostream &os) const - { - tree.dump(os); - } - - /// Restore a binary image of a dumped B+ tree from an istream. The B+ tree - /// pointers are fixed using the dump order. For dump and restore to work - /// your key_type and data_type must be integral types and contain no - /// pointers or references. Returns true if the restore was successful. - bool restore(std::istream &is) - { - return tree.restore(is); - } -}; - -} // namespace stx - -#endif // _STX_BTREE_MULTIMAP_H_ diff --git a/tommyds/benchmark/lib/stx/btree_multiset b/tommyds/benchmark/lib/stx/btree_multiset deleted file mode 100644 index a448782..0000000 --- a/tommyds/benchmark/lib/stx/btree_multiset +++ /dev/null @@ -1,39 +0,0 @@ -/** \file btree_multiset - * Forwarder header to btree_multiset.h - */ - -/* - * STX B+ Tree Template Classes v0.9 - * Copyright (C) 2008-2013 Timo Bingmann - * - * Boost Software License - Version 1.0 - August 17th, 2003 - * - * Permission is hereby granted, free of charge, to any person or organization - * obtaining a copy of the software and accompanying documentation covered by - * this license (the "Software") to use, reproduce, display, distribute, - * execute, and transmit the Software, and to prepare derivative works of the - * Software, and to permit third-parties to whom the Software is furnished to - * do so, all subject to the following: - * - * The copyright notices in the Software and this entire statement, including - * the above license grant, this restriction and the following disclaimer, must - * be included in all copies of the Software, in whole or in part, and all - * derivative works of the Software, unless such copies or derivative works are - * solely in the form of machine-executable object code generated by a source - * language processor. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT - * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE - * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#ifndef _STX_BTREE_MULTISET_ -#define _STX_BTREE_MULTISET_ - -#include - -#endif // _STX_BTREE_MULTISET_ diff --git a/tommyds/benchmark/lib/stx/btree_multiset.h b/tommyds/benchmark/lib/stx/btree_multiset.h deleted file mode 100644 index 353734d..0000000 --- a/tommyds/benchmark/lib/stx/btree_multiset.h +++ /dev/null @@ -1,601 +0,0 @@ -/** \file btree_multiset.h - * Contains the specialized B+ tree template class btree_multiset - */ - -/* - * STX B+ Tree Template Classes v0.9 - * Copyright (C) 2008-2013 Timo Bingmann - * - * Boost Software License - Version 1.0 - August 17th, 2003 - * - * Permission is hereby granted, free of charge, to any person or organization - * obtaining a copy of the software and accompanying documentation covered by - * this license (the "Software") to use, reproduce, display, distribute, - * execute, and transmit the Software, and to prepare derivative works of the - * Software, and to permit third-parties to whom the Software is furnished to - * do so, all subject to the following: - * - * The copyright notices in the Software and this entire statement, including - * the above license grant, this restriction and the following disclaimer, must - * be included in all copies of the Software, in whole or in part, and all - * derivative works of the Software, unless such copies or derivative works are - * solely in the form of machine-executable object code generated by a source - * language processor. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT - * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE - * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#ifndef _STX_BTREE_MULTISET_H_ -#define _STX_BTREE_MULTISET_H_ - -#include - -namespace stx { - -/** @brief Specialized B+ tree template class implementing STL's multiset - * container. - * - * Implements the STL multiset using a B+ tree. It can be used as a drop-in - * replacement for std::multiset. Not all asymptotic time requirements are met - * in theory. The class has a traits class defining B+ tree properties like - * slots and self-verification. Furthermore an allocator can be specified for - * tree nodes. - * - * It is somewhat inefficient to implement a multiset using a B+ tree, a plain - * B tree would hold fewer copies of the keys. - * - * The set class is derived from the base implementation class btree by - * specifying an empty struct as data_type. All function are adapted to provide - * the inner class with placeholder objects. Most tricky to get right were the - * return type's of iterators which as value_type should be the same as - * key_type, and not a pair of key and dummy-struct. This is taken case of - * using some template magic in the btree class. - */ -template , - typename _Traits = btree_default_set_traits<_Key>, - typename _Alloc = std::allocator<_Key> > -class btree_multiset -{ -public: - // *** Template Parameter Types - - /// First template parameter: The key type of the btree. This is stored in - /// inner nodes and leaves - typedef _Key key_type; - - /// Second template parameter: Key comparison function object - typedef _Compare key_compare; - - /// Third template parameter: Traits object used to define more parameters - /// of the B+ tree - typedef _Traits traits; - - /// Fourth template parameter: STL allocator - typedef _Alloc allocator_type; - - // The macro BTREE_FRIENDS can be used by outside class to access the B+ - // tree internals. This was added for wxBTreeDemo to be able to draw the - // tree. - BTREE_FRIENDS - -private: - // *** The Data_Type - - /// The empty struct used as a placeholder for the data_type. - struct empty_struct - { - }; - -public: - // *** Constructed Types - - /// The empty data_type - typedef struct empty_struct data_type; - - /// Construct the set value_type: the key_type. - typedef key_type value_type; - - /// Typedef of our own type - typedef btree_multiset self; - - /// Implementation type of the btree_base - typedef stx::btree btree_impl; - - /// Function class comparing two value_type keys. - typedef typename btree_impl::value_compare value_compare; - - /// Size type used to count keys - typedef typename btree_impl::size_type size_type; - - /// Small structure containing statistics about the tree - typedef typename btree_impl::tree_stats tree_stats; - -public: - // *** Static Constant Options and Values of the B+ Tree - - /// Base B+ tree parameter: The number of key/data slots in each leaf - static const unsigned short leafslotmax = btree_impl::leafslotmax; - - /// Base B+ tree parameter: The number of key slots in each inner node, - /// this can differ from slots in each leaf. - static const unsigned short innerslotmax = btree_impl::innerslotmax; - - /// Computed B+ tree parameter: The minimum number of key slots used in a - /// leaf. If fewer slots are used, the leaf will be merged or slots shifted - /// from it's siblings. - static const unsigned short minleafslots = btree_impl::minleafslots; - - /// Computed B+ tree parameter: The minimum number of key slots used - /// in an inner node. If fewer slots are used, the inner node will be - /// merged or slots shifted from it's siblings. - static const unsigned short mininnerslots = btree_impl::mininnerslots; - - /// Debug parameter: Enables expensive and thorough checking of the B+ tree - /// invariants after each insert/erase operation. - static const bool selfverify = btree_impl::selfverify; - - /// Debug parameter: Prints out lots of debug information about how the - /// algorithms change the tree. Requires the header file to be compiled - /// with BTREE_DEBUG and the key type must be std::ostream printable. - static const bool debug = btree_impl::debug; - - /// Operational parameter: Allow duplicate keys in the btree. - static const bool allow_duplicates = btree_impl::allow_duplicates; - -public: - // *** Iterators and Reverse Iterators - - /// STL-like iterator object for B+ tree items. The iterator points to a - /// specific slot number in a leaf. - typedef typename btree_impl::iterator iterator; - - /// STL-like iterator object for B+ tree items. The iterator points to a - /// specific slot number in a leaf. - typedef typename btree_impl::const_iterator const_iterator; - - /// create mutable reverse iterator by using STL magic - typedef typename btree_impl::reverse_iterator reverse_iterator; - - /// create constant reverse iterator by using STL magic - typedef typename btree_impl::const_reverse_iterator const_reverse_iterator; - -private: - // *** Tree Implementation Object - - /// The contained implementation object - btree_impl tree; - -public: - // *** Constructors and Destructor - - /// Default constructor initializing an empty B+ tree with the standard key - /// comparison function - explicit inline btree_multiset(const allocator_type &alloc = allocator_type()) - : tree(alloc) - { - } - - /// Constructor initializing an empty B+ tree with a special key - /// comparison object - explicit inline btree_multiset(const key_compare &kcf, - const allocator_type &alloc = allocator_type()) - : tree(kcf, alloc) - { - } - - /// Constructor initializing a B+ tree with the range [first,last) - template - inline btree_multiset(InputIterator first, InputIterator last, - const allocator_type &alloc = allocator_type()) - : tree(alloc) - { - insert(first, last); - } - - /// Constructor initializing a B+ tree with the range [first,last) and a - /// special key comparison object - template - inline btree_multiset(InputIterator first, InputIterator last, const key_compare &kcf, - const allocator_type &alloc = allocator_type()) - : tree(kcf, alloc) - { - insert(first, last); - } - - /// Frees up all used B+ tree memory pages - inline ~btree_multiset() - { - } - - /// Fast swapping of two identical B+ tree objects. - void swap(self& from) - { - std::swap(tree, from.tree); - } - -public: - // *** Key and Value Comparison Function Objects - - /// Constant access to the key comparison object sorting the B+ tree - inline key_compare key_comp() const - { - return tree.key_comp(); - } - - /// Constant access to a constructed value_type comparison object. Required - /// by the STL - inline value_compare value_comp() const - { - return tree.value_comp(); - } - -public: - // *** Allocators - - /// Return the base node allocator provided during construction. - allocator_type get_allocator() const - { - return tree.get_allocator(); - } - -public: - // *** Fast Destruction of the B+ Tree - - /// Frees all keys and all nodes of the tree - void clear() - { - tree.clear(); - } - -public: - // *** STL Iterator Construction Functions - - /// Constructs a read/data-write iterator that points to the first slot in - /// the first leaf of the B+ tree. - inline iterator begin() - { - return tree.begin(); - } - - /// Constructs a read/data-write iterator that points to the first invalid - /// slot in the last leaf of the B+ tree. - inline iterator end() - { - return tree.end(); - } - - /// Constructs a read-only constant iterator that points to the first slot - /// in the first leaf of the B+ tree. - inline const_iterator begin() const - { - return tree.begin(); - } - - /// Constructs a read-only constant iterator that points to the first - /// invalid slot in the last leaf of the B+ tree. - inline const_iterator end() const - { - return tree.end(); - } - - /// Constructs a read/data-write reverse iterator that points to the first - /// invalid slot in the last leaf of the B+ tree. Uses STL magic. - inline reverse_iterator rbegin() - { - return tree.rbegin(); - } - - /// Constructs a read/data-write reverse iterator that points to the first - /// slot in the first leaf of the B+ tree. Uses STL magic. - inline reverse_iterator rend() - { - return tree.rend(); - } - - /// Constructs a read-only reverse iterator that points to the first - /// invalid slot in the last leaf of the B+ tree. Uses STL magic. - inline const_reverse_iterator rbegin() const - { - return tree.rbegin(); - } - - /// Constructs a read-only reverse iterator that points to the first slot - /// in the first leaf of the B+ tree. Uses STL magic. - inline const_reverse_iterator rend() const - { - return tree.rend(); - } - -public: - // *** Access Functions to the Item Count - - /// Return the number of keys in the B+ tree - inline size_type size() const - { - return tree.size(); - } - - /// Returns true if there is at least one key in the B+ tree - inline bool empty() const - { - return tree.empty(); - } - - /// Returns the largest possible size of the B+ Tree. This is just a - /// function required by the STL standard, the B+ Tree can hold more items. - inline size_type max_size() const - { - return tree.max_size(); - } - - /// Return a const reference to the current statistics. - inline const tree_stats& get_stats() const - { - return tree.get_stats(); - } - -public: - // *** Standard Access Functions Querying the Tree by Descending to a Leaf - - /// Non-STL function checking whether a key is in the B+ tree. The same as - /// (find(k) != end()) or (count() != 0). - bool exists(const key_type &key) const - { - return tree.exists(key); - } - - /// Tries to locate a key in the B+ tree and returns an iterator to the - /// key slot if found. If unsuccessful it returns end(). - iterator find(const key_type &key) - { - return tree.find(key); - } - - /// Tries to locate a key in the B+ tree and returns an constant iterator - /// to the key slot if found. If unsuccessful it returns end(). - const_iterator find(const key_type &key) const - { - return tree.find(key); - } - - /// Tries to locate a key in the B+ tree and returns the number of - /// identical key entries found. - size_type count(const key_type &key) const - { - return tree.count(key); - } - - /// Searches the B+ tree and returns an iterator to the first pair - /// equal to or greater than key, or end() if all keys are smaller. - iterator lower_bound(const key_type& key) - { - return tree.lower_bound(key); - } - - /// Searches the B+ tree and returns a constant iterator to the - /// first pair equal to or greater than key, or end() if all keys - /// are smaller. - const_iterator lower_bound(const key_type& key) const - { - return tree.lower_bound(key); - } - - /// Searches the B+ tree and returns an iterator to the first pair - /// greater than key, or end() if all keys are smaller or equal. - iterator upper_bound(const key_type& key) - { - return tree.upper_bound(key); - } - - /// Searches the B+ tree and returns a constant iterator to the - /// first pair greater than key, or end() if all keys are smaller - /// or equal. - const_iterator upper_bound(const key_type& key) const - { - return tree.upper_bound(key); - } - - /// Searches the B+ tree and returns both lower_bound() and upper_bound(). - inline std::pair equal_range(const key_type& key) - { - return tree.equal_range(key); - } - - /// Searches the B+ tree and returns both lower_bound() and upper_bound(). - inline std::pair equal_range(const key_type& key) const - { - return tree.equal_range(key); - } - -public: - // *** B+ Tree Object Comparison Functions - - /// Equality relation of B+ trees of the same type. B+ trees of the same - /// size and equal key (counts) are considered equal. - inline bool operator==(const self &other) const - { - return (tree == other.tree); - } - - /// Inequality relation. Based on operator==. - inline bool operator!=(const self &other) const - { - return (tree != other.tree); - } - - /// Total ordering relation of B+ trees of the same type. It uses - /// std::lexicographical_compare() for the actual comparison of elements. - inline bool operator<(const self &other) const - { - return (tree < other.tree); - } - - /// Greater relation. Based on operator<. - inline bool operator>(const self &other) const - { - return (tree > other.tree); - } - - /// Less-equal relation. Based on operator<. - inline bool operator<=(const self &other) const - { - return (tree <= other.tree); - } - - /// Greater-equal relation. Based on operator<. - inline bool operator>=(const self &other) const - { - return (tree >= other.tree); - } - -public: - /// *** Fast Copy: Assign Operator and Copy Constructors - - /// Assignment operator. All the keys are copied - inline self& operator= (const self &other) - { - if (this != &other) - { - tree = other.tree; - } - return *this; - } - - /// Copy constructor. The newly initialized B+ tree object will contain a - /// copy of all keys. - inline btree_multiset(const self &other) - : tree(other.tree) - { - } - -public: - // *** Public Insertion Functions - - /// Attempt to insert a key into the B+ tree. As this set allows - /// duplicates, this function never fails. - inline iterator insert(const key_type& x) - { - return tree.insert2(x, data_type()).first; - } - - /// Attempt to insert a key into the B+ tree. The iterator hint is - /// currently ignored by the B+ tree insertion routine. - inline iterator insert(iterator hint, const key_type &x) - { - return tree.insert2(hint, x, data_type()); - } - - /// Attempt to insert the range [first,last) of key_type into the B+ - /// tree. Each key is inserted individually. - template - inline void insert(InputIterator first, InputIterator last) - { - InputIterator iter = first; - while(iter != last) - { - insert(*iter); - ++iter; - } - } - - /// Bulk load a sorted range [first,last). Loads items into leaves and - /// constructs a B-tree above them. The tree must be empty when calling - /// this function. - template - inline void bulk_load(Iterator first, Iterator last) - { - return tree.bulk_load(first, last); - } - -public: - // *** Public Erase Functions - - /// Erases one (the first) entry of the given key. - bool erase_one(const key_type &key) - { - return tree.erase_one(key); - } - - /// Erases all the entries of the given key. This is implemented using - /// erase_one() and thus not very efficient. - size_type erase(const key_type &key) - { - return tree.erase(key); - } - - /// Erase the key/data pair referenced by the iterator. - void erase(iterator iter) - { - return tree.erase(iter); - } - -#ifdef BTREE_TODO - /// Erase all keys in the range [first,last). This function is currently - /// not implemented by the B+ Tree. - void erase(iterator /* first */, iterator /* last */) - { - abort(); - } -#endif - -#ifdef BTREE_DEBUG -public: - // *** Debug Printing - - /// Print out the B+ tree structure with keys onto the given ostream. This function - /// requires that the header is compiled with BTREE_DEBUG and that key_type - /// is printable via std::ostream. - void print(std::ostream &os) const - { - tree.print(os); - } - - /// Print out only the leaves via the double linked list. - void print_leaves(std::ostream &os) const - { - tree.print_leaves(os); - } -#endif - -public: - // *** Verification of B+ Tree Invariants - - /// Run a thorough verification of all B+ tree invariants. The program - /// aborts via BTREE_ASSERT() if something is wrong. - void verify() const - { - tree.verify(); - } - -public: - - /// Dump the contents of the B+ tree out onto an ostream as a binary - /// image. The image contains memory pointers which will be fixed when the - /// image is restored. For this to work your key_type must be an integral - /// type and contain no pointers or references. - void dump(std::ostream &os) const - { - tree.dump(os); - } - - /// Restore a binary image of a dumped B+ tree from an istream. The B+ tree - /// pointers are fixed using the dump order. For dump and restore to work - /// your key_type must be an integral type and contain no pointers or - /// references. Returns true if the restore was successful. - bool restore(std::istream &is) - { - return tree.restore(is); - } -}; - -} // namespace stx - -#endif // _STX_BTREE_MULTISET_H_ diff --git a/tommyds/benchmark/lib/stx/btree_set b/tommyds/benchmark/lib/stx/btree_set deleted file mode 100644 index e545cea..0000000 --- a/tommyds/benchmark/lib/stx/btree_set +++ /dev/null @@ -1,39 +0,0 @@ -/** \file btree_set - * Forwarder header to btree_set.h - */ - -/* - * STX B+ Tree Template Classes v0.9 - * Copyright (C) 2008-2013 Timo Bingmann - * - * Boost Software License - Version 1.0 - August 17th, 2003 - * - * Permission is hereby granted, free of charge, to any person or organization - * obtaining a copy of the software and accompanying documentation covered by - * this license (the "Software") to use, reproduce, display, distribute, - * execute, and transmit the Software, and to prepare derivative works of the - * Software, and to permit third-parties to whom the Software is furnished to - * do so, all subject to the following: - * - * The copyright notices in the Software and this entire statement, including - * the above license grant, this restriction and the following disclaimer, must - * be included in all copies of the Software, in whole or in part, and all - * derivative works of the Software, unless such copies or derivative works are - * solely in the form of machine-executable object code generated by a source - * language processor. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT - * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE - * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#ifndef _STX_BTREE_SET_ -#define _STX_BTREE_SET_ - -#include - -#endif // _STX_BTREE_SET_ diff --git a/tommyds/benchmark/lib/stx/btree_set.h b/tommyds/benchmark/lib/stx/btree_set.h deleted file mode 100644 index fc1b20b..0000000 --- a/tommyds/benchmark/lib/stx/btree_set.h +++ /dev/null @@ -1,602 +0,0 @@ -/** \file btree_set.h - * Contains the specialized B+ tree template class btree_set - */ - -/* - * STX B+ Tree Template Classes v0.9 - * Copyright (C) 2008-2013 Timo Bingmann - * - * Boost Software License - Version 1.0 - August 17th, 2003 - * - * Permission is hereby granted, free of charge, to any person or organization - * obtaining a copy of the software and accompanying documentation covered by - * this license (the "Software") to use, reproduce, display, distribute, - * execute, and transmit the Software, and to prepare derivative works of the - * Software, and to permit third-parties to whom the Software is furnished to - * do so, all subject to the following: - * - * The copyright notices in the Software and this entire statement, including - * the above license grant, this restriction and the following disclaimer, must - * be included in all copies of the Software, in whole or in part, and all - * derivative works of the Software, unless such copies or derivative works are - * solely in the form of machine-executable object code generated by a source - * language processor. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT - * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE - * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#ifndef _STX_BTREE_SET_H_ -#define _STX_BTREE_SET_H_ - -#include - -namespace stx { - -/** @brief Specialized B+ tree template class implementing STL's set container. - * - * Implements the STL set using a B+ tree. It can be used as a drop-in - * replacement for std::set. Not all asymptotic time requirements are met in - * theory. The class has a traits class defining B+ tree properties like slots - * and self-verification. Furthermore an allocator can be specified for tree - * nodes. - * - * It is somewhat inefficient to implement a set using a B+ tree, a plain B - * tree would hold fewer copies of the keys. - * - * The set class is derived from the base implementation class btree by - * specifying an empty struct as data_type. All function are adapted to provide - * the inner class with placeholder objects. Most tricky to get right were the - * return type's of iterators which as value_type should be the same as - * key_type, and not a pair of key and dummy-struct. This is taken case of - * using some template magic in the btree class. - */ -template , - typename _Traits = btree_default_set_traits<_Key>, - typename _Alloc = std::allocator<_Key> > -class btree_set -{ -public: - // *** Template Parameter Types - - /// First template parameter: The key type of the B+ tree. This is stored - /// in inner nodes and leaves - typedef _Key key_type; - - /// Second template parameter: Key comparison function object - typedef _Compare key_compare; - - /// Third template parameter: Traits object used to define more parameters - /// of the B+ tree - typedef _Traits traits; - - /// Fourth template parameter: STL allocator - typedef _Alloc allocator_type; - - /// The macro BTREE_FRIENDS can be used by outside class to access the B+ - /// tree internals. This was added for wxBTreeDemo to be able to draw the - /// tree. - BTREE_FRIENDS - -private: - // *** The data_type - - /// \internal The empty struct used as a placeholder for the data_type - struct empty_struct - { - }; - -public: - // *** Constructed Types - - /// The empty data_type - typedef struct empty_struct data_type; - - /// Construct the set value_type: the key_type. - typedef key_type value_type; - - /// Typedef of our own type - typedef btree_set self; - - /// Implementation type of the btree_base - typedef stx::btree btree_impl; - - /// Function class comparing two value_type keys. - typedef typename btree_impl::value_compare value_compare; - - /// Size type used to count keys - typedef typename btree_impl::size_type size_type; - - /// Small structure containing statistics about the tree - typedef typename btree_impl::tree_stats tree_stats; - -public: - // *** Static Constant Options and Values of the B+ Tree - - /// Base B+ tree parameter: The number of key slots in each leaf - static const unsigned short leafslotmax = btree_impl::leafslotmax; - - /// Base B+ tree parameter: The number of key slots in each inner node, - /// this can differ from slots in each leaf. - static const unsigned short innerslotmax = btree_impl::innerslotmax; - - /// Computed B+ tree parameter: The minimum number of key slots used in a - /// leaf. If fewer slots are used, the leaf will be merged or slots shifted - /// from it's siblings. - static const unsigned short minleafslots = btree_impl::minleafslots; - - /// Computed B+ tree parameter: The minimum number of key slots used - /// in an inner node. If fewer slots are used, the inner node will be - /// merged or slots shifted from it's siblings. - static const unsigned short mininnerslots = btree_impl::mininnerslots; - - /// Debug parameter: Enables expensive and thorough checking of the B+ tree - /// invariants after each insert/erase operation. - static const bool selfverify = btree_impl::selfverify; - - /// Debug parameter: Prints out lots of debug information about how the - /// algorithms change the tree. Requires the header file to be compiled - /// with BTREE_DEBUG and the key type must be std::ostream printable. - static const bool debug = btree_impl::debug; - - /// Operational parameter: Allow duplicate keys in the btree. - static const bool allow_duplicates = btree_impl::allow_duplicates; - -public: - // *** Iterators and Reverse Iterators - - /// STL-like iterator object for B+ tree items. The iterator points to a - /// specific slot number in a leaf. - typedef typename btree_impl::iterator iterator; - - /// STL-like iterator object for B+ tree items. The iterator points to a - /// specific slot number in a leaf. - typedef typename btree_impl::const_iterator const_iterator; - - /// create mutable reverse iterator by using STL magic - typedef typename btree_impl::reverse_iterator reverse_iterator; - - /// create constant reverse iterator by using STL magic - typedef typename btree_impl::const_reverse_iterator const_reverse_iterator; - -private: - // *** Tree Implementation Object - - /// The contained implementation object - btree_impl tree; - -public: - // *** Constructors and Destructor - - /// Default constructor initializing an empty B+ tree with the standard key - /// comparison function - explicit inline btree_set(const allocator_type &alloc = allocator_type()) - : tree(alloc) - { - } - - /// Constructor initializing an empty B+ tree with a special key - /// comparison object - explicit inline btree_set(const key_compare &kcf, - const allocator_type &alloc = allocator_type()) - : tree(kcf, alloc) - { - } - - /// Constructor initializing a B+ tree with the range [first,last) - template - inline btree_set(InputIterator first, InputIterator last, - const allocator_type &alloc = allocator_type()) - : tree(alloc) - { - insert(first, last); - } - - /// Constructor initializing a B+ tree with the range [first,last) and a - /// special key comparison object - template - inline btree_set(InputIterator first, InputIterator last, const key_compare &kcf, - const allocator_type &alloc = allocator_type()) - : tree(kcf, alloc) - { - insert(first, last); - } - - /// Frees up all used B+ tree memory pages - inline ~btree_set() - { - } - - /// Fast swapping of two identical B+ tree objects. - void swap(self& from) - { - std::swap(tree, from.tree); - } - -public: - // *** Key and Value Comparison Function Objects - - /// Constant access to the key comparison object sorting the B+ tree - inline key_compare key_comp() const - { - return tree.key_comp(); - } - - /// Constant access to a constructed value_type comparison object. required - /// by the STL - inline value_compare value_comp() const - { - return tree.value_comp(); - } - -public: - // *** Allocators - - /// Return the base node allocator provided during construction. - allocator_type get_allocator() const - { - return tree.get_allocator(); - } - -public: - // *** Fast Destruction of the B+ Tree - - /// Frees all keys and all nodes of the tree - void clear() - { - tree.clear(); - } - -public: - // *** STL Iterator Construction Functions - - /// Constructs a read/data-write iterator that points to the first slot in - /// the first leaf of the B+ tree. - inline iterator begin() - { - return tree.begin(); - } - - /// Constructs a read/data-write iterator that points to the first invalid - /// slot in the last leaf of the B+ tree. - inline iterator end() - { - return tree.end(); - } - - /// Constructs a read-only constant iterator that points to the first slot - /// in the first leaf of the B+ tree. - inline const_iterator begin() const - { - return tree.begin(); - } - - /// Constructs a read-only constant iterator that points to the first - /// invalid slot in the last leaf of the B+ tree. - inline const_iterator end() const - { - return tree.end(); - } - - /// Constructs a read/data-write reverse iterator that points to the first - /// invalid slot in the last leaf of the B+ tree. Uses STL magic. - inline reverse_iterator rbegin() - { - return tree.rbegin(); - } - - /// Constructs a read/data-write reverse iterator that points to the first - /// slot in the first leaf of the B+ tree. Uses STL magic. - inline reverse_iterator rend() - { - return tree.rend(); - } - - /// Constructs a read-only reverse iterator that points to the first - /// invalid slot in the last leaf of the B+ tree. Uses STL magic. - inline const_reverse_iterator rbegin() const - { - return tree.rbegin(); - } - - /// Constructs a read-only reverse iterator that points to the first slot - /// in the first leaf of the B+ tree. Uses STL magic. - inline const_reverse_iterator rend() const - { - return tree.rend(); - } - -public: - // *** Access Functions to the Item Count - - /// Return the number of keys in the B+ tree - inline size_type size() const - { - return tree.size(); - } - - /// Returns true if there is at least one key in the B+ tree - inline bool empty() const - { - return tree.empty(); - } - - /// Returns the largest possible size of the B+ Tree. This is just a - /// function required by the STL standard, the B+ Tree can hold more items. - inline size_type max_size() const - { - return tree.max_size(); - } - - /// Return a const reference to the current statistics. - inline const tree_stats& get_stats() const - { - return tree.get_stats(); - } - -public: - // *** Standard Access Functions Querying the Tree by Descending to a Leaf - - /// Non-STL function checking whether a key is in the B+ tree. The same as - /// (find(k) != end()) or (count() != 0). - bool exists(const key_type &key) const - { - return tree.exists(key); - } - - /// Tries to locate a key in the B+ tree and returns an iterator to the - /// key slot if found. If unsuccessful it returns end(). - iterator find(const key_type &key) - { - return tree.find(key); - } - - /// Tries to locate a key in the B+ tree and returns an constant iterator - /// to the key slot if found. If unsuccessful it returns end(). - const_iterator find(const key_type &key) const - { - return tree.find(key); - } - - /// Tries to locate a key in the B+ tree and returns the number of - /// identical key entries found. As this is a unique set, count() returns - /// either 0 or 1. - size_type count(const key_type &key) const - { - return tree.count(key); - } - - /// Searches the B+ tree and returns an iterator to the first pair - /// equal to or greater than key, or end() if all keys are smaller. - iterator lower_bound(const key_type& key) - { - return tree.lower_bound(key); - } - - /// Searches the B+ tree and returns a constant iterator to the - /// first pair equal to or greater than key, or end() if all keys - /// are smaller. - const_iterator lower_bound(const key_type& key) const - { - return tree.lower_bound(key); - } - - /// Searches the B+ tree and returns an iterator to the first pair - /// greater than key, or end() if all keys are smaller or equal. - iterator upper_bound(const key_type& key) - { - return tree.upper_bound(key); - } - - /// Searches the B+ tree and returns a constant iterator to the - /// first pair greater than key, or end() if all keys are smaller - /// or equal. - const_iterator upper_bound(const key_type& key) const - { - return tree.upper_bound(key); - } - - /// Searches the B+ tree and returns both lower_bound() and upper_bound(). - inline std::pair equal_range(const key_type& key) - { - return tree.equal_range(key); - } - - /// Searches the B+ tree and returns both lower_bound() and upper_bound(). - inline std::pair equal_range(const key_type& key) const - { - return tree.equal_range(key); - } - -public: - // *** B+ Tree Object Comparison Functions - - /// Equality relation of B+ trees of the same type. B+ trees of the same - /// size and equal elements are considered equal. - inline bool operator==(const self &other) const - { - return (tree == other.tree); - } - - /// Inequality relation. Based on operator==. - inline bool operator!=(const self &other) const - { - return (tree != other.tree); - } - - /// Total ordering relation of B+ trees of the same type. It uses - /// std::lexicographical_compare() for the actual comparison of elements. - inline bool operator<(const self &other) const - { - return (tree < other.tree); - } - - /// Greater relation. Based on operator<. - inline bool operator>(const self &other) const - { - return (tree > other.tree); - } - - /// Less-equal relation. Based on operator<. - inline bool operator<=(const self &other) const - { - return (tree <= other.tree); - } - - /// Greater-equal relation. Based on operator<. - inline bool operator>=(const self &other) const - { - return (tree >= other.tree); - } - -public: - /// *** Fast Copy: Assign Operator and Copy Constructors - - /// Assignment operator. All the keys are copied - inline self& operator= (const self &other) - { - if (this != &other) - { - tree = other.tree; - } - return *this; - } - - /// Copy constructor. The newly initialized B+ tree object will contain a - /// copy of all keys. - inline btree_set(const self &other) - : tree(other.tree) - { - } - -public: - // *** Public Insertion Functions - - /// Attempt to insert a key into the B+ tree. The insert will fail if it is - /// already present. - inline std::pair insert(const key_type& x) - { - return tree.insert2(x, data_type()); - } - - /// Attempt to insert a key into the B+ tree. The iterator hint is - /// currently ignored by the B+ tree insertion routine. - inline iterator insert(iterator hint, const key_type &x) - { - return tree.insert2(hint, x, data_type()); - } - - /// Attempt to insert the range [first,last) of iterators dereferencing to - /// key_type into the B+ tree. Each key/data pair is inserted individually. - template - inline void insert(InputIterator first, InputIterator last) - { - InputIterator iter = first; - while(iter != last) - { - insert(*iter); - ++iter; - } - } - - /// Bulk load a sorted range [first,last). Loads items into leaves and - /// constructs a B-tree above them. The tree must be empty when calling - /// this function. - template - inline void bulk_load(Iterator first, Iterator last) - { - return tree.bulk_load(first, last); - } - -public: - // *** Public Erase Functions - - /// Erases the key from the set. As this is a unique set, there is no - /// difference to erase(). - bool erase_one(const key_type &key) - { - return tree.erase_one(key); - } - - /// Erases all the key/data pairs associated with the given key. - size_type erase(const key_type &key) - { - return tree.erase(key); - } - - /// Erase the key/data pair referenced by the iterator. - void erase(iterator iter) - { - return tree.erase(iter); - } - -#ifdef BTREE_TODO - /// Erase all keys in the range [first,last). This function is currently - /// not implemented by the B+ Tree. - void erase(iterator /* first */, iterator /* last */) - { - abort(); - } -#endif - -#ifdef BTREE_DEBUG -public: - // *** Debug Printing - - /// Print out the B+ tree structure with keys onto the given ostream. This function - /// requires that the header is compiled with BTREE_DEBUG and that key_type - /// is printable via std::ostream. - void print(std::ostream &os) const - { - tree.print(os); - } - - /// Print out only the leaves via the double linked list. - void print_leaves(std::ostream &os) const - { - tree.print_leaves(os); - } - -#endif - -public: - // *** Verification of B+ Tree Invariants - - /// Run a thorough verification of all B+ tree invariants. The program - /// aborts via BTREE_ASSERT() if something is wrong. - void verify() const - { - tree.verify(); - } - -public: - - /// Dump the contents of the B+ tree out onto an ostream as a binary - /// image. The image contains memory pointers which will be fixed when the - /// image is restored. For this to work your key_type must be an integral - /// type and contain no pointers or references. - void dump(std::ostream &os) const - { - tree.dump(os); - } - - /// Restore a binary image of a dumped B+ tree from an istream. The B+ tree - /// pointers are fixed using the dump order. For dump and restore to work - /// your key_type must be an integral type and contain no pointers or - /// references. Returns true if the restore was successful. - bool restore(std::istream &is) - { - return tree.restore(is); - } -}; - -} // namespace stx - -#endif // _STX_BTREE_SET_H_ diff --git a/tommyds/benchmark/lib/uthash/utarray.h b/tommyds/benchmark/lib/uthash/utarray.h deleted file mode 100644 index e3d4074..0000000 --- a/tommyds/benchmark/lib/uthash/utarray.h +++ /dev/null @@ -1,224 +0,0 @@ -/* -Copyright (c) 2008-2010, Troy D. Hanson http://uthash.sourceforge.net -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 - * see http://uthash.sourceforge.net/utarray - */ -#ifndef UTARRAY_H -#define UTARRAY_H - -#define UTARRAY_VERSION 1.9.1 - -#ifdef __GNUC__ -#define _UNUSED_ __attribute__ ((__unused__)) -#else -#define _UNUSED_ -#endif - -#include /* size_t */ -#include /* memset, etc */ -#include /* exit */ - -#define oom() exit(-1) - -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 */ - const 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) { \ - size_t _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)); \ - 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)) { \ - while(((a)->i+by) > ((a)->n)) { (a)->n = ((a)->n ? (2*(a)->n) : 8); } \ - if ( ((a)->d=(char*)realloc((a)->d, (a)->n*(a)->icd->sz)) == NULL) oom(); \ - } \ -} 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) ((char*)((a)->d + ((a)->icd->sz*(j) ))) - -#define utarray_insert(a,p,j) do { \ - utarray_reserve(a,1); \ - if (j > (a)->i) break; \ - 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) break; \ - 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) { \ - size_t _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 { \ - size_t _ut_i; \ - if (dst->i > (size_t)(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 < (size_t)(num)) { \ - utarray_reserve(dst,num-dst->i); \ - if ((dst)->icd->init) { \ - for(_ut_i=dst->i; _ut_i < 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) { \ - size_t _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_clear(a) do { \ - if ((a)->i > 0) { \ - if ((a)->icd->dtor) { \ - size_t _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_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_back(a) (((a)->i) ? (_utarray_eltptr(a,(a)->i-1)) : NULL) -#define utarray_eltidx(a,e) (((char*)(e) >= (char*)((a)->d)) ? (((char*)(e) - (char*)((a)->d))/(a)->icd->sz) : -1) - -/* 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 **_src = (char**)src, **_dst = (char**)dst; - *_dst = (*_src == NULL) ? NULL : strdup(*_src); -} -static void utarray_str_dtor(void *elt) { - char **eltc = (char**)elt; - if (*eltc) free(*eltc); -} -static const UT_icd ut_str_icd _UNUSED_ = {sizeof(char*),NULL,utarray_str_cpy,utarray_str_dtor}; -static const UT_icd ut_int_icd _UNUSED_ = {sizeof(int),NULL,NULL,NULL}; - - -#endif /* UTARRAY_H */ diff --git a/tommyds/benchmark/lib/uthash/uthash.h b/tommyds/benchmark/lib/uthash/uthash.h deleted file mode 100644 index 79d0b2a..0000000 --- a/tommyds/benchmark/lib/uthash/uthash.h +++ /dev/null @@ -1,972 +0,0 @@ -/* -Copyright (c) 2003-2010, Troy D. Hanson http://uthash.sourceforge.net -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER -OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef UTHASH_H -#define UTHASH_H - -#include /* memcmp,strlen */ -#include /* ptrdiff_t */ - -/* These macros use decltype or the earlier __typeof GNU extension. - As decltype is only available in newer compilers (VS2010 or gcc 4.3+ - when compiling c++ source) this code uses whatever method is needed - or, for VS2008 where neither is available, uses casting workarounds. */ -#ifdef _MSC_VER /* MS compiler */ -#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ -#define DECLTYPE(x) (decltype(x)) -#else /* VS2008 or older (or VS2010 in C mode) */ -#define NO_DECLTYPE -#define DECLTYPE(x) -#endif -#else /* GNU, Sun and other compilers */ -#define DECLTYPE(x) (__typeof(x)) -#endif - -#ifdef NO_DECLTYPE -#define DECLTYPE_ASSIGN(dst,src) \ -do { \ - char **_da_dst = (char**)(&(dst)); \ - *_da_dst = (char*)(src); \ -} while(0) -#else -#define DECLTYPE_ASSIGN(dst,src) \ -do { \ - (dst) = DECLTYPE(dst)(src); \ -} while(0) -#endif - -/* a number of the hash function use uint32_t which isn't defined on win32 */ -#ifdef _MSC_VER -typedef unsigned int uint32_t; -#else -#include /* uint32_t */ -#endif - -#define UTHASH_VERSION 1.9.3 - -#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ -#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ -#define uthash_free(ptr,sz) free(ptr) /* free fcn */ - -#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ -#define uthash_expand_fyi(tbl) /* can be defined to log expands */ - -/* initial number of buckets */ -#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */ -#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */ -#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */ - -/* calculate the element whose hash handle address is hhe */ -#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) - -#define HASH_FIND(hh,head,keyptr,keylen,out) \ -do { \ - unsigned _hf_bkt,_hf_hashv; \ - out=NULL; \ - if (head) { \ - HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \ - if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \ - HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \ - keyptr,keylen,out); \ - } \ - } \ -} while (0) - -#ifdef HASH_BLOOM -#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM) -#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0) -#define HASH_BLOOM_MAKE(tbl) \ -do { \ - (tbl)->bloom_nbits = HASH_BLOOM; \ - (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ - if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ - memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ - (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ -} while (0); - -#define HASH_BLOOM_FREE(tbl) \ -do { \ - uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ -} while (0); - -#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8))) -#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8))) - -#define HASH_BLOOM_ADD(tbl,hashv) \ - HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) - -#define HASH_BLOOM_TEST(tbl,hashv) \ - HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) - -#else -#define HASH_BLOOM_MAKE(tbl) -#define HASH_BLOOM_FREE(tbl) -#define HASH_BLOOM_ADD(tbl,hashv) -#define HASH_BLOOM_TEST(tbl,hashv) (1) -#endif - -#define HASH_MAKE_TABLE(hh,head) \ -do { \ - (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \ - sizeof(UT_hash_table)); \ - if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ - memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ - (head)->hh.tbl->tail = &((head)->hh); \ - (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ - (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ - (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ - (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ - HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ - if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ - memset((head)->hh.tbl->buckets, 0, \ - HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ - HASH_BLOOM_MAKE((head)->hh.tbl); \ - (head)->hh.tbl->signature = HASH_SIGNATURE; \ -} while(0) - -#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ - HASH_ADD_KEYPTR(hh,head,&add->fieldname,keylen_in,add) - -#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ -do { \ - unsigned _ha_bkt; \ - (add)->hh.next = NULL; \ - (add)->hh.key = (char*)keyptr; \ - (add)->hh.keylen = keylen_in; \ - if (!(head)) { \ - head = (add); \ - (head)->hh.prev = NULL; \ - HASH_MAKE_TABLE(hh,head); \ - } else { \ - (head)->hh.tbl->tail->next = (add); \ - (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ - (head)->hh.tbl->tail = &((add)->hh); \ - } \ - (head)->hh.tbl->num_items++; \ - (add)->hh.tbl = (head)->hh.tbl; \ - HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \ - (add)->hh.hashv, _ha_bkt); \ - HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \ - HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \ - HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \ - HASH_FSCK(hh,head); \ -} while(0) - -#define HASH_TO_BKT( hashv, num_bkts, bkt ) \ -do { \ - bkt = ((hashv) & ((num_bkts) - 1)); \ -} while(0) - -/* delete "delptr" from the hash table. - * "the usual" patch-up process for the app-file doubly-linked-list. - * The use of _hd_hh_del below deserves special explanation. - * These used to be expressed using (delptr) but that led to a bug - * if someone used the same symbol for the head and deletee, like - * HASH_DELETE(hh,users,users); - * We want that to work, but by changing the head (users) below - * we were forfeiting our ability to further refer to the deletee (users) - * in the patch-up process. Solution: use scratch space to - * copy the deletee pointer, then the latter references are via that - * scratch pointer rather than through the repointed (users) symbol. - */ -#define HASH_DELETE(hh,head,delptr) \ -do { \ - unsigned _hd_bkt; \ - struct UT_hash_handle *_hd_hh_del; \ - if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ - uthash_free((head)->hh.tbl->buckets, \ - (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ - HASH_BLOOM_FREE((head)->hh.tbl); \ - uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ - head = NULL; \ - } else { \ - _hd_hh_del = &((delptr)->hh); \ - if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ - (head)->hh.tbl->tail = \ - (UT_hash_handle*)((char*)((delptr)->hh.prev) + \ - (head)->hh.tbl->hho); \ - } \ - if ((delptr)->hh.prev) { \ - ((UT_hash_handle*)((char*)((delptr)->hh.prev) + \ - (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ - } else { \ - DECLTYPE_ASSIGN(head,(delptr)->hh.next); \ - } \ - if (_hd_hh_del->next) { \ - ((UT_hash_handle*)((char*)_hd_hh_del->next + \ - (head)->hh.tbl->hho))->prev = \ - _hd_hh_del->prev; \ - } \ - HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ - HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ - (head)->hh.tbl->num_items--; \ - } \ - HASH_FSCK(hh,head); \ -} while (0) - - -/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ -#define HASH_FIND_STR(head,findstr,out) \ - HASH_FIND(hh,head,findstr,strlen(findstr),out) -#define HASH_ADD_STR(head,strfield,add) \ - HASH_ADD(hh,head,strfield,strlen(add->strfield),add) -#define HASH_FIND_INT(head,findint,out) \ - HASH_FIND(hh,head,findint,sizeof(int),out) -#define HASH_ADD_INT(head,intfield,add) \ - HASH_ADD(hh,head,intfield,sizeof(int),add) -#define HASH_FIND_PTR(head,findptr,out) \ - HASH_FIND(hh,head,findptr,sizeof(void *),out) -#define HASH_ADD_PTR(head,ptrfield,add) \ - HASH_ADD(hh,head,ptrfield,sizeof(void *),add) -#define HASH_DEL(head,delptr) \ - HASH_DELETE(hh,head,delptr) - -/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. - * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. - */ -#ifdef HASH_DEBUG -#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) -#define HASH_FSCK(hh,head) \ -do { \ - unsigned _bkt_i; \ - unsigned _count, _bkt_count; \ - char *_prev; \ - struct UT_hash_handle *_thh; \ - if (head) { \ - _count = 0; \ - for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ - _bkt_count = 0; \ - _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ - _prev = NULL; \ - while (_thh) { \ - if (_prev != (char*)(_thh->hh_prev)) { \ - HASH_OOPS("invalid hh_prev %p, actual %p\n", \ - _thh->hh_prev, _prev ); \ - } \ - _bkt_count++; \ - _prev = (char*)(_thh); \ - _thh = _thh->hh_next; \ - } \ - _count += _bkt_count; \ - if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ - HASH_OOPS("invalid bucket count %d, actual %d\n", \ - (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ - } \ - } \ - if (_count != (head)->hh.tbl->num_items) { \ - HASH_OOPS("invalid hh item count %d, actual %d\n", \ - (head)->hh.tbl->num_items, _count ); \ - } \ - /* traverse hh in app file; check next/prev integrity, count */ \ - _count = 0; \ - _prev = NULL; \ - _thh = &(head)->hh; \ - while (_thh) { \ - _count++; \ - if (_prev !=(char*)(_thh->prev)) { \ - HASH_OOPS("invalid prev %p, actual %p\n", \ - _thh->prev, _prev ); \ - } \ - _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ - _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ - (head)->hh.tbl->hho) : NULL ); \ - } \ - if (_count != (head)->hh.tbl->num_items) { \ - HASH_OOPS("invalid app item count %d, actual %d\n", \ - (head)->hh.tbl->num_items, _count ); \ - } \ - } \ -} while (0) -#else -#define HASH_FSCK(hh,head) -#endif - -/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to - * the descriptor to which this macro is defined for tuning the hash function. - * The app can #include to get the prototype for write(2). */ -#ifdef HASH_EMIT_KEYS -#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ -do { \ - unsigned _klen = fieldlen; \ - write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ - write(HASH_EMIT_KEYS, keyptr, fieldlen); \ -} while (0) -#else -#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) -#endif - -/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ -#ifdef HASH_FUNCTION -#define HASH_FCN HASH_FUNCTION -#else -#define HASH_FCN HASH_JEN -#endif - -/* The Bernstein hash function, used in Perl prior to v5.6 */ -#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \ -do { \ - unsigned _hb_keylen=keylen; \ - char *_hb_key=(char*)(key); \ - (hashv) = 0; \ - while (_hb_keylen--) { (hashv) = ((hashv) * 33) + *_hb_key++; } \ - bkt = (hashv) & (num_bkts-1); \ -} while (0) - - -/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at - * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ -#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \ -do { \ - unsigned _sx_i; \ - char *_hs_key=(char*)(key); \ - hashv = 0; \ - for(_sx_i=0; _sx_i < keylen; _sx_i++) \ - hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ - bkt = hashv & (num_bkts-1); \ -} while (0) - -#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \ -do { \ - unsigned _fn_i; \ - char *_hf_key=(char*)(key); \ - hashv = 2166136261UL; \ - for(_fn_i=0; _fn_i < keylen; _fn_i++) \ - hashv = (hashv * 16777619) ^ _hf_key[_fn_i]; \ - bkt = hashv & (num_bkts-1); \ -} while(0); - -#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \ -do { \ - unsigned _ho_i; \ - char *_ho_key=(char*)(key); \ - hashv = 0; \ - for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ - hashv += _ho_key[_ho_i]; \ - hashv += (hashv << 10); \ - hashv ^= (hashv >> 6); \ - } \ - hashv += (hashv << 3); \ - hashv ^= (hashv >> 11); \ - hashv += (hashv << 15); \ - bkt = hashv & (num_bkts-1); \ -} while(0) - -#define HASH_JEN_MIX(a,b,c) \ -do { \ - a -= b; a -= c; a ^= ( c >> 13 ); \ - b -= c; b -= a; b ^= ( a << 8 ); \ - c -= a; c -= b; c ^= ( b >> 13 ); \ - a -= b; a -= c; a ^= ( c >> 12 ); \ - b -= c; b -= a; b ^= ( a << 16 ); \ - c -= a; c -= b; c ^= ( b >> 5 ); \ - a -= b; a -= c; a ^= ( c >> 3 ); \ - b -= c; b -= a; b ^= ( a << 10 ); \ - c -= a; c -= b; c ^= ( b >> 15 ); \ -} while (0) - -#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \ -do { \ - unsigned _hj_i,_hj_j,_hj_k; \ - char *_hj_key=(char*)(key); \ - hashv = 0xfeedbeef; \ - _hj_i = _hj_j = 0x9e3779b9; \ - _hj_k = keylen; \ - while (_hj_k >= 12) { \ - _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ - + ( (unsigned)_hj_key[2] << 16 ) \ - + ( (unsigned)_hj_key[3] << 24 ) ); \ - _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ - + ( (unsigned)_hj_key[6] << 16 ) \ - + ( (unsigned)_hj_key[7] << 24 ) ); \ - hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ - + ( (unsigned)_hj_key[10] << 16 ) \ - + ( (unsigned)_hj_key[11] << 24 ) ); \ - \ - HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ - \ - _hj_key += 12; \ - _hj_k -= 12; \ - } \ - hashv += keylen; \ - switch ( _hj_k ) { \ - case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \ - case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \ - case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \ - case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \ - case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \ - case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \ - case 5: _hj_j += _hj_key[4]; \ - case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \ - case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \ - case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \ - case 1: _hj_i += _hj_key[0]; \ - } \ - HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ - bkt = hashv & (num_bkts-1); \ -} while(0) - -/* The Paul Hsieh hash function */ -#undef get16bits -#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ - || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) -#define get16bits(d) (*((const uint16_t *) (d))) -#endif - -#if !defined (get16bits) -#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ - +(uint32_t)(((const uint8_t *)(d))[0]) ) -#endif -#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \ -do { \ - char *_sfh_key=(char*)(key); \ - uint32_t _sfh_tmp, _sfh_len = keylen; \ - \ - int _sfh_rem = _sfh_len & 3; \ - _sfh_len >>= 2; \ - hashv = 0xcafebabe; \ - \ - /* Main loop */ \ - for (;_sfh_len > 0; _sfh_len--) { \ - hashv += get16bits (_sfh_key); \ - _sfh_tmp = (get16bits (_sfh_key+2) << 11) ^ hashv; \ - hashv = (hashv << 16) ^ _sfh_tmp; \ - _sfh_key += 2*sizeof (uint16_t); \ - hashv += hashv >> 11; \ - } \ - \ - /* Handle end cases */ \ - switch (_sfh_rem) { \ - case 3: hashv += get16bits (_sfh_key); \ - hashv ^= hashv << 16; \ - hashv ^= _sfh_key[sizeof (uint16_t)] << 18; \ - hashv += hashv >> 11; \ - break; \ - case 2: hashv += get16bits (_sfh_key); \ - hashv ^= hashv << 11; \ - hashv += hashv >> 17; \ - break; \ - case 1: hashv += *_sfh_key; \ - hashv ^= hashv << 10; \ - hashv += hashv >> 1; \ - } \ - \ - /* Force "avalanching" of final 127 bits */ \ - hashv ^= hashv << 3; \ - hashv += hashv >> 5; \ - hashv ^= hashv << 4; \ - hashv += hashv >> 17; \ - hashv ^= hashv << 25; \ - hashv += hashv >> 6; \ - bkt = hashv & (num_bkts-1); \ -} while(0); - -#ifdef HASH_USING_NO_STRICT_ALIASING -/* The MurmurHash exploits some CPU's (e.g. x86) tolerance for unaligned reads. - * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. - * So MurmurHash comes in two versions, the faster unaligned one and the slower - * aligned one. We only use the faster one on CPU's where we know it's safe. - * - * Note the preprocessor built-in defines can be emitted using: - * - * gcc -m64 -dM -E - < /dev/null (on gcc) - * cc -## a.c (where a.c is a simple test file) (Sun Studio) - */ -#if (defined(__i386__) || defined(__x86_64__)) -#define HASH_MUR HASH_MUR_UNALIGNED -#else -#define HASH_MUR HASH_MUR_ALIGNED -#endif - -/* Appleby's MurmurHash fast version for unaligned-tolerant archs like i386 */ -#define HASH_MUR_UNALIGNED(key,keylen,num_bkts,hashv,bkt) \ -do { \ - const unsigned int _mur_m = 0x5bd1e995; \ - const int _mur_r = 24; \ - hashv = 0xcafebabe ^ keylen; \ - char *_mur_key = (char *)(key); \ - uint32_t _mur_tmp, _mur_len = keylen; \ - \ - for (;_mur_len >= 4; _mur_len-=4) { \ - _mur_tmp = *(uint32_t *)_mur_key; \ - _mur_tmp *= _mur_m; \ - _mur_tmp ^= _mur_tmp >> _mur_r; \ - _mur_tmp *= _mur_m; \ - hashv *= _mur_m; \ - hashv ^= _mur_tmp; \ - _mur_key += 4; \ - } \ - \ - switch(_mur_len) \ - { \ - case 3: hashv ^= _mur_key[2] << 16; \ - case 2: hashv ^= _mur_key[1] << 8; \ - case 1: hashv ^= _mur_key[0]; \ - hashv *= _mur_m; \ - }; \ - \ - hashv ^= hashv >> 13; \ - hashv *= _mur_m; \ - hashv ^= hashv >> 15; \ - \ - bkt = hashv & (num_bkts-1); \ -} while(0) - -/* Appleby's MurmurHash version for alignment-sensitive archs like Sparc */ -#define HASH_MUR_ALIGNED(key,keylen,num_bkts,hashv,bkt) \ -do { \ - const unsigned int _mur_m = 0x5bd1e995; \ - const int _mur_r = 24; \ - hashv = 0xcafebabe ^ (keylen); \ - char *_mur_key = (char *)(key); \ - uint32_t _mur_len = keylen; \ - int _mur_align = (int)_mur_key & 3; \ - \ - if (_mur_align && (_mur_len >= 4)) { \ - unsigned _mur_t = 0, _mur_d = 0; \ - switch(_mur_align) { \ - case 1: _mur_t |= _mur_key[2] << 16; \ - case 2: _mur_t |= _mur_key[1] << 8; \ - case 3: _mur_t |= _mur_key[0]; \ - } \ - _mur_t <<= (8 * _mur_align); \ - _mur_key += 4-_mur_align; \ - _mur_len -= 4-_mur_align; \ - int _mur_sl = 8 * (4-_mur_align); \ - int _mur_sr = 8 * _mur_align; \ - \ - for (;_mur_len >= 4; _mur_len-=4) { \ - _mur_d = *(unsigned *)_mur_key; \ - _mur_t = (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \ - unsigned _mur_k = _mur_t; \ - _mur_k *= _mur_m; \ - _mur_k ^= _mur_k >> _mur_r; \ - _mur_k *= _mur_m; \ - hashv *= _mur_m; \ - hashv ^= _mur_k; \ - _mur_t = _mur_d; \ - _mur_key += 4; \ - } \ - _mur_d = 0; \ - if(_mur_len >= _mur_align) { \ - switch(_mur_align) { \ - case 3: _mur_d |= _mur_key[2] << 16; \ - case 2: _mur_d |= _mur_key[1] << 8; \ - case 1: _mur_d |= _mur_key[0]; \ - } \ - unsigned _mur_k = (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \ - _mur_k *= _mur_m; \ - _mur_k ^= _mur_k >> _mur_r; \ - _mur_k *= _mur_m; \ - hashv *= _mur_m; \ - hashv ^= _mur_k; \ - _mur_k += _mur_align; \ - _mur_len -= _mur_align; \ - \ - switch(_mur_len) \ - { \ - case 3: hashv ^= _mur_key[2] << 16; \ - case 2: hashv ^= _mur_key[1] << 8; \ - case 1: hashv ^= _mur_key[0]; \ - hashv *= _mur_m; \ - } \ - } else { \ - switch(_mur_len) \ - { \ - case 3: _mur_d ^= _mur_key[2] << 16; \ - case 2: _mur_d ^= _mur_key[1] << 8; \ - case 1: _mur_d ^= _mur_key[0]; \ - case 0: hashv ^= (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \ - hashv *= _mur_m; \ - } \ - } \ - \ - hashv ^= hashv >> 13; \ - hashv *= _mur_m; \ - hashv ^= hashv >> 15; \ - } else { \ - for (;_mur_len >= 4; _mur_len-=4) { \ - unsigned _mur_k = *(unsigned*)_mur_key; \ - _mur_k *= _mur_m; \ - _mur_k ^= _mur_k >> _mur_r; \ - _mur_k *= _mur_m; \ - hashv *= _mur_m; \ - hashv ^= _mur_k; \ - _mur_key += 4; \ - } \ - switch(_mur_len) \ - { \ - case 3: hashv ^= _mur_key[2] << 16; \ - case 2: hashv ^= _mur_key[1] << 8; \ - case 1: hashv ^= _mur_key[0]; \ - hashv *= _mur_m; \ - } \ - \ - hashv ^= hashv >> 13; \ - hashv *= _mur_m; \ - hashv ^= hashv >> 15; \ - } \ - bkt = hashv & (num_bkts-1); \ -} while(0) -#endif /* HASH_USING_NO_STRICT_ALIASING */ - -/* key comparison function; return 0 if keys equal */ -#define HASH_KEYCMP(a,b,len) memcmp(a,b,len) - -/* iterate over items in a known bucket to find desired item */ -#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \ -do { \ - if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \ - else out=NULL; \ - while (out) { \ - if (out->hh.keylen == keylen_in) { \ - if ((HASH_KEYCMP(out->hh.key,keyptr,keylen_in)) == 0) break; \ - } \ - if (out->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,out->hh.hh_next)); \ - else out = NULL; \ - } \ -} while(0) - -/* add an item to a bucket */ -#define HASH_ADD_TO_BKT(head,addhh) \ -do { \ - head.count++; \ - (addhh)->hh_next = head.hh_head; \ - (addhh)->hh_prev = NULL; \ - if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \ - (head).hh_head=addhh; \ - if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \ - && (addhh)->tbl->noexpand != 1) { \ - HASH_EXPAND_BUCKETS((addhh)->tbl); \ - } \ -} while(0) - -/* remove an item from a given bucket */ -#define HASH_DEL_IN_BKT(hh,head,hh_del) \ - (head).count--; \ - if ((head).hh_head == hh_del) { \ - (head).hh_head = hh_del->hh_next; \ - } \ - if (hh_del->hh_prev) { \ - hh_del->hh_prev->hh_next = hh_del->hh_next; \ - } \ - if (hh_del->hh_next) { \ - hh_del->hh_next->hh_prev = hh_del->hh_prev; \ - } - -/* Bucket expansion has the effect of doubling the number of buckets - * and redistributing the items into the new buckets. Ideally the - * items will distribute more or less evenly into the new buckets - * (the extent to which this is true is a measure of the quality of - * the hash function as it applies to the key domain). - * - * With the items distributed into more buckets, the chain length - * (item count) in each bucket is reduced. Thus by expanding buckets - * the hash keeps a bound on the chain length. This bounded chain - * length is the essence of how a hash provides constant time lookup. - * - * The calculation of tbl->ideal_chain_maxlen below deserves some - * explanation. First, keep in mind that we're calculating the ideal - * maximum chain length based on the *new* (doubled) bucket count. - * In fractions this is just n/b (n=number of items,b=new num buckets). - * Since the ideal chain length is an integer, we want to calculate - * ceil(n/b). We don't depend on floating point arithmetic in this - * hash, so to calculate ceil(n/b) with integers we could write - * - * ceil(n/b) = (n/b) + ((n%b)?1:0) - * - * and in fact a previous version of this hash did just that. - * But now we have improved things a bit by recognizing that b is - * always a power of two. We keep its base 2 log handy (call it lb), - * so now we can write this with a bit shift and logical AND: - * - * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) - * - */ -#define HASH_EXPAND_BUCKETS(tbl) \ -do { \ - unsigned _he_bkt; \ - unsigned _he_bkt_i; \ - struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ - UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ - _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ - 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ - if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ - memset(_he_new_buckets, 0, \ - 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ - tbl->ideal_chain_maxlen = \ - (tbl->num_items >> (tbl->log2_num_buckets+1)) + \ - ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \ - tbl->nonideal_items = 0; \ - for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ - { \ - _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ - while (_he_thh) { \ - _he_hh_nxt = _he_thh->hh_next; \ - HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \ - _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ - if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ - tbl->nonideal_items++; \ - _he_newbkt->expand_mult = _he_newbkt->count / \ - tbl->ideal_chain_maxlen; \ - } \ - _he_thh->hh_prev = NULL; \ - _he_thh->hh_next = _he_newbkt->hh_head; \ - if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \ - _he_thh; \ - _he_newbkt->hh_head = _he_thh; \ - _he_thh = _he_hh_nxt; \ - } \ - } \ - uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ - tbl->num_buckets *= 2; \ - tbl->log2_num_buckets++; \ - tbl->buckets = _he_new_buckets; \ - tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ - (tbl->ineff_expands+1) : 0; \ - if (tbl->ineff_expands > 1) { \ - tbl->noexpand=1; \ - uthash_noexpand_fyi(tbl); \ - } \ - uthash_expand_fyi(tbl); \ -} while(0) - - -/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ -/* Note that HASH_SORT assumes the hash handle name to be hh. - * HASH_SRT was added to allow the hash handle name to be passed in. */ -#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) -#define HASH_SRT(hh,head,cmpfcn) \ -do { \ - unsigned _hs_i; \ - unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ - struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ - if (head) { \ - _hs_insize = 1; \ - _hs_looping = 1; \ - _hs_list = &((head)->hh); \ - while (_hs_looping) { \ - _hs_p = _hs_list; \ - _hs_list = NULL; \ - _hs_tail = NULL; \ - _hs_nmerges = 0; \ - while (_hs_p) { \ - _hs_nmerges++; \ - _hs_q = _hs_p; \ - _hs_psize = 0; \ - for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ - _hs_psize++; \ - _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ - ((void*)((char*)(_hs_q->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - if (! (_hs_q) ) break; \ - } \ - _hs_qsize = _hs_insize; \ - while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \ - if (_hs_psize == 0) { \ - _hs_e = _hs_q; \ - _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ - ((void*)((char*)(_hs_q->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - _hs_qsize--; \ - } else if ( (_hs_qsize == 0) || !(_hs_q) ) { \ - _hs_e = _hs_p; \ - _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ - ((void*)((char*)(_hs_p->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - _hs_psize--; \ - } else if (( \ - cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ - DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ - ) <= 0) { \ - _hs_e = _hs_p; \ - _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ - ((void*)((char*)(_hs_p->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - _hs_psize--; \ - } else { \ - _hs_e = _hs_q; \ - _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ - ((void*)((char*)(_hs_q->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - _hs_qsize--; \ - } \ - if ( _hs_tail ) { \ - _hs_tail->next = ((_hs_e) ? \ - ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ - } else { \ - _hs_list = _hs_e; \ - } \ - _hs_e->prev = ((_hs_tail) ? \ - ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ - _hs_tail = _hs_e; \ - } \ - _hs_p = _hs_q; \ - } \ - _hs_tail->next = NULL; \ - if ( _hs_nmerges <= 1 ) { \ - _hs_looping=0; \ - (head)->hh.tbl->tail = _hs_tail; \ - DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ - } \ - _hs_insize *= 2; \ - } \ - HASH_FSCK(hh,head); \ - } \ -} while (0) - -/* This function selects items from one hash into another hash. - * The end result is that the selected items have dual presence - * in both hashes. There is no copy of the items made; rather - * they are added into the new hash through a secondary hash - * hash handle that must be present in the structure. */ -#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ -do { \ - unsigned _src_bkt, _dst_bkt; \ - void *_last_elt=NULL, *_elt; \ - UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ - ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ - if (src) { \ - for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ - for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ - _src_hh; \ - _src_hh = _src_hh->hh_next) { \ - _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ - if (cond(_elt)) { \ - _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ - _dst_hh->key = _src_hh->key; \ - _dst_hh->keylen = _src_hh->keylen; \ - _dst_hh->hashv = _src_hh->hashv; \ - _dst_hh->prev = _last_elt; \ - _dst_hh->next = NULL; \ - if (_last_elt_hh) { _last_elt_hh->next = _elt; } \ - if (!dst) { \ - DECLTYPE_ASSIGN(dst,_elt); \ - HASH_MAKE_TABLE(hh_dst,dst); \ - } else { \ - _dst_hh->tbl = (dst)->hh_dst.tbl; \ - } \ - HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ - HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ - (dst)->hh_dst.tbl->num_items++; \ - _last_elt = _elt; \ - _last_elt_hh = _dst_hh; \ - } \ - } \ - } \ - } \ - HASH_FSCK(hh_dst,dst); \ -} while (0) - -#define HASH_CLEAR(hh,head) \ -do { \ - if (head) { \ - uthash_free((head)->hh.tbl->buckets, \ - (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ - uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ - (head)=NULL; \ - } \ -} while(0) - -#ifdef NO_DECLTYPE -#define HASH_ITER(hh,head,el,tmp) \ -for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL); \ - el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL)) -#else -#define HASH_ITER(hh,head,el,tmp) \ -for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); \ - el; (el)=(tmp),(tmp)=DECLTYPE(el)((tmp)?(tmp)->hh.next:NULL)) -#endif - -/* obtain a count of items in the hash */ -#define HASH_COUNT(head) HASH_CNT(hh,head) -#define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0) - -typedef struct UT_hash_bucket { - struct UT_hash_handle *hh_head; - unsigned count; - - /* expand_mult is normally set to 0. In this situation, the max chain length - * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If - * the bucket's chain exceeds this length, bucket expansion is triggered). - * However, setting expand_mult to a non-zero value delays bucket expansion - * (that would be triggered by additions to this particular bucket) - * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. - * (The multiplier is simply expand_mult+1). The whole idea of this - * multiplier is to reduce bucket expansions, since they are expensive, in - * situations where we know that a particular bucket tends to be overused. - * It is better to let its chain length grow to a longer yet-still-bounded - * value, than to do an O(n) bucket expansion too often. - */ - unsigned expand_mult; - -} UT_hash_bucket; - -/* random signature used only to find hash tables in external analysis */ -#define HASH_SIGNATURE 0xa0111fe1 -#define HASH_BLOOM_SIGNATURE 0xb12220f2 - -typedef struct UT_hash_table { - UT_hash_bucket *buckets; - unsigned num_buckets, log2_num_buckets; - unsigned num_items; - struct UT_hash_handle *tail; /* tail hh in app file, for fast append */ - ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ - - /* in an ideal situation (all buckets used equally), no bucket would have - * more than ceil(#items/#buckets) items. that's the ideal chain length. */ - unsigned ideal_chain_maxlen; - - /* nonideal_items is the number of items in the hash whose chain position - * exceeds the ideal chain maxlen. these items pay the penalty for an uneven - * hash distribution; reaching them in a chain traversal takes >ideal steps */ - unsigned nonideal_items; - - /* ineffective expands occur when a bucket doubling was performed, but - * afterward, more than half the items in the hash had nonideal chain - * positions. If this happens on two consecutive expansions we inhibit any - * further expansion, as it's not helping; this happens when the hash - * function isn't a good fit for the key domain. When expansion is inhibited - * the hash will still work, albeit no longer in constant time. */ - unsigned ineff_expands, noexpand; - - uint32_t signature; /* used only to find hash tables in external analysis */ -#ifdef HASH_BLOOM - uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ - uint8_t *bloom_bv; - char bloom_nbits; -#endif - -} UT_hash_table; - -typedef struct UT_hash_handle { - struct UT_hash_table *tbl; - void *prev; /* prev element in app file */ - void *next; /* next element in app file */ - struct UT_hash_handle *hh_prev; /* previous hh in bucket file */ - struct UT_hash_handle *hh_next; /* next hh in bucket file */ - void *key; /* ptr to enclosing struct's key */ - unsigned keylen; /* enclosing struct's key len */ - unsigned hashv; /* result of hash-fcn(key) */ -} UT_hash_handle; - -#endif /* UTHASH_H */ diff --git a/tommyds/benchmark/lib/uthash/utlist.h b/tommyds/benchmark/lib/uthash/utlist.h deleted file mode 100644 index 34c725b..0000000 --- a/tommyds/benchmark/lib/uthash/utlist.h +++ /dev/null @@ -1,490 +0,0 @@ -/* -Copyright (c) 2007-2010, Troy D. Hanson http://uthash.sourceforge.net -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER -OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef UTLIST_H -#define UTLIST_H - -#define UTLIST_VERSION 1.9.1 - -/* - * This file contains macros to manipulate singly and doubly-linked lists. - * - * 1. LL_ macros: singly-linked lists. - * 2. DL_ macros: doubly-linked lists. - * 3. CDL_ macros: circular doubly-linked lists. - * - * To use singly-linked lists, your structure must have a "next" pointer. - * To use doubly-linked lists, your structure must "prev" and "next" pointers. - * Either way, the pointer to the head of the list must be initialized to NULL. - * - * ----------------.EXAMPLE ------------------------- - * struct item { - * int id; - * struct item *prev, *next; - * } - * - * struct item *list = NULL: - * - * int main() { - * struct item *item; - * ... allocate and populate item ... - * DL_APPEND(list, item); - * } - * -------------------------------------------------- - * - * For doubly-linked lists, the append and delete macros are O(1) - * For singly-linked lists, append and delete are O(n) but prepend is O(1) - * The sort macro is O(n log(n)) for all types of single/double/circular lists. - */ - -/* These macros use decltype or the earlier __typeof GNU extension. - As decltype is only available in newer compilers (VS2010 or gcc 4.3+ - when compiling c++ code), this code uses whatever method is needed - or, for VS2008 where neither is available, uses casting workarounds. */ -#ifdef _MSC_VER /* MS compiler */ -#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ -#define LDECLTYPE(x) decltype(x) -#else /* VS2008 or older (or VS2010 in C mode) */ -#define NO_DECLTYPE -#define LDECLTYPE(x) char* -#endif -#else /* GNU, Sun and other compilers */ -#define LDECLTYPE(x) __typeof(x) -#endif - -/* for VS2008 we use some workarounds to get around the lack of decltype, - * namely, we always reassign our tmp variable to the list head if we need - * to dereference its prev/next pointers, and save/restore the real head.*/ -#ifdef NO_DECLTYPE -#define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } -#define _NEXT(elt,list) ((char*)((list)->next)) -#define _NEXTASGN(elt,list,to) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } -#define _PREV(elt,list) ((char*)((list)->prev)) -#define _PREVASGN(elt,list,to) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } -#define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } -#define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } -#else -#define _SV(elt,list) -#define _NEXT(elt,list) ((elt)->next) -#define _NEXTASGN(elt,list,to) ((elt)->next)=(to) -#define _PREV(elt,list) ((elt)->prev) -#define _PREVASGN(elt,list,to) ((elt)->prev)=(to) -#define _RS(list) -#define _CASTASGN(a,b) (a)=(b) -#endif - -/****************************************************************************** - * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort * - * Unwieldy variable names used here to avoid shadowing passed-in variables. * - *****************************************************************************/ -#define LL_SORT(list, cmp) \ -do { \ - LDECLTYPE(list) _ls_p; \ - LDECLTYPE(list) _ls_q; \ - LDECLTYPE(list) _ls_e; \ - LDECLTYPE(list) _ls_tail; \ - LDECLTYPE(list) _ls_oldhead; \ - LDECLTYPE(list) _tmp; \ - int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ - if (list) { \ - _ls_insize = 1; \ - _ls_looping = 1; \ - while (_ls_looping) { \ - _CASTASGN(_ls_p,list); \ - _CASTASGN(_ls_oldhead,list); \ - list = NULL; \ - _ls_tail = NULL; \ - _ls_nmerges = 0; \ - while (_ls_p) { \ - _ls_nmerges++; \ - _ls_q = _ls_p; \ - _ls_psize = 0; \ - for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ - _ls_psize++; \ - _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); \ - if (!_ls_q) break; \ - } \ - _ls_qsize = _ls_insize; \ - while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ - if (_ls_psize == 0) { \ - _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ - } else if (_ls_qsize == 0 || !_ls_q) { \ - _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ - } else if (cmp(_ls_p,_ls_q) <= 0) { \ - _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ - } else { \ - _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ - } \ - if (_ls_tail) { \ - _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list); \ - } else { \ - _CASTASGN(list,_ls_e); \ - } \ - _ls_tail = _ls_e; \ - } \ - _ls_p = _ls_q; \ - } \ - _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL); _RS(list); \ - if (_ls_nmerges <= 1) { \ - _ls_looping=0; \ - } \ - _ls_insize *= 2; \ - } \ - } else _tmp=NULL; /* quiet gcc unused variable warning */ \ -} while (0) - -#define DL_SORT(list, cmp) \ -do { \ - LDECLTYPE(list) _ls_p; \ - LDECLTYPE(list) _ls_q; \ - LDECLTYPE(list) _ls_e; \ - LDECLTYPE(list) _ls_tail; \ - LDECLTYPE(list) _ls_oldhead; \ - LDECLTYPE(list) _tmp; \ - int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ - if (list) { \ - _ls_insize = 1; \ - _ls_looping = 1; \ - while (_ls_looping) { \ - _CASTASGN(_ls_p,list); \ - _CASTASGN(_ls_oldhead,list); \ - list = NULL; \ - _ls_tail = NULL; \ - _ls_nmerges = 0; \ - while (_ls_p) { \ - _ls_nmerges++; \ - _ls_q = _ls_p; \ - _ls_psize = 0; \ - for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ - _ls_psize++; \ - _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); \ - if (!_ls_q) break; \ - } \ - _ls_qsize = _ls_insize; \ - while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ - if (_ls_psize == 0) { \ - _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ - } else if (_ls_qsize == 0 || !_ls_q) { \ - _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ - } else if (cmp(_ls_p,_ls_q) <= 0) { \ - _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ - } else { \ - _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ - } \ - if (_ls_tail) { \ - _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list); \ - } else { \ - _CASTASGN(list,_ls_e); \ - } \ - _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail); _RS(list); \ - _ls_tail = _ls_e; \ - } \ - _ls_p = _ls_q; \ - } \ - _CASTASGN(list->prev, _ls_tail); \ - _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL); _RS(list); \ - if (_ls_nmerges <= 1) { \ - _ls_looping=0; \ - } \ - _ls_insize *= 2; \ - } \ - } else _tmp=NULL; /* quiet gcc unused variable warning */ \ -} while (0) - -#define CDL_SORT(list, cmp) \ -do { \ - LDECLTYPE(list) _ls_p; \ - LDECLTYPE(list) _ls_q; \ - LDECLTYPE(list) _ls_e; \ - LDECLTYPE(list) _ls_tail; \ - LDECLTYPE(list) _ls_oldhead; \ - LDECLTYPE(list) _tmp; \ - LDECLTYPE(list) _tmp2; \ - int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ - if (list) { \ - _ls_insize = 1; \ - _ls_looping = 1; \ - while (_ls_looping) { \ - _CASTASGN(_ls_p,list); \ - _CASTASGN(_ls_oldhead,list); \ - list = NULL; \ - _ls_tail = NULL; \ - _ls_nmerges = 0; \ - while (_ls_p) { \ - _ls_nmerges++; \ - _ls_q = _ls_p; \ - _ls_psize = 0; \ - for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ - _ls_psize++; \ - _SV(_ls_q,list); \ - if (_NEXT(_ls_q,list) == _ls_oldhead) { \ - _ls_q = NULL; \ - } else { \ - _ls_q = _NEXT(_ls_q,list); \ - } \ - _RS(list); \ - if (!_ls_q) break; \ - } \ - _ls_qsize = _ls_insize; \ - while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ - if (_ls_psize == 0) { \ - _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ - if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ - } else if (_ls_qsize == 0 || !_ls_q) { \ - _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ - if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ - } else if (cmp(_ls_p,_ls_q) <= 0) { \ - _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \ - if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ - } else { \ - _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \ - if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ - } \ - if (_ls_tail) { \ - _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list); \ - } else { \ - _CASTASGN(list,_ls_e); \ - } \ - _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail); _RS(list); \ - _ls_tail = _ls_e; \ - } \ - _ls_p = _ls_q; \ - } \ - _CASTASGN(list->prev,_ls_tail); \ - _CASTASGN(_tmp2,list); \ - _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp2); _RS(list); \ - if (_ls_nmerges <= 1) { \ - _ls_looping=0; \ - } \ - _ls_insize *= 2; \ - } \ - } else _tmp=NULL; /* quiet gcc unused variable warning */ \ -} while (0) - -/****************************************************************************** - * singly linked list macros (non-circular) * - *****************************************************************************/ -#define LL_PREPEND(head,add) \ -do { \ - (add)->next = head; \ - head = add; \ -} while (0) - -#define LL_APPEND(head,add) \ -do { \ - LDECLTYPE(head) _tmp; \ - (add)->next=NULL; \ - if (head) { \ - _tmp = head; \ - while (_tmp->next) { _tmp = _tmp->next; } \ - _tmp->next=(add); \ - } else { \ - (head)=(add); \ - } \ -} while (0) - -#define LL_DELETE(head,del) \ -do { \ - LDECLTYPE(head) _tmp; \ - if ((head) == (del)) { \ - (head)=(head)->next; \ - } else { \ - _tmp = head; \ - while (_tmp->next && (_tmp->next != (del))) { \ - _tmp = _tmp->next; \ - } \ - if (_tmp->next) { \ - _tmp->next = ((del)->next); \ - } \ - } \ -} while (0) - -/* Here are VS2008 replacements for LL_APPEND and LL_DELETE */ -#define LL_APPEND_VS2008(head,add) \ -do { \ - if (head) { \ - (add)->next = head; /* use add->next as a temp variable */ \ - while ((add)->next->next) { (add)->next = (add)->next->next; } \ - (add)->next->next=(add); \ - } else { \ - (head)=(add); \ - } \ - (add)->next=NULL; \ -} while (0) - -#define LL_DELETE_VS2008(head,del) \ -do { \ - if ((head) == (del)) { \ - (head)=(head)->next; \ - } else { \ - char *_tmp = (char*)(head); \ - while (head->next && (head->next != (del))) { \ - head = head->next; \ - } \ - if (head->next) { \ - head->next = ((del)->next); \ - } \ - { \ - char **_head_alias = (char**)&(head); \ - *_head_alias = _tmp; \ - } \ - } \ -} while (0) -#ifdef NO_DECLTYPE -#undef LL_APPEND -#define LL_APPEND LL_APPEND_VS2008 -#undef LL_DELETE -#define LL_DELETE LL_DELETE_VS2008 -#endif -/* end VS2008 replacements */ - -#define LL_FOREACH(head,el) \ - for(el=head;el;el=el->next) - -#define LL_FOREACH_SAFE(head,el,tmp) \ - for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) - -#define LL_SEARCH_SCALAR(head,out,field,val) \ -do { \ - LL_FOREACH(head,out) { \ - if ((out)->field == (val)) break; \ - } \ -} while(0) - -#define LL_SEARCH(head,out,elt,cmp) \ -do { \ - LL_FOREACH(head,out) { \ - if ((cmp(out,elt))==0) break; \ - } \ -} while(0) - -/****************************************************************************** - * doubly linked list macros (non-circular) * - *****************************************************************************/ -#define DL_PREPEND(head,add) \ -do { \ - (add)->next = head; \ - if (head) { \ - (add)->prev = (head)->prev; \ - (head)->prev = (add); \ - } else { \ - (add)->prev = (add); \ - } \ - (head) = (add); \ -} while (0) - -#define DL_APPEND(head,add) \ -do { \ - if (head) { \ - (add)->prev = (head)->prev; \ - (head)->prev->next = (add); \ - (head)->prev = (add); \ - (add)->next = NULL; \ - } else { \ - (head)=(add); \ - (head)->prev = (head); \ - (head)->next = NULL; \ - } \ -} while (0); - -#define DL_DELETE(head,del) \ -do { \ - if ((del)->prev == (del)) { \ - (head)=NULL; \ - } else if ((del)==(head)) { \ - (del)->next->prev = (del)->prev; \ - (head) = (del)->next; \ - } else { \ - (del)->prev->next = (del)->next; \ - if ((del)->next) { \ - (del)->next->prev = (del)->prev; \ - } else { \ - (head)->prev = (del)->prev; \ - } \ - } \ -} while (0); - - -#define DL_FOREACH(head,el) \ - for(el=head;el;el=el->next) - -/* this version is safe for deleting the elements during iteration */ -#define DL_FOREACH_SAFE(head,el,tmp) \ - for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) - -/* these are identical to their singly-linked list counterparts */ -#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR -#define DL_SEARCH LL_SEARCH - -/****************************************************************************** - * circular doubly linked list macros * - *****************************************************************************/ -#define CDL_PREPEND(head,add) \ -do { \ - if (head) { \ - (add)->prev = (head)->prev; \ - (add)->next = (head); \ - (head)->prev = (add); \ - (add)->prev->next = (add); \ - } else { \ - (add)->prev = (add); \ - (add)->next = (add); \ - } \ -(head)=(add); \ -} while (0) - -#define CDL_DELETE(head,del) \ -do { \ - if ( ((head)==(del)) && ((head)->next == (head))) { \ - (head) = 0L; \ - } else { \ - (del)->next->prev = (del)->prev; \ - (del)->prev->next = (del)->next; \ - if ((del) == (head)) (head)=(del)->next; \ - } \ -} while (0); - -#define CDL_FOREACH(head,el) \ - for(el=head;el;el=(el->next==head ? 0L : el->next)) - -#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ - for((el)=(head), ((tmp1)=(head)?((head)->prev):NULL); \ - (el) && ((tmp2)=(el)->next, 1); \ - ((el) = (((el)==(tmp1)) ? 0L : (tmp2)))) - -#define CDL_SEARCH_SCALAR(head,out,field,val) \ -do { \ - CDL_FOREACH(head,out) { \ - if ((out)->field == (val)) break; \ - } \ -} while(0) - -#define CDL_SEARCH(head,out,elt,cmp) \ -do { \ - CDL_FOREACH(head,out) { \ - if ((cmp(out,elt))==0) break; \ - } \ -} while(0) - -#endif /* UTLIST_H */ - diff --git a/tommyds/benchmark/lib/uthash/utstring.h b/tommyds/benchmark/lib/uthash/utstring.h deleted file mode 100644 index 277874f..0000000 --- a/tommyds/benchmark/lib/uthash/utstring.h +++ /dev/null @@ -1,137 +0,0 @@ -/* -Copyright (c) 2008-2010, Troy D. Hanson http://uthash.sourceforge.net -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 - * see http://uthash.sourceforge.net/utstring - */ -#ifndef UTSTRING_H -#define UTSTRING_H - -#define UTSTRING_VERSION 1.9.1 - -#ifdef __GNUC__ -#define _UNUSED_ __attribute__ ((__unused__)) -#else -#define _UNUSED_ -#endif - -#include -#include -#include -#define oom() exit(-1) - -typedef struct { - char *d; - size_t n; /* allocd size */ - size_t i; /* index of first unused byte */ -} UT_string; - -#define utstring_reserve(s,amt) \ -do { \ - if (((s)->n - (s)->i) < (size_t)(amt)) { \ - (s)->d = (char*)realloc((s)->d, (s)->n + amt); \ - if ((s)->d == NULL) oom(); \ - (s)->n += amt; \ - } \ -} while(0) - -#define utstring_init(s) \ -do { \ - (s)->n = 0; (s)->i = 0; (s)->d = NULL; \ - utstring_reserve(s,100); \ -} 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*)calloc(sizeof(UT_string),1); \ - if (!s) oom(); \ - utstring_init(s); \ -} while(0) - -#define utstring_clear(s) \ -do { \ - (s)->i = 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) ((unsigned)((s)->i)) - -#define utstring_body(s) ((s)->d) - -_UNUSED_ static void utstring_printf_va(UT_string *s, const char *fmt, va_list ap) { - int n; - va_list cp; - while (1) { -#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) && (n < (int)(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 */ - } -} -_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); -} - -#endif /* UTSTRING_H */ diff --git a/tommyds/check.c b/tommyds/check.c deleted file mode 100644 index 0e0f7c7..0000000 --- a/tommyds/check.c +++ /dev/null @@ -1,1373 +0,0 @@ -/* - * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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. - */ - -/** - * Tommy check program. - * - * Simply run it without any options. If it terminates printing "OK" all the - * checks are succesful. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(__linux) -#include -#include -#include -#endif - -#if defined(__MACH__) -#include -#endif - -#include "tommyds/tommy.h" - -#define TOMMY_SIZE 1000000 - -#define PAYLOAD 16 /**< Size of payload data for objects */ - -struct object { - int value; - tommy_node node; - char payload[PAYLOAD]; -}; - -unsigned compare_counter; - -int compare(const void* void_a, const void* void_b) -{ - const struct object* a = void_a; - const struct object* b = void_b; - - ++compare_counter; - - if (a->value < b->value) - return -1; - if (a->value > b->value) - return 1; - return 0; -} - -struct object_vector { - int value; - char payload[PAYLOAD]; -}; - -int compare_vector(const void* void_a, const void* void_b) -{ - const struct object_vector* a = void_a; - const struct object_vector* b = void_b; - - ++compare_counter; - - if (a->value < b->value) - return -1; - if (a->value > b->value) - return 1; - return 0; -} - -struct object_hash { - int value; - tommy_node node; - char payload[PAYLOAD]; -}; - -struct object_trie { - int value; - tommy_trie_node node; - char payload[PAYLOAD]; -}; - -struct object_trie_inplace { - int value; - tommy_trie_inplace_node node; - char payload[PAYLOAD]; -}; - -/******************************************************************************/ -/* time */ - -#if defined(_WIN32) -static LARGE_INTEGER win_frequency; -#endif - -static void nano_init(void) -{ -#if defined(_WIN32) - if (!QueryPerformanceFrequency(&win_frequency)) { - win_frequency.QuadPart = 0; - } -#endif -} - -static tommy_uint64_t nano(void) -{ - tommy_uint64_t ret; -#if defined(_WIN32) - LARGE_INTEGER t; - - if (!QueryPerformanceCounter(&t)) - return 0; - - ret = (t.QuadPart / win_frequency.QuadPart) * 1000000000; - - ret += (t.QuadPart % win_frequency.QuadPart) * 1000000000 / win_frequency.QuadPart; -#elif defined(__MACH__) - mach_timebase_info_data_t info; - kern_return_t r; - tommy_uint64_t t; - - t = mach_absolute_time(); - - r = mach_timebase_info(&info); - if (r != 0) { - abort(); - } - - ret = (t / info.denom) * info.numer; - - ret += (t % info.denom) * info.numer / info.denom; -#elif defined(__linux) - struct timespec ts; - int r; - - r = clock_gettime(CLOCK_MONOTONIC, &ts); - if (r != 0) { - abort(); - } - - ret = ts.tv_sec * (tommy_uint64_t)1000000000 + ts.tv_nsec; -#else - struct timeval tv; - int r; - - r = gettimeofday(&tv, 0); - if (r != 0) { - abort(); - } - - ret = tv.tv_sec * (tommy_uint64_t)1000000000 + tv.tv_usec * 1000; -#endif - return ret; -} - -/******************************************************************************/ -/* random */ - -/** - * Pseudo random number generator. - * Note that using (rand() % max) in Visual C results in totally bogus values, - * with *strong* cache effects when accessing elements in a not really random order. - * This happen because Visual C uses a simple linear congruential generator with only 32 bits. - */ -tommy_uint64_t SEED = 0; - -unsigned rnd(unsigned max) -{ - unsigned r; - tommy_uint64_t divider; - -loop: - /* linear congruential generator from MMIX by Donald Knuth, http://en.wikipedia.org/wiki/Linear_congruential_generator */ -#ifdef _MSC_VER - divider = 0xFFFFFFFFFFFFFFFF / max; - SEED = SEED * 6364136223846793005 + 1442695040888963407; -#else - divider = 0xFFFFFFFFFFFFFFFFULL / max; - SEED = SEED * 6364136223846793005LL + 1442695040888963407LL; -#endif - - r = (unsigned)(SEED / divider); - - /* it may happen as the divider is approximated down */ - if (r >= max) - goto loop; - - return r; -} - -/******************************************************************************/ -/* helper */ - -unsigned isqrt(unsigned n) -{ - unsigned root, remain, place; - - root = 0; - - remain = n; - - place = 0x40000000; - - while (place > remain) - place /= 4; - - while (place) { - if (remain >= root + place) { - remain -= root + place; - root += 2 * place; - } - - root /= 2; - place /= 4; - } - - return root; -} - -/** - * Cache clearing buffer. - */ -static unsigned char the_cache[16*1024*1024]; -static const char* the_str; -static tommy_uint64_t the_start; - -void cache_clear(void) -{ - unsigned i; - - /* read & write */ - for(i=0;inext) { - const struct object* a = node->data; - const struct object* b = node->next->data; - /* check order */ - if (a->value > b->value) - abort(); - /* check order for stable sort */ - if (a->value == b->value && a > b) - abort(); - } - node = node->next; - } -} - -void test_list(void) -{ - struct object* LIST; - struct object_vector* VECTOR; - tommy_node* list; - unsigned i; - const unsigned size = TOMMY_SIZE; - - LIST = malloc(size * sizeof(struct object)); - VECTOR = malloc(size * sizeof(struct object_vector)); - - for(i=0;idata; - if (obj->value == module - 1) - break; - bucket = bucket->next; - } - if (bucket == 0) - abort(); - - /* deinitialize without removing elements */ - tommy_hashlin_done(&hashlin); - - START("hashlin stack"); - limit = 5 * isqrt(size); - for(n=0;n<=limit;++n) { - /* last iteration is full size */ - if (n == limit) - n = limit = size; - - tommy_hashlin_init(&hashlin); - - /* insert */ - for(i=0;i - diff --git a/tommyds/tommy-header.html b/tommyds/tommy-header.html deleted file mode 100644 index 1e101b3..0000000 --- a/tommyds/tommy-header.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - -TommyDS - - - - - diff --git a/tommyds/tommy.css b/tommyds/tommy.css deleted file mode 100644 index c5bba3d..0000000 --- a/tommyds/tommy.css +++ /dev/null @@ -1,803 +0,0 @@ -/* The standard CSS for doxygen */ - -body, table, div, p, dl { - font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif; - font-size: 12px; -} - -/* @group Heading Levels */ - -h1 { - font-size: 150%; -} - -h2 { - font-size: 120%; -} - -h3 { - font-size: 100%; -} - -dt { - font-weight: bold; -} - -div.multicol { - -moz-column-gap: 1em; - -webkit-column-gap: 1em; - -moz-column-count: 3; - -webkit-column-count: 3; -} - -p.startli, p.startdd, p.starttd { - margin-top: 2px; -} - -p.endli { - margin-bottom: 0px; -} - -p.enddd { - margin-bottom: 4px; -} - -p.endtd { - margin-bottom: 2px; -} - -/* @end */ - -caption { - font-weight: bold; -} - -span.legend { - font-size: 70%; - text-align: center; -} - -h3.version { - font-size: 90%; - text-align: center; -} - -div.qindex, div.navtab{ - background-color: #EBEFF6; - border: 1px solid #A3B4D7; - text-align: center; - margin: 2px; - padding: 2px; -} - -div.qindex, div.navpath { - width: 100%; - line-height: 140%; -} - -div.navtab { - margin-right: 15px; -} - -/* @group Link Styling */ - -a { - color: #3D578C; - font-weight: normal; - text-decoration: none; -} - -.contents a:visited { - color: #4665A2; -} - -a:hover { - text-decoration: underline; -} - -a.qindex { - font-weight: bold; -} - -a.qindexHL { - font-weight: bold; - background-color: #9CAFD4; - color: #ffffff; - border: 1px double #869DCA; -} - -.contents a.qindexHL:visited { - color: #ffffff; -} - -a.el { - font-weight: bold; -} - -a.elRef { -} - -a.code { - color: #4665A2; -} - -a.codeRef { - color: #4665A2; -} - -/* @end */ - -dl.el { - margin-left: -1cm; -} - -.fragment { - font-family: monospace, fixed; - font-size: 105%; -} - -pre.fragment { - border: 1px solid #C4CFE5; - background-color: #FBFCFD; - padding: 4px 6px; - margin: 4px 8px 4px 2px; - overflow: auto; - word-wrap: break-word; - font-size: 9pt; - line-height: 125%; -} - -div.ah { - background-color: black; - font-weight: bold; - color: #ffffff; - margin-bottom: 3px; - margin-top: 3px; - padding: 0.2em; - border: solid thin #333; - border-radius: 0.5em; - -webkit-border-radius: .5em; - -moz-border-radius: .5em; - box-shadow: 2px 2px 3px #999; - -webkit-box-shadow: 2px 2px 3px #999; - -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; - background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); - background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000); -} - -div.groupHeader { - margin-left: 16px; - margin-top: 12px; - font-weight: bold; -} - -div.groupText { - margin-left: 16px; - font-style: italic; -} - -body { - background: white; - color: black; - margin: 0; -} - -div.contents { - margin-top: 10px; - margin-left: 10px; - margin-right: 10px; -} - -td.indexkey { - background-color: #EBEFF6; - font-weight: bold; - border: 1px solid #C4CFE5; - margin: 2px 0px 2px 0; - padding: 2px 10px; -} - -td.indexvalue { - background-color: #EBEFF6; - border: 1px solid #C4CFE5; - padding: 2px 10px; - margin: 2px 0px; -} - -tr.memlist { - background-color: #EEF1F7; -} - -p.formulaDsp { - text-align: center; -} - -img.formulaDsp { - -} - -img.formulaInl { - vertical-align: middle; -} - -div.center { - text-align: center; - margin-top: 0px; - margin-bottom: 0px; - padding: 0px; -} - -div.center img { - border: 0px; -} - -address.footer { - text-align: right; - padding-right: 12px; -} - -img.footer { - border: 0px; - vertical-align: middle; -} - -/* @group Code Colorization */ - -span.keyword { - color: #008000 -} - -span.keywordtype { - color: #604020 -} - -span.keywordflow { - color: #e08000 -} - -span.comment { - color: #800000 -} - -span.preprocessor { - color: #806020 -} - -span.stringliteral { - color: #002080 -} - -span.charliteral { - color: #008080 -} - -span.vhdldigit { - color: #ff00ff -} - -span.vhdlchar { - color: #000000 -} - -span.vhdlkeyword { - color: #700070 -} - -span.vhdllogic { - color: #ff0000 -} - -/* @end */ - -/* -.search { - color: #003399; - font-weight: bold; -} - -form.search { - margin-bottom: 0px; - margin-top: 0px; -} - -input.search { - font-size: 75%; - color: #000080; - font-weight: normal; - background-color: #e8eef2; -} -*/ - -td.tiny { - font-size: 75%; -} - -.dirtab { - padding: 4px; - border-collapse: collapse; - border: 1px solid #A3B4D7; -} - -th.dirtab { - background: #EBEFF6; - font-weight: bold; -} - -hr { - height: 0px; - border: none; - border-top: 1px solid #4A6AAA; -} - -hr.footer { - height: 1px; -} - -/* @group Member Descriptions */ - -table.memberdecls { - border-spacing: 0px; - padding: 0px; -} - -.mdescLeft, .mdescRight, -.memItemLeft, .memItemRight, -.memTemplItemLeft, .memTemplItemRight, .memTemplParams { - background-color: #F9FAFC; - border: none; - margin: 4px; - padding: 1px 0 0 8px; -} - -.mdescLeft, .mdescRight { - padding: 0px 8px 4px 8px; - color: #555; -} - -.memItemLeft, .memItemRight, .memTemplParams { - border-top: 1px solid #C4CFE5; -} - -.memItemLeft, .memTemplItemLeft { - white-space: nowrap; -} - -.memTemplParams { - color: #4665A2; - white-space: nowrap; -} - -/* @end */ - -/* @group Member Details */ - -/* Styles for detailed member documentation */ - -.memtemplate { - font-size: 80%; - color: #4665A2; - font-weight: normal; - margin-left: 9px; -} - -.memnav { - background-color: #EBEFF6; - border: 1px solid #A3B4D7; - text-align: center; - margin: 2px; - margin-right: 15px; - padding: 2px; -} - -.memitem { - padding: 0; - margin-bottom: 10px; -} - -.memname { - white-space: nowrap; - font-weight: bold; - margin-left: 6px; -} - -.memproto { - border-top: 1px solid #A8B8D9; - border-left: 1px solid #A8B8D9; - border-right: 1px solid #A8B8D9; - padding: 6px 0px 6px 0px; - color: #253555; - font-weight: bold; - text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); - /* opera specific markup */ - box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); - border-top-right-radius: 8px; - border-top-left-radius: 8px; - /* firefox specific markup */ - -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; - -moz-border-radius-topright: 8px; - -moz-border-radius-topleft: 8px; - /* webkit specific markup */ - -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); - -webkit-border-top-right-radius: 8px; - -webkit-border-top-left-radius: 8px; - background-image:url('nav_f.png'); - background-repeat:repeat-x; - background-color: #E2E8F2; - -} - -.memdoc { - border-bottom: 1px solid #A8B8D9; - border-left: 1px solid #A8B8D9; - border-right: 1px solid #A8B8D9; - padding: 2px 5px; - background-color: #FBFCFD; - border-top-width: 0; - /* opera specific markup */ - border-bottom-left-radius: 8px; - border-bottom-right-radius: 8px; - box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); - /* firefox specific markup */ - -moz-border-radius-bottomleft: 8px; - -moz-border-radius-bottomright: 8px; - -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; - background-image: -moz-linear-gradient(center top, #FFFFFF 0%, #FFFFFF 60%, #F7F8FB 95%, #EEF1F7); - /* webkit specific markup */ - -webkit-border-bottom-left-radius: 8px; - -webkit-border-bottom-right-radius: 8px; - -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); - background-image: -webkit-gradient(linear,center top,center bottom,from(#FFFFFF), color-stop(0.6,#FFFFFF), color-stop(0.60,#FFFFFF), color-stop(0.95,#F7F8FB), to(#EEF1F7)); -} - -.paramkey { - text-align: right; -} - -.paramtype { - white-space: nowrap; -} - -.paramname { - color: #602020; - white-space: nowrap; -} -.paramname em { - font-style: normal; -} - -.params, .retval, .exception, .tparams { - border-spacing: 6px 2px; -} - -.params .paramname, .retval .paramname { - font-weight: bold; - vertical-align: top; -} - -.params .paramtype { - font-style: italic; - vertical-align: top; -} - -.params .paramdir { - font-family: "courier new",courier,monospace; - vertical-align: top; -} - - - - -/* @end */ - -/* @group Directory (tree) */ - -/* for the tree view */ - -.ftvtree { - font-family: sans-serif; - margin: 0px; -} - -/* these are for tree view when used as main index */ - -.directory { - font-size: 9pt; - font-weight: bold; - margin: 5px; -} - -.directory h3 { - margin: 0px; - margin-top: 1em; - font-size: 11pt; -} - -/* -The following two styles can be used to replace the root node title -with an image of your choice. Simply uncomment the next two styles, -specify the name of your image and be sure to set 'height' to the -proper pixel height of your image. -*/ - -/* -.directory h3.swap { - height: 61px; - background-repeat: no-repeat; - background-image: url("yourimage.gif"); -} -.directory h3.swap span { - display: none; -} -*/ - -.directory > h3 { - margin-top: 0; -} - -.directory p { - margin: 0px; - white-space: nowrap; -} - -.directory div { - display: none; - margin: 0px; -} - -.directory img { - vertical-align: -30%; -} - -/* these are for tree view when not used as main index */ - -.directory-alt { - font-size: 100%; - font-weight: bold; -} - -.directory-alt h3 { - margin: 0px; - margin-top: 1em; - font-size: 11pt; -} - -.directory-alt > h3 { - margin-top: 0; -} - -.directory-alt p { - margin: 0px; - white-space: nowrap; -} - -.directory-alt div { - display: none; - margin: 0px; -} - -.directory-alt img { - vertical-align: -30%; -} - -/* @end */ - -div.dynheader { - margin-top: 8px; -} - -address { - font-style: normal; - color: #2A3D61; -} - -table.doxtable { - border-collapse:collapse; -} - -table.doxtable td, table.doxtable th { - border: 1px solid #2D4068; - padding: 3px 7px 2px; -} - -table.doxtable th { - background-color: #374F7F; - color: #FFFFFF; - font-size: 110%; - padding-bottom: 4px; - padding-top: 5px; - text-align:left; -} - -.tabsearch { - top: 0px; - left: 10px; - height: 36px; - background-image: url('tab_b.png'); - z-index: 101; - overflow: hidden; - font-size: 13px; -} - -.navpath ul -{ - font-size: 11px; - background-image:url('tab_b.png'); - background-repeat:repeat-x; - height:30px; - line-height:30px; - color:#8AA0CC; - border:solid 1px #C2CDE4; - overflow:hidden; - margin:0px; - padding:0px; -} - -.navpath li -{ - list-style-type:none; - float:left; - padding-left:10px; - padding-right: 15px; - background-image:url('bc_s.png'); - background-repeat:no-repeat; - background-position:right; - color:#364D7C; -} - -.navpath a -{ - height:32px; - display:block; - text-decoration: none; - outline: none; -} - -.navpath a:hover -{ - color:#6884BD; -} - -div.summary -{ - float: right; - font-size: 8pt; - padding-right: 5px; - width: 50%; - text-align: right; -} - -div.summary a -{ - white-space: nowrap; -} - -div.header -{ - background-image:url('nav_h.png'); - background-repeat:repeat-x; - background-color: #F9FAFC; - margin: 0px; - border-bottom: 1px solid #C4CFE5; -} - -div.headertitle -{ - padding: 5px 5px 5px 10px; -} - -/* font size */ -body, table, div, p, dl { - font-size: 18px; -} - -pre.fragment { - font-size: 12pt; -} - -h1 { - font-size: 200%; - font-weight: bold; - color: #283a5d; -} - -h2, h2.a { - font-size: 150%; - font-weight: bold; - color: #283a5d; -} - -/* tab bar */ -.tablist a { - text-shadow: none; - background-image: none; - background-color: #ebeff6; - text-decoration: none; -} - -.tablist a:hover { - text-shadow: none; - background-image: none; - text-decoration: underline; - color: #283a5d; - background-color: #ebeff6; -} - -.tablist li { - background-image: none; - background-color: #ebeff6; -} - -.tablist li.current a { - text-shadow: none; - background-image: none; - background-color: #4665a2; - text-decoration: none; -} - -.tabs { - font-size: 20px; - background-image: none; - background-color: #ebeff6; - border-bottom: 1px solid #C4CFE5; -} - -.tabs2 { - font-size: 18px; - background-image: none; - background-color: #ebeff6; - border-bottom: 1px solid #C4CFE5; -} - -.tabs3 { - font-size: 16px; - background-image: none; - background-color: #ebeff6; - border-bottom: 1px solid #C4CFE5; -} - -div.header { - background-image:none; -} - -/* padding */ - -p, .fragment, .textblock, table { - padding-left: 40px; - padding-right: 40px; -} - -ul { - padding-left: 80px; - padding-right: 40px; -} - -table.memberdecls { - padding-left: 20px; -} - -h1, h2, h3 { - padding-left: 20px; -} - -/* padding inside .memitem */ - -.memitem { - padding-left: 20px; - padding-right: 20px; -} - -.memitem p, .memitem .fragment, dl { - padding-left: 20px; - padding-right: 20px; -} - -.memname { - padding-left: 0px; - padding-right: 0px; -} - -/* padding inside .memberdecls */ - -.memberdecls h2 { - padding-left: 0px; -} - diff --git a/tommyds/tommy.doxygen b/tommyds/tommy.doxygen deleted file mode 100644 index 5b32fa1..0000000 --- a/tommyds/tommy.doxygen +++ /dev/null @@ -1,1671 +0,0 @@ -# Doxyfile 1.7.2 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project. -# -# All text after a hash (#) is considered a comment and will be ignored. -# The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" "). - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. - -PROJECT_NAME = Tommy - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. - -PROJECT_NUMBER = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = doc - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, -# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English -# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, -# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, -# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" - -ABBREVIATE_BRIEF = - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = YES - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. - -FULL_PATH_NAMES = YES - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful if your file system -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) - -JAVADOC_AUTOBRIEF = YES - -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 8 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = YES - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified -# scopes will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for -# Fortran. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for -# VHDL. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given extension. -# Doxygen has a built-in mapping, but you can override or extend it using this -# tag. The format is ext=language, where ext is a file extension, and language -# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, -# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make -# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C -# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions -# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. - -EXTENSION_MAPPING = - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also makes the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. -# Doxygen will parse them like normal C++ but will assume all classes use public -# instead of private inheritance when no explicit protection keyword is present. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate getter -# and setter methods for a property. Setting this option to YES (the default) -# will make doxygen replace the get and set methods by a property in the -# documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the -# methods anyway, you should set this option to NO. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. - -TYPEDEF_HIDES_STRUCT = NO - -# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to -# determine which symbols to keep in memory and which to flush to disk. -# When the cache is full, less often used symbols will be written to disk. -# For small to medium size projects (<1000 input files) the default value is -# probably good enough. For larger projects a too small cache size can cause -# doxygen to be busy swapping symbols to and from disk most of the time -# causing a significant performance penality. -# If the system has enough physical memory increasing the cache will improve the -# performance by keeping more symbols in memory. Note that the value works on -# a logarithmic scale so increasing the size by one will roughly double the -# memory usage. The cache size is given by this formula: -# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols - -SYMBOL_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -EXTRACT_STATIC = YES - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespaces are hidden. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_MEMBERS = YES - -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_CLASSES = YES - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = NO - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen -# will list include files with double quotes in the documentation -# rather than with sharp brackets. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. - -SORT_MEMBER_DOCS = NO - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. - -SORT_BRIEF_DOCS = NO - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen -# will sort the (brief and detailed) documentation of class members so that -# constructors and destructors are listed first. If set to NO (the default) -# the constructors will appear in the respective orders defined by -# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. -# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO -# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) -# the group names will appear in their defined order. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. - -SORT_BY_SCOPE_NAME = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. - -GENERATE_TODOLIST = NO - -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. - -GENERATE_TESTLIST = NO - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = NO - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST= NO - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or macro consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and macros in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. - -SHOW_USED_FILES = YES - -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = NO - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. -# This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. The create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. -# You can optionally specify a file name after the option, if omitted -# DoxygenLayout.xml will be used as the name of the layout file. - -LAYOUT_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. - -WARNINGS = YES - -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. - -WARN_IF_UNDOCUMENTED = YES - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# The WARN_NO_PARAMDOC option can be enabled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. - -WARN_NO_PARAMDOC = YES - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. - -INPUT = tommyds - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for -# the list of possible encodings. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh -# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py -# *.f90 *.f *.vhd *.vhdl - -FILE_PATTERNS = tommy.h \ - tommyalloc.h \ - tommyarray.h \ - tommyarrayof.h \ - tommyarrayblk.h \ - tommyarrayblkof.h \ - tommyhash.h \ - tommyhashdyn.h \ - tommyhashlin.h \ - tommyhashtbl.h \ - tommyhashtrie.h \ - tommylist.h \ - tommytrie.h \ - tommytrieinp.h \ - tommytypes.h - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -RECURSIVE = NO - -# The EXCLUDE tag can be used to specify files and/or directories that should -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix filesystem feature) are excluded -# from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = . - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. -# If FILTER_PATTERNS is specified, this tag will be -# ignored. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. -# Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. -# The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER -# is applied to all files. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented -# functions referencing it will be listed. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. -# Otherwise they will link to the documentation. - -REFERENCES_LINK_SOURCE = NO - -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = NO - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = tommy_ - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = . - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. - -HTML_HEADER = tommy-header.html - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = tommy-footer.html - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! - -HTML_STYLESHEET = tommy.css - -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. -# Doxygen will adjust the colors in the stylesheet and background images -# according to this color. Hue is specified as an angle on a colorwheel, -# see http://en.wikipedia.org/wiki/Hue for more information. -# For instance the value 0 represents red, 60 is yellow, 120 is green, -# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. -# The allowed range is 0 to 359. - -HTML_COLORSTYLE_HUE = 220 - -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of -# the colors in the HTML output. For a value of 0 the output will use -# grayscales only. A value of 255 will produce the most vivid colors. - -HTML_COLORSTYLE_SAT = 100 - -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to -# the luminance component of the colors in the HTML output. Values below -# 100 gradually make the output lighter, whereas values above 100 make -# the output darker. The value divided by 100 is the actual gamma applied, -# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, -# and 100 does not change the gamma. - -HTML_COLORSTYLE_GAMMA = 80 - -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting -# this to NO can help when comparing the output of multiple runs. - -HTML_TIMESTAMP = NO - -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). - -HTML_DYNAMIC_SECTIONS = NO - -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. -# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. - -GENERATE_DOCSET = NO - -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify -# the documentation publisher. This should be a reverse domain-name style -# string, e.g. com.mycompany.MyDocSet.documentation. - -DOCSET_PUBLISHER_ID = org.doxygen.Publisher - -# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. - -DOCSET_PUBLISHER_NAME = Publisher - -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output directory. - -CHM_FILE = - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = - -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). - -GENERATE_CHI = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. - -CHM_INDEX_ENCODING = - -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated -# that can be used as input for Qt's qhelpgenerator to generate a -# Qt Compressed Help (.qch) of the generated HTML documentation. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. -# The path specified is relative to the HTML output folder. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace - -QHP_NAMESPACE = org.doxygen.Project - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#virtual-folders - -QHP_VIRTUAL_FOLDER = doc - -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to -# add. For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see -# -# Qt Help Project / Custom Filters. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's -# filter section matches. -# -# Qt Help Project / Filter Attributes. - -QHP_SECT_FILTER_ATTRS = - -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files -# will be generated, which together with the HTML files, form an Eclipse help -# plugin. To install this plugin and make it available under the help contents -# menu in Eclipse, the contents of the directory containing the HTML and XML -# files needs to be copied into the plugins directory of eclipse. The name of -# the directory within the plugins directory should be the same as -# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before -# the help appears. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have -# this name. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. - -DISABLE_INDEX = NO - -# This tag can be used to set the number of enum values (range [0,1..20]) -# that doxygen will group on one line in the generated HTML documentation. -# Note that a value of 0 will completely suppress the enum values from appearing in the overview section. - -ENUM_VALUES_PER_LINE = 4 - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to YES, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). -# Windows users are probably better off using the HTML help feature. - -GENERATE_TREEVIEW = NO - -# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, -# and Class Hierarchy pages using a tree view instead of an ordered list. - -USE_INLINE_TREES = NO - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open -# links to external symbols imported via tag files in a separate window. - -EXT_LINKS_IN_WINDOW = NO - -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. - -FORMULA_FONTSIZE = 10 - -# Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are -# not supported properly for IE 6.0, but are supported on all modern browsers. -# Note that when changing this option you need to delete any form_*.png files -# in the HTML output before the changes have effect. - -FORMULA_TRANSPARENT = YES - -# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax -# (see http://www.mathjax.org) which uses client side Javascript for the -# rendering instead of using prerendered bitmaps. Use this if you do not -# have LaTeX installed or if you want to formulas look prettier in the HTML -# output. When enabled you also need to install MathJax separately and -# configure the path to it using the MATHJAX_RELPATH option. - -USE_MATHJAX = NO - -# When MathJax is enabled you need to specify the location relative to the -# HTML output directory using the MATHJAX_RELPATH option. The destination -# directory should contain the MathJax.js script. For instance, if the mathjax -# directory is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to the mathjax.org site, so you can quickly see the result without installing -# MathJax, but it is strongly recommended to install a local copy of MathJax -# before deployment. - -MATHJAX_RELPATH = http://www.mathjax.org/mathjax - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box -# for the HTML output. The underlying search engine uses javascript -# and DHTML and should work on any modern browser. Note that when using -# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets -# (GENERATE_DOCSET) there is already a search function so this one should -# typically be disabled. For large projects the javascript based search engine -# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. - -SEARCHENGINE = NO - -# When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a PHP enabled web server instead of at the web client -# using Javascript. Doxygen will generate the search PHP script and index -# file to put on the web server. The advantage of the server -# based approach is that it scales better to large projects and allows -# full text search. The disadvantages are that it is more difficult to setup -# and does not have live searching capabilities. - -SERVER_BASED_SEARCH = NO - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = NO - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. -# Note that when enabling USE_PDFLATEX this option is only used for -# generating bitmaps for formulas in the HTML output, but not in the -# Makefile that is written to the output directory. - -LATEX_CMD_NAME = latex - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = makeindex - -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, letter, legal and -# executive. If left blank a4wide will be used. - -PAPER_TYPE = a4wide - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = YES - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = YES - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = NO - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -# If LATEX_SOURCE_CODE is set to YES then doxygen will include -# source code with syntax highlighting in the LaTeX output. -# Note that which sources are shown also depends on other settings -# such as SOURCE_BROWSER. - -LATEX_SOURCE_CODE = NO - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with -# other RTF readers or editors. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -RTF_HYPERLINKS = NO - -# Load stylesheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide -# replacements, missing definitions are set to their default value. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = NO - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command -# would be unable to find the correct page. The default is NO. - -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- - -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of -# the code including all documentation. - -GENERATE_XML = NO - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that -# enabling this will significantly increase the size of the XML output. - -XML_PROGRAMLISTING = YES - -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- - -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental -# and incomplete at the moment. - -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- - -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able -# to generate PDF and DVI output from the Perl module output. - -PERLMOD_LATEX = NO - -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. -# This is useful -# if you want to understand what is going on. -# On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = YES - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_DEFINED tags. - -EXPAND_ONLY_PREDEF = YES - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# in the INCLUDE_PATH (see below) will be search if a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator -# instead of the = operator. - -PREDEFINED = "tommy_inline=" \ - "tommy_restrict=" - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all function-like macros that are alone -# on a line, have an all uppercase name, and do not end with a semicolon. Such -# function macros are typically used for boiler-plate code, and will confuse -# the parser if not removed. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen -# is run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -ALLEXTERNALS = NO - -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will -# be listed. - -EXTERNAL_GROUPS = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option also works with HAVE_DOT disabled, but it is recommended to -# install and use dot, since it yields more powerful graphs. - -CLASS_DIAGRAMS = YES - -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see -# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented -# or is not a class. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = NO - -# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is -# allowed to run in parallel. When set to 0 (the default) doxygen will -# base this on the number of processors available in the system. You can set it -# explicitly to a value larger than 0 to get control over the balance -# between CPU load and processing speed. - -DOT_NUM_THREADS = 0 - -# By default doxygen will write a font called FreeSans.ttf to the output -# directory and reference it in all dot files that doxygen generates. This -# font does not include all possible unicode characters however, so when you need -# these (or just want a differently looking font) you can specify the font name -# using DOT_FONTNAME. You need need to make sure dot is able to find the font, -# which can be done by putting it in a standard location or by setting the -# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory -# containing the font. - -DOT_FONTNAME = FreeSans - -# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. -# The default size is 10pt. - -DOT_FONTSIZE = 10 - -# By default doxygen will tell dot to use the output directory to look for the -# FreeSans.ttf font (which doxygen will put there itself). If you specify a -# different font using DOT_FONTNAME you can set the path where dot -# can find it using this tag. - -DOT_FONTPATH = - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies - -GROUP_GRAPHS = YES - -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling -# Language. - -UML_LOOK = NO - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = NO - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH and HAVE_DOT options are set to YES then -# doxygen will generate a call dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable call graphs -# for selected functions only using the \callgraph command. - -CALL_GRAPH = NO - -# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then -# doxygen will generate a caller dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable caller -# graphs for selected functions only using the \callergraph command. - -CALLER_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will generate a graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are png, jpg, or gif. -# If left blank png will be used. - -DOT_IMAGE_FORMAT = png - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. - -DOT_PATH = - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# The MSCFILE_DIRS tag can be used to specify one or more directories that -# contain msc files that are included in the documentation (see the -# \mscfile command). - -MSCFILE_DIRS = - -# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of -# nodes that will be shown in the graph. If the number of nodes in a graph -# becomes larger than this value, doxygen will truncate the graph, which is -# visualized by representing a node as a red box. Note that doxygen if the -# number of direct children of the root node in a graph is already larger than -# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note -# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. - -DOT_GRAPH_MAX_NODES = 50 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that the size of a graph can be further restricted by -# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not -# seem to support this out of the box. Warning: Depending on the platform used, -# enabling this option may lead to badly anti-aliased labels on the edges of -# a graph (i.e. they become hard to read). - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) -# support this, this feature is disabled by default. - -DOT_MULTI_TARGETS = YES - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES diff --git a/tommyds/tommyds/tommy.c b/tommyds/tommyds/tommy.c deleted file mode 100644 index cbb0407..0000000 --- a/tommyds/tommyds/tommy.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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 "tommyhash.c" -#include "tommyalloc.c" -#include "tommyarray.c" -#include "tommyarrayof.c" -#include "tommyarrayblk.c" -#include "tommyarrayblkof.c" -#include "tommylist.c" -#include "tommytrie.c" -#include "tommytrieinp.c" -#include "tommyhashtbl.c" -#include "tommyhashdyn.c" -#include "tommyhashlin.c" - diff --git a/tommyds/tommyds/tommy.h b/tommyds/tommyds/tommy.h deleted file mode 100644 index b92863f..0000000 --- a/tommyds/tommyds/tommy.h +++ /dev/null @@ -1,770 +0,0 @@ -/* - * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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. - */ - -/** \mainpage - * \section Introduction - * Tommy is a C library of array, hashtables and tries data structures, - * designed for high performance and providing an easy to use interface. - * - * It's faster than all the similar libraries like - * rbtree, - * judy, - * googlebtree, - * stxbtree, - * khash, - * uthash, - * nedtrie, - * judyarray, - * concurrencykit and others. - * Only googledensehash is a real competitor for Tommy. - * - * The data structures provided are: - * - * - ::tommy_list - A double linked list. - * - ::tommy_array, ::tommy_arrayof - A linear array. - * It doesn't fragment the heap. - * - ::tommy_arrayblk, ::tommy_arrayblkof - A blocked linear array. - * It doesn't fragment the heap and it minimizes the space occupation. - * - ::tommy_hashtable - A fixed size chained hashtable. - * - ::tommy_hashdyn - A dynamic chained hashtable. - * - ::tommy_hashlin - A linear chained hashtable. - * It doesn't have the problem of the delay when resizing and - * it doesn't fragment the heap. - * - ::tommy_trie - A trie optimized for cache utilization. - * - ::tommy_trie_inplace - A trie completely inplace. - * - * The most interesting are ::tommy_array, ::tommy_hashdyn, ::tommy_hashlin, ::tommy_trie and ::tommy_trie_inplace. - * - * The official site of TommyDS is http://tommyds.sourceforge.net/, - * - * \section Use - * - * All the Tommy containers are used to store pointers to generic objects, associated to an - * integer value, that could be a key or a hash value. - * - * They are semantically equivalent at the C++ multimap\ - * and unordered_multimap\. - * - * An object, to be inserted in a container, should contain a node of type ::tommy_node. - * Inside this node is present a pointer to the object itself in the tommy_node::data field, - * the key used to identify the object in the tommy_node::key field, and other fields used - * by the containers. - * - * This is a typical object declaration: - * \code - * struct object { - * tommy_node node; - * // other fields - * }; - * \endcode - * - * To insert an object in a container, you have to provide the address of the embedded node, - * the address of the object and the value of the key. - * \code - * int key_to_insert = 1; - * struct object* obj = malloc(sizeof(struct object)); - * ... - * tommy_trie_insert(..., &obj->node, obj, key_to_insert); - * \endcode - * - * To search an object you have to provide the key and call the search function. - * \code - * int key_to_find = 1; - * struct object* obj = tommy_trie_search(..., key_to_find); - * if (obj) { - * // found - * } - * \endcode - * - * To access all the objects with the same keys you have to iterate over the bucket - * assigned at the specified key. - * \code - * int key_to_find = 1; - * tommy_trie_node* i = tommy_trie_bucket(..., key_to_find); - * - * while (i) { - * struct object* obj = i->data; // gets the object pointer - * - * printf("%d\n", obj->value); // process the object - * - * i = i->next; // goes to the next element - * } - * \endcode - * - * To remove an object you have to provide the key and call the remove function. - * \code - * int key_to_remove = 1; - * struct object* obj = tommy_trie_remove(..., key_to_remove); - * if (obj) { - * // found - * free(obj); // frees the object allocated memory - * } - * \endcode - * - * Dealing with hashtables, instead of the key, you have to provide the hash value of the object, - * and a compare function able to differentiate objects with the same hash value. - * To compute the hash value, you can use the generic tommy_hash_u32() function, or the - * specialized integer hash function tommy_inthash_u32(). - * - * \section Features - * - * Tommy is fast and easy to use. - * - * Tommy is portable to all platforms and operating systems. - * - * Tommy containers support multiple elements with the same key. - * - * Tommy containers keep the original insertion order of elements with equal keys. - * - * Tommy is released with the \ref license "2-clause BSD license". - * - * See the \ref design page for more details and limitations. - * - * \section Performance - * Here you can see some timings comparing with other notable implementations. - * The Hit graph shows the time required for searching random objects with a key. - * The Change graph shows the time required for searching, removing and reinsert random objects - * with a different key value. - * - * Times are expressed in nanoseconds for each element, and lower is better. - * - * To have some reference numbers, you can check Latency numbers every programmer should know. - * - * A complete analysis is available in the \ref benchmark page. - * - * - * - * - * - * \page benchmark Tommy Benchmarks - * - * To evaluate Tommy performances, an extensive benchmark was done, - * comparing it to the best libraries of data structures available: - * - * Specifically we test: - * - ::tommy_hashtable - Fixed size chained hashtable. - * - ::tommy_hashdyn - Dynamic chained hashtable. - * - ::tommy_hashlin - Linear chained hashtable. - * - ::tommy_trie - Trie optimized for cache usage. - * - ::tommy_trie_inplace - Trie completely inplace. - * - rbtree - Red-black tree by Jason Evans. - * - nedtrie - Binary trie inplace by Niall Douglas. - * - khash - Dynamic open addressing hashtable by Attractive Chaos. - * - uthash - Dynamic chaining hashtable by Troy D. Hanson. - * - judy - Burst trie (JudyL) by Doug Baskins. - * - judyarray - Burst trie by Karl Malbrain. - * - googledensehash - Dynamic open addressing hashtable by Craig Silverstein at Google. - * - googlebtree - Btree by Google. - * - stxbtree - STX Btree by Timo Bingmann. - * - c++unordered_map - C++ STL unordered_map<> template. - * - c++map - C++ STL map<> template. - * - tesseract - Binary Search Tesseract by Gregorius van den Hoven. - * - googlelibchash - LibCHash by Craig Silverstein at Google. - * - concurrencykit - Non-blocking hash set by Samy Al Bahra. - * - * Note that googlelibchash and concurrencykit are not shown in the graphs - * because they present a lot of spikes. See the \ref notes the end. - * - * \section thebenchmark The Benchmark - * - * The benchmark consists in storing a set of N pointers to objects and - * searching them using integer keys. - * - * Compared to the case of mapping integers to integers, mapping pointers to objects means that - * the pointers are also dereferenced, to simulate the object access, - * resulting in additional cache misses. - * This gives an advantage to implementations that store information in the objects itself, - * as the additional cache misses are already implicit. - * - * The test done are: - * - Insert Insert all the objects starting with an empty container. - * - Change Find and remove one object and reinsert it with a different key, repeated for all the objects. - * - Hit Find with success all the objects and dereference them. - * - Miss Find with failure all the objects. - * - Remove Remove all the objects and dereference them. - * - * The Change, Hit and Miss tests operate always with N - * objects in the containers. - * The Insert test starts with an empty container, and the Remove - * test ends with an empty container. - * The objects are always dereferenced, as we are supposing to use them. This - * happens even in the remove case, as we are supposing to deallocate them. - * - * All the objects are preallocated in the heap, and this allocation time is not - * included in the test. - * - * The objects contain an integer value field used for consistency checks, - * an unused payload field of 16 bytes, and any other data required by the - * data structure. - * - * The objects are identified and stored using integer and unique keys. - * The key domain used is dense, and it's defined by the set - * of N even numbers starting from 0x80000000 to 0x80000000+2*N. - * - * The use of even numbers allows to have missing keys inside the domain for - * the Change test. - * In such tests it's used the key domain defined by the set of N odd numbers - * starting from 0x80000000+1 to 0x80000000+2*N+1. - * Note that using additional keys at the corners of the domain would have given - * an unfair advantage to tries and trees as they implicitly keep track of the - * maximum and minimum key values inserted. - * - * The use of the 0x80000000 base, allow to test a key domain not necessarily - * starting at 0. Using a 0 base would have given an unfair advantage to some - * implementation handling it as a special case. - * - * For all the hashtables the keys are hashed using the tommy_inthash_u32() function - * that ensures an uniform distribution. This hash function is also reversible, - * meaning that no collision is going to be caused by hashing the keys. - * For tries and trees the keys are not hashed, and used directly. - * - * The tests are repeated using keys in Random mode and in Forward mode. - * In the forward mode the key values are used in order from the lowest to the highest. - * In the random mode the key values are used in a completely random order. - * In the Change test in forward mode, each object is reinserted using the previous - * key incremented by 1. In random mode each object is reinserted using a completely - * different and uncorrelated key. - * - * The forward order advantages tries and trees as they use the key directly and they have a - * cache advantage on using consecutive keys. - * The random order advantages hashtables, as the hash function already randomizes the key. - * Usually real uses case are in between, and the random one is the worst. - * - * \section result Results - * - * The most significant tests depend on your data usage model, but if in doubt, - * you can look at Random Hit and Random Change. - * They represent the real world worst condition. - * - * - * - * In the Random Hit graph you can see a vertical split at the 100.000 elements limit. - * Before this limit the cache of modern processor is able to contains most of the data, and it allow a very fast access with almost all data structures. - * After this limit, the number of cache misses is the dominant factor, and the curve depends mainly on the number of cache-miss - * required. - * - * For rbtree and nedtrie, it's log2(N) as they have two branches on each node, log4(N) for ::tommy_trie_inplace, log8(N) for ::tommy_trie and 1 for hashtables. - * For ::tommy_trie_inplace and ::tommy_trie you can change the slope configuring a different number of branches for node. - * - * - * - * The Random Change graph confirms the vertical split at the 100.000 elements limit. - * It also show that hashtables are almost unbeatable with a random access. - * - * \section random Random order - * Here you can see the whole Random test results in different platforms. - * - * In the Random test, hashtables are almost always winning, seconds are - * tries, and as last trees. - * - * The best choices are ::tommy_hashdyn, ::tommy_hashlin, and googledensehash, with - * ::tommy_hashlin having the advantage to be real-time friendly and not - * increasing the heap fragmentation. - * - * - *
- * - * - * - *
- * - * - * - *
- * - * - * - *
- * - * - * - *
- * - * - * - *
- * - * \section forward Forward order - * Here you can see the whole Forward test results in different platforms. - * - * In the Forward test, tries are the winners. Hashtables are competitive - * until the cache limit, then they lose against tries. Trees are the slowest. - * - * The best choices are ::tommy_trie and ::tommy_trie_inplace, where ::tommy_trie is - * a bit faster, and ::tommy_trie_inplace doesn't require a custom allocator. - * - * Note that also hashtables are faster in forward order than random. This may - * seem a bit surprising as the hash function randomizes the access even with - * consecutive keys. This happens because the objects are allocated in consecutive - * memory, and accessing them in order, improves the cache utilization, even if - * the hashed key is random. - * - * Note that you can make hashtables to reach tries performance tweaking - * the hash function to put near keys allocated nearby. - * This is possible if you know in advance the distribution of keys. - * For example, in the benchmark you could use something like: - * \code - * #define hash(v) tommy_inthash_u32(v & ~0xF) + (v & 0xF) - * \endcode - * and make keys that differ only by the lowest bits - * to have hashes with the same property, resulting in - * objects stored nearby, and improving cache utilization. - * - * - * - *
- * - * - * - *
- * - * - * - *
- * - * - * - *
- * - * - * - *
- * - * - * - *
- * - * \section size Size - * Here you can see the memory usage of the different data structures. - * - * - *
- * - * - * - *
- * - * \section code Code - * - * The compilers used in the benchmark are: - * - gcc 4.9.2 in Linux with options: -O3 -march=nehalem - * - Visual C 2012 in Windows with options: /Ox /Oy- /GL /GS- /arch:SSE2 - * - * The following is pseudo code of the benchmark used. In this case it's written for the C++ unordered_map. - * - * \code - * #define N 10000000 // Number of elements - * #define PAYLOAD 16 // Size of the object - * - * // Basic object inserted in the colletion - * struct obj { - * unsigned value; // Key used for searching - * char payload[PAYLOAD]; - * }; - * - * // Custom hash function to avoid to use the STL one - * class custom_hash { - * public: - * unsigned operator()(unsigned key) const { return tommy_inthash_u32(key); } - * }; - * - * // Map collection from "unsigned" to "pointer to object" - * typedef std::unordered_map bag_t; - * bag_t bag; - * - * // Preallocate objects - * obj* OBJ = new obj[N]; - * - * // Keys used for inserting and searching elements - * unsigned INSERT[N]; - * unsigned SEARCH[N]; - * - * // Initialize the keys - * for(i=0;ivalue = key; - * - * // Insert it - * bag[key] = element; - * } - * \endcode - * - * \subsection change Change benchmark - * \code - * for(i=0;isecond; - * bag.erase(j); - * - * // Reinsert the element with a new key - * // Use +1 in the key to ensure that the new key is unique - * key = INSERT[i] + 1; - * element->value = key; - * bag[key] = element; - * } - * \endcode - * - * \subsection hit Hit benchmark - * \code - * for(i=0;isecond; - * if (element->value != key) - * abort(); - * } - * \endcode - * - * \subsection miss Miss benchmark - * \code - * for(i=0;isecond; - * if (element->value != key) - * abort(); - * } - * \endcode - * - * \section others Other benchmarks - * Here some links to other performance comparison: - * - * Comparison of Hash Table Libraries - * - * Hash Table Benchmarks - * - * \section notes Notes - * - * Here some notes about the data structure tested not part of Tommy. - * - * \subsection googlelibchash Google C libchash - * It's the C implementation located in the experimental/ directory of the googlesparsehash archive. - * It has very bad performances in the Change test for some N values. - * See this graph with a lot of spikes. - * The C++ version doesn't suffer of this problem. - * - * \subsection googledensehash Google C++ densehash - * It doesn't release memory on deletion. - * To avoid an unfair advantage in the Remove test, we force a periodic - * resize calling resize(0) after any deallocation. - * The resize is executed when the load factor is lower than 20%. - * - * \subsection khash khash - * It doesn't release memory on deletion. This gives an unfair advantage on the Remove test. - * - * \subsection nedtrie nedtrie - * I've found a crash bug when inserting keys with the 0 value. - * The fix of this issue is now in the nedtries github. - * We do not use the C++ implementation as it doesn't compile with gcc 4.4.3. - * - * \subsection judy Judy - * Sometimes it has bad performances in some specific platform - * and for some specific input data size. - * This makes difficult to predict the performance, as it is usually good until - * you get one of these cases. - * See for example this graph with a big replicable spike at 50.000 elements. - * - * \subsection ck Concurrency Kit - * It has very bad performances in the Change test for some N values. - * See this graph with a lot of spikes. - * - * \page multiindex Tommy Multi Indexing - * - * Tommy provides only partial iterator support with the "foreach" functions. - * If you need real iterators you have to insert all the objects also in a ::tommy_list, - * and use the list as iterator. - * - * This technique allows to keep track of the insertion order with the list, - * and provides more search possibilities using different data structures for - * different search keys. - * - * See the next example, for a objects inserted in a ::tommy_list, and in - * two ::tommy_hashdyn using different keys. - * - * \code - * struct object { - * // data fields - * int value_0; - * int value_1; - * - * // for containers - * tommy_node list_node; // node for the list - * tommy_node hash_node_0; // node for the first hash - * tommy_node hash_node_1; // node for the second hash - * }; - * - * // search function for value_1 - * int search_1(const void* arg, const void* obj) - * { - * return *(const int*)arg != ((const struct object*)obj)->value_1; - * } - * - * tommy_hashdyn hash_0; - * tommy_hashdyn hash_1; - * tommy_list list; - * - * // initializes the hash tables and the list - * tommy_hashdyn_init(&hash_0); - * tommy_hashdyn_init(&hash_1); - * tommy_list_init(&list); - * - * ... - * - * // creates an object and inserts it - * struct object* obj = malloc(sizeof(struct object)); - * obj->value_0 = ...; - * obj->value_1 = ...; - * // inserts in the first hash table - * tommy_hashdyn_insert(&hash_0, &obj->hash_node_0, obj, tommy_inthash_u32(obj->value_0)); - * // inserts in the second hash table - * tommy_hashdyn_insert(&hash_1, &obj->hash_node_1, obj, tommy_inthash_u32(obj->value_1)); - * // inserts in the list - * tommy_list_insert_tail(&list, &obj->list_node, obj); - * - * ... - * - * // searches an object by value_1 and deletes it - * int value_to_find = ...; - * struct object* obj = tommy_hashdyn_search(&hash_1, search_1, &value_to_find, tommy_inthash_u32(value_to_find)); - * if (obj) { - * // if found removes all the references - * tommy_hashdyn_remove_existing(&hash_0, &obj->hash_node_0); - * tommy_hashdyn_remove_existing(&hash_1, &obj->hash_node_1); - * tommy_list_remove_existing(&list, &obj->list_node); - * } - * - * ... - * - * // complex iterator logic - * tommy_node* i = tommy_list_head(&list); - * while (i != 0) { - * // get the object - * struct object* obj = i->data; - * ... - * // go to the next element - * i = i->next; - * ... - * // go to the prev element - * i = i->prev; - * ... - * } - * - * ... - * - * // deallocates the objects iterating the list - * tommy_list_foreach(&list, free); - * - * // deallocates the hash tables - * tommy_hashdyn_done(&hash_0); - * tommy_hashdyn_done(&hash_1); - * \endcode - * - * \page design Tommy Design - * - * Tommy is designed to fulfill the need of generic data structures for the - * C language, providing at the same time high performance and a clean - * and easy to use interface. - * - * \section testing Testing - * - * Extensive and automated tests with the runtime checker valgrind - * and the static analyzer clang - * are done to ensure the correctness of the library. - * - * The test has a code coverage of 100%, - * measured with lcov. - * - * \section Limitations - * - * Tommy is not thread safe. You have always to provide thread safety using - * locks before calling any Tommy functions. - * - * Tommy doesn't provide iterators over the implicit order defined by the data - * structures. To iterate on elements you must insert them also into a ::tommy_list, - * and use the list as iterator. See the \ref multiindex example for more details. - * Note that this is a real limitation only for ::tommy_trie, as it's the only - * data structure defining an useable order. - * - * Tommy doesn't provide an error reporting mechanism for a malloc() failure. - * You have to provide it redefining malloc() if you expect it to fail. - * - * Tommy assumes to never have more than 2^32-1 elements in a container. - * - * \section compromise Compromises - * - * Finding the right balance between efficency and easy to use, required some - * comprimises, mostly on memory efficency, to avoid to cripple the interface. - * The following is a list of such decisions. - * - * \subsection multi_key Multi key - * All the Tommy containers support the insertion of multiple elements with - * the same key, adding in each node a list of equal elements. - * - * They are the equivalent at the C++ associative containers multimap\ - * and unordered_multimap\ - * that allow duplicates of the same key. - * - * A more memory conservative approach is to not allow duplicated elements, - * removing the need of this list. - * - * \subsection data_pointer Data pointer - * The tommy_node::data field is present to allow search and remove functions to return - * directly a pointer at the element stored in the container. - * - * A more memory conservative approach is to require the user to compute - * the element pointer from the embedded node with a fixed displacement. - * For an example, see the Linux Kernel declaration of - * container_of(). - * - * \subsection insertion_order Insertion order - * The list used for collisions is double linked to allow - * insertion of elements at the end of the list to keep the - * insertion order of equal elements. - * - * A more memory conservative approach is to use a single linked list, - * inserting elements only at the start of the list, losing the - * original insertion order. - * - * \subsection zero_list Zero terminated list - * The 0 terminated format of tommy_node::next is present to provide - * a forward iterator terminating in 0. This allows the user to write a simple - * iteration loop over the list of elements in the same bucket. - * - * A more efficient approach is to use a circular list, as operating on - * nodes in a circular list doesn't requires to manage the special - * terminating case when adding or removing elements. - * - * \page license Tommy License - * Tommy is released with a 2-clause BSD license. - * - * \code - * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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. - * \endcode - */ - -/** \file - * All in one include for Tommy. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -#include "tommytypes.h" -#include "tommyhash.h" -#include "tommyalloc.h" -#include "tommyarray.h" -#include "tommyarrayof.h" -#include "tommyarrayblk.h" -#include "tommyarrayblkof.h" -#include "tommylist.h" -#include "tommytrie.h" -#include "tommytrieinp.h" -#include "tommyhashtbl.h" -#include "tommyhashdyn.h" -#include "tommyhashlin.h" - -#ifdef __cplusplus -} -#endif - diff --git a/tommyds/tommyds/tommyalloc.c b/tommyds/tommyds/tommyalloc.c deleted file mode 100644 index 2c51365..0000000 --- a/tommyds/tommyds/tommyalloc.c +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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 "tommyalloc.h" - -/******************************************************************************/ -/* allocator */ - -/** - * Basic allocation segment. - * Smaller of a memory page, to allow also a little heap overread. - * The heap manager may put it in a single memory page. - */ -#define TOMMY_ALLOCATOR_BLOCK_SIZE (4096 - 64) - -void tommy_allocator_init(tommy_allocator* alloc, tommy_size_t block_size, tommy_size_t align_size) -{ - /* setup the minimal alignment */ - if (align_size < sizeof(void*)) - align_size = sizeof(void*); - - /* ensure that the block_size keeps the alignment */ - if (block_size % align_size != 0) - block_size += align_size - block_size % align_size; - - alloc->block_size = block_size; - alloc->align_size = align_size; - - alloc->count = 0; - alloc->free_block = 0; - alloc->used_segment = 0; -} - -/** - * Reset the allocator and free all. - */ -static void allocator_reset(tommy_allocator* alloc) -{ - tommy_allocator_entry* block = alloc->used_segment; - - while (block) { - tommy_allocator_entry* block_next = block->next; - tommy_free(block); - block = block_next; - } - - alloc->count = 0; - alloc->free_block = 0; - alloc->used_segment = 0; -} - -void tommy_allocator_done(tommy_allocator* alloc) -{ - allocator_reset(alloc); -} - -void* tommy_allocator_alloc(tommy_allocator* alloc) -{ - void* ptr; - - /* if no free block available */ - if (!alloc->free_block) { - tommy_uintptr_t off, mis; - tommy_size_t size = TOMMY_ALLOCATOR_BLOCK_SIZE; - char* data = tommy_cast(char*, tommy_malloc(size)); - tommy_allocator_entry* segment = (tommy_allocator_entry*)data; - - /* put in the segment list */ - segment->next = alloc->used_segment; - alloc->used_segment = segment; - data += sizeof(tommy_allocator_entry); - - /* align if not aligned */ - off = (tommy_uintptr_t)data; - mis = off % alloc->align_size; - if (mis != 0) { - data += alloc->align_size - mis; - size -= alloc->align_size - mis; - } - - /* insert in free list */ - while (size >= alloc->block_size) { - tommy_allocator_entry* free_block = (tommy_allocator_entry*)data; - free_block->next = alloc->free_block; - alloc->free_block = free_block; - - data += alloc->block_size; - size -= alloc->block_size; - } - } - - /* remove one from the free list */ - ptr = alloc->free_block; - alloc->free_block = alloc->free_block->next; - - ++alloc->count; - - return ptr; -} - -void tommy_allocator_free(tommy_allocator* alloc, void* ptr) -{ - tommy_allocator_entry* free_block = tommy_cast(tommy_allocator_entry*, ptr); - - /* put it in the free list */ - free_block->next = alloc->free_block; - alloc->free_block = free_block; - - --alloc->count; -} - -tommy_size_t tommy_allocator_memory_usage(tommy_allocator* alloc) -{ - return alloc->count * (tommy_size_t)alloc->block_size; -} - diff --git a/tommyds/tommyds/tommyalloc.h b/tommyds/tommyds/tommyalloc.h deleted file mode 100644 index 8bf675a..0000000 --- a/tommyds/tommyds/tommyalloc.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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. - */ - -/** \file - * Allocator of fixed size blocks. - */ - -#ifndef __TOMMYALLOC_H -#define __TOMMYALLOC_H - -#include "tommytypes.h" - -/******************************************************************************/ -/* allocator */ - -/** \internal - * Allocator entry. - */ -struct tommy_allocator_entry_struct { - struct tommy_allocator_entry_struct* next; /**< Pointer at the next entry. 0 for last. */ -}; -typedef struct tommy_allocator_entry_struct tommy_allocator_entry; - -/** - * Allocator of fixed size blocks. - */ -typedef struct tommy_allocator_struct { - struct tommy_allocator_entry_struct* free_block; /**< List of free blocks. */ - struct tommy_allocator_entry_struct* used_segment; /**< List of allocated segments. */ - tommy_size_t block_size; /**< Block size. */ - tommy_size_t align_size; /**< Alignment size. */ - tommy_count_t count; /**< Number of allocated elements. */ -} tommy_allocator; - -/** - * Initializes the allocator. - * \param alloc Allocator to initialize. - * \param block_size Size of the block to allocate. - * \param align_size Minimum alignment requirement. No less than sizeof(void*). - */ -void tommy_allocator_init(tommy_allocator* alloc, tommy_size_t block_size, tommy_size_t align_size); - -/** - * Deinitialize the allocator. - * It also releases all the allocated memory to the heap. - * \param alloc Allocator to deinitialize. - */ -void tommy_allocator_done(tommy_allocator* alloc); - -/** - * Allocates a block. - * \param alloc Allocator to use. - */ -void* tommy_allocator_alloc(tommy_allocator* alloc); - -/** - * Deallocates a block. - * You must use the same allocator used in the tommy_allocator_alloc() call. - * \param alloc Allocator to use. - * \param ptr Block to free. - */ -void tommy_allocator_free(tommy_allocator* alloc, void* ptr); - -/** - * Gets the size of allocated memory. - * \param alloc Allocator to use. - */ -tommy_size_t tommy_allocator_memory_usage(tommy_allocator* alloc); - -#endif - diff --git a/tommyds/tommyds/tommyarray.c b/tommyds/tommyds/tommyarray.c deleted file mode 100644 index a8d946b..0000000 --- a/tommyds/tommyds/tommyarray.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2011, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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 "tommyarray.h" - -/******************************************************************************/ -/* array */ - -void tommy_array_init(tommy_array* array) -{ - tommy_uint_t i; - - /* fixed initial size */ - array->bucket_bit = TOMMY_ARRAY_BIT; - array->bucket_max = 1 << array->bucket_bit; - array->bucket[0] = tommy_cast(void**, tommy_calloc(array->bucket_max, sizeof(void*))); - for (i = 1; i < TOMMY_ARRAY_BIT; ++i) - array->bucket[i] = array->bucket[0]; - - array->count = 0; -} - -void tommy_array_done(tommy_array* array) -{ - tommy_uint_t i; - - tommy_free(array->bucket[0]); - for (i = TOMMY_ARRAY_BIT; i < array->bucket_bit; ++i) { - void** segment = array->bucket[i]; - tommy_free(&segment[((tommy_ptrdiff_t)1) << i]); - } -} - -void tommy_array_grow(tommy_array* array, tommy_count_t count) -{ - if (array->count >= count) - return; - array->count = count; - - while (count > array->bucket_max) { - void** segment; - - /* allocate one more segment */ - segment = tommy_cast(void**, tommy_calloc(array->bucket_max, sizeof(void*))); - - /* store it adjusting the offset */ - /* cast to ptrdiff_t to ensure to get a negative value */ - array->bucket[array->bucket_bit] = &segment[-(tommy_ptrdiff_t)array->bucket_max]; - - ++array->bucket_bit; - array->bucket_max = 1 << array->bucket_bit; - } -} - -tommy_size_t tommy_array_memory_usage(tommy_array* array) -{ - return array->bucket_max * (tommy_size_t)sizeof(void*); -} - diff --git a/tommyds/tommyds/tommyarray.h b/tommyds/tommyds/tommyarray.h deleted file mode 100644 index c04193f..0000000 --- a/tommyds/tommyds/tommyarray.h +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (c) 2011, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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. - */ - -/** \file - * Dynamic array based on segments of exponential growing size. - * - * This array is able to grow dynamically upon request, without any reallocation. - * - * The grow operation involves an allocation of a new array segment, without reallocating - * the already used memory, and then not increasing the heap fragmentation. - * This also implies that the address of the stored elements never change. - * - * Allocated segments grow in size exponentially. - */ - -#ifndef __TOMMYARRAY_H -#define __TOMMYARRAY_H - -#include "tommytypes.h" - -#include /* for assert */ - -/******************************************************************************/ -/* array */ - -/** - * Initial and minimal size of the array expressed as a power of 2. - * The initial size is 2^TOMMY_ARRAY_BIT. - */ -#define TOMMY_ARRAY_BIT 6 - -/** \internal - * Max number of elements as a power of 2. - */ -#define TOMMY_ARRAY_BIT_MAX 32 - -/** - * Array container type. - * \note Don't use internal fields directly, but access the container only using functions. - */ -typedef struct tommy_array_struct { - void** bucket[TOMMY_ARRAY_BIT_MAX]; /**< Dynamic array of buckets. */ - tommy_uint_t bucket_bit; /**< Bits used in the bit mask. */ - tommy_count_t bucket_max; /**< Number of buckets. */ - tommy_count_t count; /**< Number of initialized elements in the array. */ -} tommy_array; - -/** - * Initializes the array. - */ -void tommy_array_init(tommy_array* array); - -/** - * Deinitializes the array. - */ -void tommy_array_done(tommy_array* array); - -/** - * Grows the size up to the specified value. - * All the new elements in the array are initialized with the 0 value. - */ -void tommy_array_grow(tommy_array* array, tommy_count_t size); - -/** - * Gets a reference of the element at the specified position. - * You must be sure that space for this position is already - * allocated calling tommy_array_grow(). - */ -tommy_inline void** tommy_array_ref(tommy_array* array, tommy_count_t pos) -{ - tommy_uint_t bsr; - - assert(pos < array->count); - - /* get the highest bit set, in case of all 0, return 0 */ - bsr = tommy_ilog2_u32(pos | 1); - - return &array->bucket[bsr][pos]; -} - -/** - * Sets the element at the specified position. - * You must be sure that space for this position is already - * allocated calling tommy_array_grow(). - */ -tommy_inline void tommy_array_set(tommy_array* array, tommy_count_t pos, void* element) -{ - *tommy_array_ref(array, pos) = element; -} - -/** - * Gets the element at the specified position. - * You must be sure that space for this position is already - * allocated calling tommy_array_grow(). - */ -tommy_inline void* tommy_array_get(tommy_array* array, tommy_count_t pos) -{ - return *tommy_array_ref(array, pos); -} - -/** - * Grows and inserts a new element at the end of the array. - */ -tommy_inline void tommy_array_insert(tommy_array* array, void* element) -{ - tommy_count_t pos = array->count; - - tommy_array_grow(array, pos + 1); - - tommy_array_set(array, pos, element); -} - -/** - * Gets the initialized size of the array. - */ -tommy_inline tommy_count_t tommy_array_size(tommy_array* array) -{ - return array->count; -} - -/** - * Gets the size of allocated memory. - */ -tommy_size_t tommy_array_memory_usage(tommy_array* array); - -#endif - diff --git a/tommyds/tommyds/tommyarrayblk.c b/tommyds/tommyds/tommyarrayblk.c deleted file mode 100644 index d4dda76..0000000 --- a/tommyds/tommyds/tommyarrayblk.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2013, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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 "tommyarrayblk.h" - -/******************************************************************************/ -/* array */ - -void tommy_arrayblk_init(tommy_arrayblk* array) -{ - tommy_array_init(&array->block); - - array->count = 0; -} - -void tommy_arrayblk_done(tommy_arrayblk* array) -{ - tommy_count_t i; - - for (i = 0; i < tommy_array_size(&array->block); ++i) - tommy_free(tommy_array_get(&array->block, i)); - - tommy_array_done(&array->block); -} - -void tommy_arrayblk_grow(tommy_arrayblk* array, tommy_count_t count) -{ - tommy_count_t block_max; - tommy_count_t block_mac; - - if (array->count >= count) - return; - array->count = count; - - block_max = (count + TOMMY_ARRAYBLK_SIZE - 1) / TOMMY_ARRAYBLK_SIZE; - block_mac = tommy_array_size(&array->block); - - if (block_mac < block_max) { - /* grow the block array */ - tommy_array_grow(&array->block, block_max); - - /* allocate new blocks */ - while (block_mac < block_max) { - void** ptr = tommy_cast(void**, tommy_calloc(TOMMY_ARRAYBLK_SIZE, sizeof(void*))); - - /* set the new block */ - tommy_array_set(&array->block, block_mac, ptr); - - ++block_mac; - } - } -} - -tommy_size_t tommy_arrayblk_memory_usage(tommy_arrayblk* array) -{ - return tommy_array_memory_usage(&array->block) + tommy_array_size(&array->block) * TOMMY_ARRAYBLK_SIZE * sizeof(void*); -} - diff --git a/tommyds/tommyds/tommyarrayblk.h b/tommyds/tommyds/tommyarrayblk.h deleted file mode 100644 index 20d0525..0000000 --- a/tommyds/tommyds/tommyarrayblk.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (c) 2013, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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. - */ - -/** \file - * Dynamic array based on blocks of fixed size. - * - * This array is able to grow dynamically upon request, without any reallocation. - * - * The grow operation involves an allocation of a new array block, without reallocating - * the already used memory, and then not increasing the heap fragmentation, - * and minimize the space occupation. - * This also implies that the address of the stored elements never change. - * - * Allocated blocks are always of the same fixed size of 4 Ki pointers. - */ - -#ifndef __TOMMYARRAYBLK_H -#define __TOMMYARRAYBLK_H - -#include "tommytypes.h" -#include "tommyarray.h" - -#include /* for assert */ - -/******************************************************************************/ -/* array */ - -/** - * Elements for each block. - */ -#define TOMMY_ARRAYBLK_SIZE (4 * 1024) - -/** - * Array container type. - * \note Don't use internal fields directly, but access the container only using functions. - */ -typedef struct tommy_arrayblk_struct { - tommy_array block; /**< Array of blocks. */ - tommy_count_t count; /**< Number of initialized elements in the array. */ -} tommy_arrayblk; - -/** - * Initializes the array. - */ -void tommy_arrayblk_init(tommy_arrayblk* array); - -/** - * Deinitializes the array. - */ -void tommy_arrayblk_done(tommy_arrayblk* array); - -/** - * Grows the size up to the specified value. - * All the new elements in the array are initialized with the 0 value. - */ -void tommy_arrayblk_grow(tommy_arrayblk* array, tommy_count_t size); - -/** - * Gets a reference of the element at the specified position. - * You must be sure that space for this position is already - * allocated calling tommy_arrayblk_grow(). - */ -tommy_inline void** tommy_arrayblk_ref(tommy_arrayblk* array, tommy_count_t pos) -{ - void** ptr; - - assert(pos < array->count); - - ptr = tommy_cast(void**, tommy_array_get(&array->block, pos / TOMMY_ARRAYBLK_SIZE)); - - return &ptr[pos % TOMMY_ARRAYBLK_SIZE]; -} - -/** - * Sets the element at the specified position. - * You must be sure that space for this position is already - * allocated calling tommy_arrayblk_grow(). - */ -tommy_inline void tommy_arrayblk_set(tommy_arrayblk* array, tommy_count_t pos, void* element) -{ - *tommy_arrayblk_ref(array, pos) = element; -} - -/** - * Gets the element at the specified position. - * You must be sure that space for this position is already - * allocated calling tommy_arrayblk_grow(). - */ -tommy_inline void* tommy_arrayblk_get(tommy_arrayblk* array, tommy_count_t pos) -{ - return *tommy_arrayblk_ref(array, pos); -} - -/** - * Grows and inserts a new element at the end of the array. - */ -tommy_inline void tommy_arrayblk_insert(tommy_arrayblk* array, void* element) -{ - tommy_count_t pos = array->count; - - tommy_arrayblk_grow(array, pos + 1); - - tommy_arrayblk_set(array, pos, element); -} - -/** - * Gets the initialized size of the array. - */ -tommy_inline tommy_count_t tommy_arrayblk_size(tommy_arrayblk* array) -{ - return array->count; -} - -/** - * Gets the size of allocated memory. - */ -tommy_size_t tommy_arrayblk_memory_usage(tommy_arrayblk* array); - -#endif - diff --git a/tommyds/tommyds/tommyarrayblkof.c b/tommyds/tommyds/tommyarrayblkof.c deleted file mode 100644 index 7d3275d..0000000 --- a/tommyds/tommyds/tommyarrayblkof.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2013, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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 "tommyarrayblkof.h" - -/******************************************************************************/ -/* array */ - -void tommy_arrayblkof_init(tommy_arrayblkof* array, tommy_size_t element_size) -{ - tommy_array_init(&array->block); - - array->element_size = element_size; - array->count = 0; -} - -void tommy_arrayblkof_done(tommy_arrayblkof* array) -{ - tommy_count_t i; - - for (i = 0; i < tommy_array_size(&array->block); ++i) - tommy_free(tommy_array_get(&array->block, i)); - - tommy_array_done(&array->block); -} - -void tommy_arrayblkof_grow(tommy_arrayblkof* array, tommy_count_t count) -{ - tommy_count_t block_max; - tommy_count_t block_mac; - - if (array->count >= count) - return; - array->count = count; - - block_max = (count + TOMMY_ARRAYBLK_SIZE - 1) / TOMMY_ARRAYBLK_SIZE; - block_mac = tommy_array_size(&array->block); - - if (block_mac < block_max) { - /* grow the block array */ - tommy_array_grow(&array->block, block_max); - - /* allocate new blocks */ - while (block_mac < block_max) { - void** ptr = tommy_cast(void**, tommy_calloc(TOMMY_ARRAYBLK_SIZE, array->element_size)); - - /* set the new block */ - tommy_array_set(&array->block, block_mac, ptr); - - ++block_mac; - } - } -} - -tommy_size_t tommy_arrayblkof_memory_usage(tommy_arrayblkof* array) -{ - return tommy_array_memory_usage(&array->block) + tommy_array_size(&array->block) * TOMMY_ARRAYBLKOF_SIZE * array->element_size; -} - diff --git a/tommyds/tommyds/tommyarrayblkof.h b/tommyds/tommyds/tommyarrayblkof.h deleted file mode 100644 index f7ce93a..0000000 --- a/tommyds/tommyds/tommyarrayblkof.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2013, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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. - */ - -/** \file - * Dynamic array based on blocks of fixed size. - * - * This array is able to grow dynamically upon request, without any reallocation. - * - * This is very similar at ::tommy_arrayblk, but it allows to store elements of any - * size and not just pointers. - * - * Note that in this case tommy_arrayblkof_ref() returns a pointer at the element, - * that should be used for getting and setting elements in the array, - * as generic getter and setter are not available. - */ - -#ifndef __TOMMYARRAYBLKOF_H -#define __TOMMYARRAYBLKOF_H - -#include "tommytypes.h" -#include "tommyarray.h" - -#include /* for assert */ - -/******************************************************************************/ -/* array */ - -/** - * Elements for each block. - */ -#define TOMMY_ARRAYBLKOF_SIZE (4 * 1024) - -/** - * Array container type. - * \note Don't use internal fields directly, but access the container only using functions. - */ -typedef struct tommy_arrayblkof_struct { - tommy_array block; /**< Array of blocks. */ - tommy_size_t element_size; /**< Size of the stored element in bytes. */ - tommy_count_t count; /**< Number of initialized elements in the array. */ -} tommy_arrayblkof; - -/** - * Initializes the array. - * \param element_size Size in byte of the element to store in the array. - */ -void tommy_arrayblkof_init(tommy_arrayblkof* array, tommy_size_t element_size); - -/** - * Deinitializes the array. - */ -void tommy_arrayblkof_done(tommy_arrayblkof* array); - -/** - * Grows the size up to the specified value. - * All the new elements in the array are initialized with the 0 value. - */ -void tommy_arrayblkof_grow(tommy_arrayblkof* array, tommy_count_t size); - -/** - * Gets a reference of the element at the specified position. - * You must be sure that space for this position is already - * allocated calling tommy_arrayblkof_grow(). - */ -tommy_inline void* tommy_arrayblkof_ref(tommy_arrayblkof* array, tommy_count_t pos) -{ - unsigned char* base; - - assert(pos < array->count); - - base = tommy_cast(unsigned char*, tommy_array_get(&array->block, pos / TOMMY_ARRAYBLKOF_SIZE)); - - return base + (pos % TOMMY_ARRAYBLKOF_SIZE) * array->element_size; -} - -/** - * Gets the initialized size of the array. - */ -tommy_inline tommy_count_t tommy_arrayblkof_size(tommy_arrayblkof* array) -{ - return array->count; -} - -/** - * Gets the size of allocated memory. - */ -tommy_size_t tommy_arrayblkof_memory_usage(tommy_arrayblkof* array); - -#endif - diff --git a/tommyds/tommyds/tommyarrayof.c b/tommyds/tommyds/tommyarrayof.c deleted file mode 100644 index 5a8359f..0000000 --- a/tommyds/tommyds/tommyarrayof.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2013, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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 "tommyarrayof.h" - -/******************************************************************************/ -/* array */ - -void tommy_arrayof_init(tommy_arrayof* array, tommy_size_t element_size) -{ - tommy_uint_t i; - - /* fixed initial size */ - array->element_size = element_size; - array->bucket_bit = TOMMY_ARRAYOF_BIT; - array->bucket_max = 1 << array->bucket_bit; - array->bucket[0] = tommy_calloc(array->bucket_max, array->element_size); - for (i = 1; i < TOMMY_ARRAYOF_BIT; ++i) - array->bucket[i] = array->bucket[0]; - - array->count = 0; -} - -void tommy_arrayof_done(tommy_arrayof* array) -{ - tommy_uint_t i; - - tommy_free(array->bucket[0]); - for (i = TOMMY_ARRAYOF_BIT; i < array->bucket_bit; ++i) { - void* segment = array->bucket[i]; - tommy_free(tommy_cast(unsigned char*, segment) + (((tommy_ptrdiff_t)1) << i) * array->element_size); - } -} - -void tommy_arrayof_grow(tommy_arrayof* array, tommy_count_t count) -{ - if (array->count >= count) - return; - array->count = count; - - while (count > array->bucket_max) { - void* segment; - - /* allocate one more segment */ - segment = tommy_calloc(array->bucket_max, array->element_size); - - /* store it adjusting the offset */ - /* cast to ptrdiff_t to ensure to get a negative value */ - array->bucket[array->bucket_bit] = tommy_cast(unsigned char*, segment) - (tommy_ptrdiff_t)array->bucket_max * array->element_size; - - ++array->bucket_bit; - array->bucket_max = 1 << array->bucket_bit; - } -} - -tommy_size_t tommy_arrayof_memory_usage(tommy_arrayof* array) -{ - return array->bucket_max * (tommy_size_t)array->element_size; -} - diff --git a/tommyds/tommyds/tommyarrayof.h b/tommyds/tommyds/tommyarrayof.h deleted file mode 100644 index 4ab8c66..0000000 --- a/tommyds/tommyds/tommyarrayof.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (c) 2013, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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. - */ - -/** \file - * Dynamic array based on segments of exponential growing size. - * - * This array is able to grow dynamically upon request, without any reallocation. - * - * This is very similar at ::tommy_array, but it allows to store elements of any - * size and not just pointers. - * - * Note that in this case tommy_arrayof_ref() returns a pointer at the element, - * that should be used for getting and setting elements in the array, - * as generic getter and setter are not available. - */ - -#ifndef __TOMMYARRAYOF_H -#define __TOMMYARRAYOF_H - -#include "tommytypes.h" - -#include /* for assert */ - -/******************************************************************************/ -/* array */ - -/** - * Initial and minimal size of the array expressed as a power of 2. - * The initial size is 2^TOMMY_ARRAYOF_BIT. - */ -#define TOMMY_ARRAYOF_BIT 6 - -/** \internal - * Max number of elements as a power of 2. - */ -#define TOMMY_ARRAYOF_BIT_MAX 32 - -/** - * Array container type. - * \note Don't use internal fields directly, but access the container only using functions. - */ -typedef struct tommy_arrayof_struct { - void* bucket[TOMMY_ARRAYOF_BIT_MAX]; /**< Dynamic array of buckets. */ - tommy_size_t element_size; /**< Size of the stored element in bytes. */ - tommy_uint_t bucket_bit; /**< Bits used in the bit mask. */ - tommy_count_t bucket_max; /**< Number of buckets. */ - tommy_count_t count; /**< Number of initialized elements in the array. */ -} tommy_arrayof; - -/** - * Initializes the array. - * \param element_size Size in byte of the element to store in the array. - */ -void tommy_arrayof_init(tommy_arrayof* array, tommy_size_t element_size); - -/** - * Deinitializes the array. - */ -void tommy_arrayof_done(tommy_arrayof* array); - -/** - * Grows the size up to the specified value. - * All the new elements in the array are initialized with the 0 value. - */ -void tommy_arrayof_grow(tommy_arrayof* array, tommy_count_t size); - -/** - * Gets a reference of the element at the specified position. - * You must be sure that space for this position is already - * allocated calling tommy_arrayof_grow(). - */ -tommy_inline void* tommy_arrayof_ref(tommy_arrayof* array, tommy_count_t pos) -{ - unsigned char* ptr; - tommy_uint_t bsr; - - assert(pos < array->count); - - /* get the highest bit set, in case of all 0, return 0 */ - bsr = tommy_ilog2_u32(pos | 1); - - ptr = tommy_cast(unsigned char*, array->bucket[bsr]); - - return ptr + pos * array->element_size; -} - -/** - * Gets the initialized size of the array. - */ -tommy_inline tommy_count_t tommy_arrayof_size(tommy_arrayof* array) -{ - return array->count; -} - -/** - * Gets the size of allocated memory. - */ -tommy_size_t tommy_arrayof_memory_usage(tommy_arrayof* array); - -#endif - diff --git a/tommyds/tommyds/tommychain.h b/tommyds/tommyds/tommychain.h deleted file mode 100644 index 71d3ccc..0000000 --- a/tommyds/tommyds/tommychain.h +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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. - */ - -/** \file - * Chain of nodes. - * A chain of nodes is an abstraction used to implements complex list operations - * like sorting. - * - * Do not use this directly. Use lists instead. - */ - -#ifndef __TOMMYCHAIN_H -#define __TOMMYCHAIN_H - -#include "tommytypes.h" - -/******************************************************************************/ -/* chain */ - -/** - * Chain of nodes. - * A chain of nodes is a sequence of nodes with the following properties: - * - It contains at least one node. A chains of zero nodes cannot exist. - * - The next field of the tail is of *undefined* value. - * - The prev field of the head is of *undefined* value. - * - All the other inner prev and next fields are correctly set. - */ -typedef struct tommy_chain_struct { - tommy_node* head; /**< Pointer at the head of the chain. */ - tommy_node* tail; /**< Pointer at the tail of the chain. */ -} tommy_chain; - -/** - * Splices a chain in the middle of another chain. - */ -tommy_inline void tommy_chain_splice(tommy_node* first_before, tommy_node* first_after, tommy_node* second_head, tommy_node* second_tail) -{ - /* set the prev list */ - first_after->prev = second_tail; - second_head->prev = first_before; - - /* set the next list */ - first_before->next = second_head; - second_tail->next = first_after; -} - -/** - * Concats two chains. - */ -tommy_inline void tommy_chain_concat(tommy_node* first_tail, tommy_node* second_head) -{ - /* set the prev list */ - second_head->prev = first_tail; - - /* set the next list */ - first_tail->next = second_head; -} - -/** - * Merges two chains. - */ -tommy_inline void tommy_chain_merge(tommy_chain* first, tommy_chain* second, tommy_compare_func* cmp) -{ - tommy_node* first_i = first->head; - tommy_node* second_i = second->head; - - /* merge */ - while (1) { - if (cmp(first_i->data, second_i->data) > 0) { - tommy_node* next = second_i->next; - if (first_i == first->head) { - tommy_chain_concat(second_i, first_i); - first->head = second_i; - } else { - tommy_chain_splice(first_i->prev, first_i, second_i, second_i); - } - if (second_i == second->tail) - break; - second_i = next; - } else { - if (first_i == first->tail) { - tommy_chain_concat(first_i, second_i); - first->tail = second->tail; - break; - } - first_i = first_i->next; - } - } -} - -/** - * Merges two chains managing special degenerated cases. - * It's funtionally equivalent at tommy_chain_merge() but faster with already ordered chains. - */ -tommy_inline void tommy_chain_merge_degenerated(tommy_chain* first, tommy_chain* second, tommy_compare_func* cmp) -{ - /* identify the condition first <= second */ - if (cmp(first->tail->data, second->head->data) <= 0) { - tommy_chain_concat(first->tail, second->head); - first->tail = second->tail; - return; - } - - /* identify the condition second < first */ - /* here we must be strict on comparison to keep the sort stable */ - if (cmp(second->tail->data, first->head->data) < 0) { - tommy_chain_concat(second->tail, first->head); - first->head = second->head; - return; - } - - tommy_chain_merge(first, second, cmp); -} - -/** - * Max number of elements as a power of 2. - */ -#define TOMMY_CHAIN_BIT_MAX 32 - -/** - * Sorts a chain. - * It's a stable merge sort using power of 2 buckets, with O(N*log(N)) complexity, - * similar at the one used in the SGI STL libraries and in the Linux Kernel, - * but faster on degenerated cases like already ordered lists. - * - * SGI STL stl_list.h - * http://www.sgi.com/tech/stl/stl_list.h - * - * Linux Kernel lib/list_sort.c - * http://lxr.linux.no/#linux+v2.6.36/lib/list_sort.c - */ -tommy_inline void tommy_chain_mergesort(tommy_chain* chain, tommy_compare_func* cmp) -{ - /* - * Bit buckets of chains. - * Each bucket contains 2^i nodes or it's empty. - * The chain at address TOMMY_CHAIN_BIT_MAX is an independet variable operating as "carry". - * We keep it in the same "bit" vector to avoid reports from the valgrind tool sgcheck. - */ - tommy_chain bit[TOMMY_CHAIN_BIT_MAX + 1]; - - /** - * Value stored inside the bit bucket. - * It's used to know which bucket is empty of full. - */ - tommy_count_t counter; - tommy_node* node = chain->head; - tommy_node* tail = chain->tail; - tommy_count_t mask; - tommy_count_t i; - - counter = 0; - while (1) { - tommy_node* next; - tommy_chain* last; - - /* carry bit to add */ - last = &bit[TOMMY_CHAIN_BIT_MAX]; - bit[TOMMY_CHAIN_BIT_MAX].head = node; - bit[TOMMY_CHAIN_BIT_MAX].tail = node; - next = node->next; - - /* add the bit, propagating the carry */ - i = 0; - mask = counter; - while ((mask & 1) != 0) { - tommy_chain_merge_degenerated(&bit[i], last, cmp); - mask >>= 1; - last = &bit[i]; - ++i; - } - - /* copy the carry in the first empty bit */ - bit[i] = *last; - - /* add the carry in the counter */ - ++counter; - - if (node == tail) - break; - node = next; - } - - /* merge the buckets */ - i = tommy_ctz_u32(counter); - mask = counter >> i; - while (mask != 1) { - mask >>= 1; - if (mask & 1) - tommy_chain_merge_degenerated(&bit[i + 1], &bit[i], cmp); - else - bit[i + 1] = bit[i]; - ++i; - } - - *chain = bit[i]; -} - -#endif - diff --git a/tommyds/tommyds/tommyhash.c b/tommyds/tommyds/tommyhash.c deleted file mode 100644 index 004c2ae..0000000 --- a/tommyds/tommyds/tommyhash.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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 "tommyhash.h" - -/******************************************************************************/ -/* hash */ - -tommy_inline tommy_uint32_t tommy_le_uint32_read(const void* ptr) -{ - /* allow unaligned read on Intel x86 and x86_64 platforms */ -#if defined(__i386__) || defined(_M_IX86) || defined(_X86_) || defined(__x86_64__) || defined(_M_X64) - /* defines from http://predef.sourceforge.net/ */ - return *(const tommy_uint32_t*)ptr; -#else - const unsigned char* ptr8 = tommy_cast(const unsigned char*, ptr); - return ptr8[0] + ((tommy_uint32_t)ptr8[1] << 8) + ((tommy_uint32_t)ptr8[2] << 16) + ((tommy_uint32_t)ptr8[3] << 24); -#endif -} - -#define tommy_rot(x, k) \ - (((x) << (k)) | ((x) >> (32 - (k)))) - -#define tommy_mix(a, b, c) \ - do { \ - a -= c; a ^= tommy_rot(c, 4); c += b; \ - b -= a; b ^= tommy_rot(a, 6); a += c; \ - c -= b; c ^= tommy_rot(b, 8); b += a; \ - a -= c; a ^= tommy_rot(c, 16); c += b; \ - b -= a; b ^= tommy_rot(a, 19); a += c; \ - c -= b; c ^= tommy_rot(b, 4); b += a; \ - } while (0) - -#define tommy_final(a, b, c) \ - do { \ - c ^= b; c -= tommy_rot(b, 14); \ - a ^= c; a -= tommy_rot(c, 11); \ - b ^= a; b -= tommy_rot(a, 25); \ - c ^= b; c -= tommy_rot(b, 16); \ - a ^= c; a -= tommy_rot(c, 4); \ - b ^= a; b -= tommy_rot(a, 14); \ - c ^= b; c -= tommy_rot(b, 24); \ - } while (0) - -tommy_uint32_t tommy_hash_u32(tommy_uint32_t init_val, const void* void_key, tommy_size_t key_len) -{ - const unsigned char* key = tommy_cast(const unsigned char*, void_key); - tommy_uint32_t a, b, c; - - a = b = c = 0xdeadbeef + ((tommy_uint32_t)key_len) + init_val; - - while (key_len > 12) { - a += tommy_le_uint32_read(key + 0); - b += tommy_le_uint32_read(key + 4); - c += tommy_le_uint32_read(key + 8); - - tommy_mix(a, b, c); - - key_len -= 12; - key += 12; - } - - switch (key_len) { - case 0 : - return c; /* used only when called with a zero length */ - case 12 : - c += tommy_le_uint32_read(key + 8); - b += tommy_le_uint32_read(key + 4); - a += tommy_le_uint32_read(key + 0); - break; - case 11 : c += ((tommy_uint32_t)key[10]) << 16; - case 10 : c += ((tommy_uint32_t)key[9]) << 8; - case 9 : c += key[8]; - case 8 : - b += tommy_le_uint32_read(key + 4); - a += tommy_le_uint32_read(key + 0); - break; - case 7 : b += ((tommy_uint32_t)key[6]) << 16; - case 6 : b += ((tommy_uint32_t)key[5]) << 8; - case 5 : b += key[4]; - case 4 : - a += tommy_le_uint32_read(key + 0); - break; - case 3 : a += ((tommy_uint32_t)key[2]) << 16; - case 2 : a += ((tommy_uint32_t)key[1]) << 8; - case 1 : a += key[0]; - } - - tommy_final(a, b, c); - - return c; -} - -tommy_uint64_t tommy_hash_u64(tommy_uint64_t init_val, const void* void_key, tommy_size_t key_len) -{ - const unsigned char* key = tommy_cast(const unsigned char*, void_key); - tommy_uint32_t a, b, c; - - a = b = c = 0xdeadbeef + ((tommy_uint32_t)key_len) + (init_val & 0xffffffff); - c += init_val >> 32; - - while (key_len > 12) { - a += tommy_le_uint32_read(key + 0); - b += tommy_le_uint32_read(key + 4); - c += tommy_le_uint32_read(key + 8); - - tommy_mix(a, b, c); - - key_len -= 12; - key += 12; - } - - switch (key_len) { - case 0 : - return c + ((tommy_uint64_t)b << 32); /* used only when called with a zero length */ - case 12 : - c += tommy_le_uint32_read(key + 8); - b += tommy_le_uint32_read(key + 4); - a += tommy_le_uint32_read(key + 0); - break; - case 11 : c += ((tommy_uint32_t)key[10]) << 16; - case 10 : c += ((tommy_uint32_t)key[9]) << 8; - case 9 : c += key[8]; - case 8 : - b += tommy_le_uint32_read(key + 4); - a += tommy_le_uint32_read(key + 0); - break; - case 7 : b += ((tommy_uint32_t)key[6]) << 16; - case 6 : b += ((tommy_uint32_t)key[5]) << 8; - case 5 : b += key[4]; - case 4 : - a += tommy_le_uint32_read(key + 0); - break; - case 3 : a += ((tommy_uint32_t)key[2]) << 16; - case 2 : a += ((tommy_uint32_t)key[1]) << 8; - case 1 : a += key[0]; - } - - tommy_final(a, b, c); - - return c + ((tommy_uint64_t)b << 32); -} - diff --git a/tommyds/tommyds/tommyhash.h b/tommyds/tommyds/tommyhash.h deleted file mode 100644 index 33317b4..0000000 --- a/tommyds/tommyds/tommyhash.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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. - */ - -/** \file - * Hash functions for the use with ::tommy_hashtable, ::tommy_hashdyn and ::tommy_hashlin. - */ - -#ifndef __TOMMYHASH_H -#define __TOMMYHASH_H - -#include "tommytypes.h" - -/******************************************************************************/ -/* hash */ - -/** - * Hash type used in hashtables. - */ -typedef tommy_key_t tommy_hash_t; - -/** - * Hash function with a 32 bits result. - * Implementation of the Robert Jenkins "lookup3" hash 32 bits version, - * from http://www.burtleburtle.net/bob/hash/doobs.html, function hashlittle(). - * \param init_val Initialization value. - * Using a different initialization value, you can generate a completely different set of hash values. - * Use 0 if not relevant. - * \param void_key Pointer at the data to hash. - * \param key_len Size of the data to hash. - * \note - * This function is endianess independent. - * \return The hash value of 32 bits. - */ -tommy_uint32_t tommy_hash_u32(tommy_uint32_t init_val, const void* void_key, tommy_size_t key_len); - -/** - * Hash function with a 64 bits result. - * Implementation of the Robert Jenkins "lookup3" hash 64 bits versions, - * from http://www.burtleburtle.net/bob/hash/doobs.html, function hashlittle2(). - * \param init_val Initialization value. - * Using a different initialization value, you can generate a completely different set of hash values. - * Use 0 if not relevant. - * \param void_key Pointer at the data to hash. - * \param key_len Size of the data to hash. - * \note - * This function is endianess independent. - * \return The hash value of 64 bits. - */ -tommy_uint64_t tommy_hash_u64(tommy_uint64_t init_val, const void* void_key, tommy_size_t key_len); - -/** - * Integer reversible hash function for 32 bits. - * Implementation of the Robert Jenkins "4-byte Integer Hashing", - * from http://burtleburtle.net/bob/hash/integer.html - */ -tommy_inline tommy_uint32_t tommy_inthash_u32(tommy_uint32_t key) -{ - key -= key << 6; - key ^= key >> 17; - key -= key << 9; - key ^= key << 4; - key -= key << 3; - key ^= key << 10; - key ^= key >> 15; - - return key; -} - -/** - * Integer reversible hash function for 64 bits. - * Implementation of the Thomas Wang "Integer Hash Function", - * from http://web.archive.org/web/20071223173210/http://www.concentric.net/~Ttwang/tech/inthash.htm - */ -tommy_inline tommy_uint64_t tommy_inthash_u64(tommy_uint64_t key) -{ - key = ~key + (key << 21); - key = key ^ (key >> 24); - key = key + (key << 3) + (key << 8); - key = key ^ (key >> 14); - key = key + (key << 2) + (key << 4); - key = key ^ (key >> 28); - key = key + (key << 31); - - return key; -} - -#endif - diff --git a/tommyds/tommyds/tommyhashdyn.c b/tommyds/tommyds/tommyhashdyn.c deleted file mode 100644 index 02ef404..0000000 --- a/tommyds/tommyds/tommyhashdyn.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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 "tommyhashdyn.h" -#include "tommylist.h" - -/******************************************************************************/ -/* hashdyn */ - -void tommy_hashdyn_init(tommy_hashdyn* hashdyn) -{ - /* fixed initial size */ - hashdyn->bucket_bit = TOMMY_HASHDYN_BIT; - hashdyn->bucket_max = 1 << hashdyn->bucket_bit; - hashdyn->bucket_mask = hashdyn->bucket_max - 1; - hashdyn->bucket = tommy_cast(tommy_hashdyn_node**, tommy_calloc(hashdyn->bucket_max, sizeof(tommy_hashdyn_node*))); - - hashdyn->count = 0; -} - -void tommy_hashdyn_done(tommy_hashdyn* hashdyn) -{ - tommy_free(hashdyn->bucket); -} - -/** - * Resize the bucket vector. - */ -static void tommy_hashdyn_resize(tommy_hashdyn* hashdyn, tommy_count_t new_bucket_bit) -{ - tommy_count_t bucket_bit; - tommy_count_t bucket_max; - tommy_count_t new_bucket_max; - tommy_count_t new_bucket_mask; - tommy_hashdyn_node** new_bucket; - - bucket_bit = hashdyn->bucket_bit; - bucket_max = hashdyn->bucket_max; - - new_bucket_max = 1 << new_bucket_bit; - new_bucket_mask = new_bucket_max - 1; - - /* allocate the new vector using malloc() and not calloc() */ - /* because data is fully initialized in the update process */ - new_bucket = tommy_cast(tommy_hashdyn_node**, tommy_malloc(new_bucket_max * sizeof(tommy_hashdyn_node*))); - - /* reinsert all the elements */ - if (new_bucket_bit > bucket_bit) { - tommy_count_t i; - - /* grow */ - for (i = 0; i < bucket_max; ++i) { - tommy_hashdyn_node* j; - - /* setup the new two buckets */ - new_bucket[i] = 0; - new_bucket[i + bucket_max] = 0; - - /* reinsert the bucket */ - j = hashdyn->bucket[i]; - while (j) { - tommy_hashdyn_node* j_next = j->next; - tommy_count_t pos = j->key & new_bucket_mask; - if (new_bucket[pos]) - tommy_list_insert_tail_not_empty(new_bucket[pos], j); - else - tommy_list_insert_first(&new_bucket[pos], j); - j = j_next; - } - } - } else { - tommy_count_t i; - - /* shrink */ - for (i = 0; i < new_bucket_max; ++i) { - /* setup the new bucket with the lower bucket*/ - new_bucket[i] = hashdyn->bucket[i]; - - /* concat the upper bucket */ - tommy_list_concat(&new_bucket[i], &hashdyn->bucket[i + new_bucket_max]); - } - } - - tommy_free(hashdyn->bucket); - - /* setup */ - hashdyn->bucket_bit = new_bucket_bit; - hashdyn->bucket_max = new_bucket_max; - hashdyn->bucket_mask = new_bucket_mask; - hashdyn->bucket = new_bucket; -} - -/** - * Grow. - */ -tommy_inline void hashdyn_grow_step(tommy_hashdyn* hashdyn) -{ - /* grow if more than 50% full */ - if (hashdyn->count >= hashdyn->bucket_max / 2) - tommy_hashdyn_resize(hashdyn, hashdyn->bucket_bit + 1); -} - -/** - * Shrink. - */ -tommy_inline void hashdyn_shrink_step(tommy_hashdyn* hashdyn) -{ - /* shrink if less than 12.5% full */ - if (hashdyn->count <= hashdyn->bucket_max / 8 && hashdyn->bucket_bit > TOMMY_HASHDYN_BIT) - tommy_hashdyn_resize(hashdyn, hashdyn->bucket_bit - 1); -} - -void tommy_hashdyn_insert(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node, void* data, tommy_hash_t hash) -{ - tommy_count_t pos = hash & hashdyn->bucket_mask; - - tommy_list_insert_tail(&hashdyn->bucket[pos], node, data); - - node->key = hash; - - ++hashdyn->count; - - hashdyn_grow_step(hashdyn); -} - -void* tommy_hashdyn_remove_existing(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node) -{ - tommy_count_t pos = node->key & hashdyn->bucket_mask; - - tommy_list_remove_existing(&hashdyn->bucket[pos], node); - - --hashdyn->count; - - hashdyn_shrink_step(hashdyn); - - return node->data; -} - -void* tommy_hashdyn_remove(tommy_hashdyn* hashdyn, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash) -{ - tommy_count_t pos = hash & hashdyn->bucket_mask; - tommy_hashdyn_node* node = hashdyn->bucket[pos]; - - while (node) { - /* we first check if the hash matches, as in the same bucket we may have multiples hash values */ - if (node->key == hash && cmp(cmp_arg, node->data) == 0) { - tommy_list_remove_existing(&hashdyn->bucket[pos], node); - - --hashdyn->count; - - hashdyn_shrink_step(hashdyn); - - return node->data; - } - node = node->next; - } - - return 0; -} - -void tommy_hashdyn_foreach(tommy_hashdyn* hashdyn, tommy_foreach_func* func) -{ - tommy_count_t bucket_max = hashdyn->bucket_max; - tommy_hashdyn_node** bucket = hashdyn->bucket; - tommy_count_t pos; - - for (pos = 0; pos < bucket_max; ++pos) { - tommy_hashdyn_node* node = bucket[pos]; - - while (node) { - void* data = node->data; - node = node->next; - func(data); - } - } -} - -void tommy_hashdyn_foreach_arg(tommy_hashdyn* hashdyn, tommy_foreach_arg_func* func, void* arg) -{ - tommy_count_t bucket_max = hashdyn->bucket_max; - tommy_hashdyn_node** bucket = hashdyn->bucket; - tommy_count_t pos; - - for (pos = 0; pos < bucket_max; ++pos) { - tommy_hashdyn_node* node = bucket[pos]; - - while (node) { - void* data = node->data; - node = node->next; - func(arg, data); - } - } -} - -tommy_size_t tommy_hashdyn_memory_usage(tommy_hashdyn* hashdyn) -{ - return hashdyn->bucket_max * (tommy_size_t)sizeof(hashdyn->bucket[0]) - + tommy_hashdyn_count(hashdyn) * (tommy_size_t)sizeof(tommy_hashdyn_node); -} - diff --git a/tommyds/tommyds/tommyhashdyn.h b/tommyds/tommyds/tommyhashdyn.h deleted file mode 100644 index afbdfab..0000000 --- a/tommyds/tommyds/tommyhashdyn.h +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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. - */ - -/** \file - * Dynamic chained hashtable. - * - * This hashtable resizes dynamically. It starts with the minimal size of 16 buckets, it doubles - * the size then it reaches a load factor greater than 0.5 and it halves the size with a load - * factor lower than 0.125. - * - * All the elements are reallocated in a single resize operation done inside - * tommy_hashdyn_insert() or tommy_hashdyn_remove(). - * - * Note that the resize operation takes approximatively 100 [ms] with 1 million of elements, - * and 1 [second] with 10 millions. This could be a problem in real-time applications. - * - * The resize also fragment the heap, as it involves allocating a double-sized table, copy elements, - * and deallocating the older table. Leaving a big hole in the heap. - * - * The ::tommy_hashlin hashtable fixes both problems. - * - * To initialize the hashtable you have to call tommy_hashdyn_init(). - * - * \code - * tommy_hashslin hashdyn; - * - * tommy_hashdyn_init(&hashdyn); - * \endcode - * - * To insert elements in the hashtable you have to call tommy_hashdyn_insert() for - * each element. - * In the insertion call you have to specify the address of the node, the - * address of the object, and the hash value of the key to use. - * The address of the object is used to initialize the tommy_node::data field - * of the node, and the hash to initialize the tommy_node::key field. - * - * \code - * struct object { - * tommy_node node; - * // other fields - * int value; - * }; - * - * struct object* obj = malloc(sizeof(struct object)); // creates the object - * - * obj->value = ...; // initializes the object - * - * tommy_hashdyn_insert(&hashdyn, &obj->node, obj, tommy_inthash_u32(obj->value)); // inserts the object - * \endcode - * - * To find and element in the hashtable you have to call tommy_hashtable_search() - * providing a comparison function, its argument, and the hash of the key to search. - * - * \code - * int compare(const void* arg, const void* obj) - * { - * return *(const int*)arg != ((const struct object*)obj)->value; - * } - * - * int value_to_find = 1; - * struct object* obj = tommy_hashdyn_search(&hashdyn, compare, &value_to_find, tommy_inthash_u32(value_to_find)); - * if (!obj) { - * // not found - * } else { - * // found - * } - * \endcode - * - * To iterate over all the elements in the hashtable with the same key, you have to - * use tommy_hashdyn_bucket() and follow the tommy_node::next pointer until NULL. - * You have also to check explicitely for the key, as the bucket may contains - * different keys. - * - * \code - * int value_to_find = 1; - * tommy_node* i = tommy_hashdyn_bucket(&hashdyn, tommy_inthash_u32(value_to_find)); - * while (i) { - * struct object* obj = i->data; // gets the object pointer - * - * if (obj->value == value_to_find) { - * printf("%d\n", obj->value); // process the object - * } - * - * i = i->next; // goes to the next element - * } - * \endcode - * - * To remove an element from the hashtable you have to call tommy_hashdyn_remove() - * providing a comparison function, its argument, and the hash of the key to search - * and remove. - * - * \code - * struct object* obj = tommy_hashdyn_remove(&hashdyn, compare, &value_to_remove, tommy_inthash_u32(value_to_remove)); - * if (obj) { - * free(obj); // frees the object allocated memory - * } - * \endcode - * - * To destroy the hashtable you have to remove all the elements, and deinitialize - * the hashtable calling tommy_hashdyn_done(). - * - * \code - * tommy_hashdyn_done(&hashdyn); - * \endcode - * - * If you need to iterate over all the elements in the hashtable, you can use - * tommy_hashdyn_foreach() or tommy_hashdyn_foreach_arg(). - * If you need a more precise control with a real iteration, you have to insert - * all the elements also in a ::tommy_list, and use the list to iterate. - * See the \ref multiindex example for more detail. - */ - -#ifndef __TOMMYHASHDYN_H -#define __TOMMYHASHDYN_H - -#include "tommyhash.h" - -/******************************************************************************/ -/* hashdyn */ - -/** \internal - * Initial and minimal size of the hashtable expressed as a power of 2. - * The initial size is 2^TOMMY_HASHDYN_BIT. - */ -#define TOMMY_HASHDYN_BIT 4 - -/** - * Hashtable node. - * This is the node that you have to include inside your objects. - */ -typedef tommy_node tommy_hashdyn_node; - -/** - * Hashtable container type. - * \note Don't use internal fields directly, but access the container only using functions. - */ -typedef struct tommy_hashdyn_struct { - tommy_hashdyn_node** bucket; /**< Hash buckets. One list for each hash modulus. */ - tommy_uint_t bucket_bit; /**< Bits used in the bit mask. */ - tommy_count_t bucket_max; /**< Number of buckets. */ - tommy_count_t bucket_mask; /**< Bit mask to access the buckets. */ - tommy_count_t count; /**< Number of elements. */ -} tommy_hashdyn; - -/** - * Initializes the hashtable. - */ -void tommy_hashdyn_init(tommy_hashdyn* hashdyn); - -/** - * Deinitializes the hashtable. - * - * You can call this function with elements still contained, - * but such elements are not going to be freed by this call. - */ -void tommy_hashdyn_done(tommy_hashdyn* hashdyn); - -/** - * Inserts an element in the hashtable. - */ -void tommy_hashdyn_insert(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node, void* data, tommy_hash_t hash); - -/** - * Searches and removes an element from the hashtable. - * You have to provide a compare function and the hash of the element you want to remove. - * If the element is not found, 0 is returned. - * If more equal elements are present, the first one is removed. - * \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one. - * The function should return 0 for equal elements, anything other for different elements. - * \param cmp_arg Compare argument passed as first argument of the compare function. - * \param hash Hash of the element to find and remove. - * \return The removed element, or 0 if not found. - */ -void* tommy_hashdyn_remove(tommy_hashdyn* hashdyn, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash); - -/** - * Gets the bucket of the specified hash. - * The bucket is guaranteed to contain ALL the elements with the specified hash, - * but it can contain also others. - * You can access elements in the bucket following the ::next pointer until 0. - * \param hash Hash of the element to find. - * \return The head of the bucket, or 0 if empty. - */ -tommy_inline tommy_hashdyn_node* tommy_hashdyn_bucket(tommy_hashdyn* hashdyn, tommy_hash_t hash) -{ - return hashdyn->bucket[hash & hashdyn->bucket_mask]; -} - -/** - * Searches an element in the hashtable. - * You have to provide a compare function and the hash of the element you want to find. - * If more equal elements are present, the first one is returned. - * \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one. - * The function should return 0 for equal elements, anything other for different elements. - * \param cmp_arg Compare argument passed as first argument of the compare function. - * \param hash Hash of the element to find. - * \return The first element found, or 0 if none. - */ -tommy_inline void* tommy_hashdyn_search(tommy_hashdyn* hashdyn, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash) -{ - tommy_hashdyn_node* i = tommy_hashdyn_bucket(hashdyn, hash); - - while (i) { - /* we first check if the hash matches, as in the same bucket we may have multiples hash values */ - if (i->key == hash && cmp(cmp_arg, i->data) == 0) - return i->data; - i = i->next; - } - return 0; -} - -/** - * Removes an element from the hashtable. - * You must already have the address of the element to remove. - * \return The tommy_node::data field of the node removed. - */ -void* tommy_hashdyn_remove_existing(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node); - -/** - * Calls the specified function for each element in the hashtable. - * - * You can use this function to deallocate all the elements inserted. - * - * \code - * tommy_hashdyn hashdyn; - * - * // initializes the hashtable - * tommy_hashdyn_init(&hashdyn); - * - * ... - * - * // creates an object - * struct object* obj = malloc(sizeof(struct object)); - * - * ... - * - * // insert it in the hashtable - * tommy_hashdyn_insert(&hashdyn, &obj->node, obj, tommy_inthash_u32(obj->value)); - * - * ... - * - * // deallocates all the objects iterating the hashtable - * tommy_hashdyn_foreach(&hashdyn, free); - * - * // deallocates the hashtable - * tommy_hashdyn_done(&hashdyn); - * \endcode - */ -void tommy_hashdyn_foreach(tommy_hashdyn* hashdyn, tommy_foreach_func* func); - -/** - * Calls the specified function with an argument for each element in the hashtable. - */ -void tommy_hashdyn_foreach_arg(tommy_hashdyn* hashdyn, tommy_foreach_arg_func* func, void* arg); - -/** - * Gets the number of elements. - */ -tommy_inline tommy_count_t tommy_hashdyn_count(tommy_hashdyn* hashdyn) -{ - return hashdyn->count; -} - -/** - * Gets the size of allocated memory. - * It includes the size of the ::tommy_hashdyn_node of the stored elements. - */ -tommy_size_t tommy_hashdyn_memory_usage(tommy_hashdyn* hashdyn); - -#endif - diff --git a/tommyds/tommyds/tommyhashlin.c b/tommyds/tommyds/tommyhashlin.c deleted file mode 100644 index bcf7d98..0000000 --- a/tommyds/tommyds/tommyhashlin.c +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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 "tommyhashlin.h" -#include "tommylist.h" - -#include /* for assert */ - -/******************************************************************************/ -/* hashlin */ - -/** - * Reallocation states. - */ -#define TOMMY_HASHLIN_STATE_STABLE 0 -#define TOMMY_HASHLIN_STATE_GROW 1 -#define TOMMY_HASHLIN_STATE_SHRINK 2 - -/** - * Set the hashtable in stable state. - */ -tommy_inline void tommy_hashlin_stable(tommy_hashlin* hashlin) -{ - hashlin->state = TOMMY_HASHLIN_STATE_STABLE; - - /* setup low_mask/max/split to allow tommy_hashlin_bucket_ref() */ - /* and tommy_hashlin_foreach() to work regardless we are in stable state */ - hashlin->low_max = hashlin->bucket_max; - hashlin->low_mask = hashlin->bucket_mask; - hashlin->split = 0; -} - -void tommy_hashlin_init(tommy_hashlin* hashlin) -{ - tommy_uint_t i; - - /* fixed initial size */ - hashlin->bucket_bit = TOMMY_HASHLIN_BIT; - hashlin->bucket_max = 1 << hashlin->bucket_bit; - hashlin->bucket_mask = hashlin->bucket_max - 1; - hashlin->bucket[0] = tommy_cast(tommy_hashlin_node**, tommy_calloc(hashlin->bucket_max, sizeof(tommy_hashlin_node*))); - for (i = 1; i < TOMMY_HASHLIN_BIT; ++i) - hashlin->bucket[i] = hashlin->bucket[0]; - - /* stable state */ - tommy_hashlin_stable(hashlin); - - hashlin->count = 0; -} - -void tommy_hashlin_done(tommy_hashlin* hashlin) -{ - tommy_uint_t i; - - tommy_free(hashlin->bucket[0]); - for (i = TOMMY_HASHLIN_BIT; i < hashlin->bucket_bit; ++i) { - tommy_hashlin_node** segment = hashlin->bucket[i]; - tommy_free(&segment[((tommy_ptrdiff_t)1) << i]); - } -} - -/** - * Grow one step. - */ -tommy_inline void hashlin_grow_step(tommy_hashlin* hashlin) -{ - /* grow if more than 50% full */ - if (hashlin->state != TOMMY_HASHLIN_STATE_GROW - && hashlin->count > hashlin->bucket_max / 2 - ) { - /* if we are stable, setup a new grow state */ - /* otherwise continue with the already setup shrink one */ - /* but in backward direction */ - if (hashlin->state == TOMMY_HASHLIN_STATE_STABLE) { - tommy_hashlin_node** segment; - - /* set the lower size */ - hashlin->low_max = hashlin->bucket_max; - hashlin->low_mask = hashlin->bucket_mask; - - /* allocate the new vector using malloc() and not calloc() */ - /* because data is fully initialized in the split process */ - segment = tommy_cast(tommy_hashlin_node**, tommy_malloc(hashlin->low_max * sizeof(tommy_hashlin_node*))); - - /* store it adjusting the offset */ - /* cast to ptrdiff_t to ensure to get a negative value */ - hashlin->bucket[hashlin->bucket_bit] = &segment[-(tommy_ptrdiff_t)hashlin->low_max]; - - /* grow the hash size */ - ++hashlin->bucket_bit; - hashlin->bucket_max = 1 << hashlin->bucket_bit; - hashlin->bucket_mask = hashlin->bucket_max - 1; - - /* start from the beginning going forward */ - hashlin->split = 0; - } - - /* grow state */ - hashlin->state = TOMMY_HASHLIN_STATE_GROW; - } - - /* if we are growing */ - if (hashlin->state == TOMMY_HASHLIN_STATE_GROW) { - /* compute the split target required to finish the reallocation before the next resize */ - tommy_count_t split_target = 2 * hashlin->count; - - /* reallocate buckets until the split target */ - while (hashlin->split + hashlin->low_max < split_target) { - tommy_hashlin_node** split[2]; - tommy_hashlin_node* j; - tommy_count_t mask; - - /* get the low bucket */ - split[0] = tommy_hashlin_pos(hashlin, hashlin->split); - - /* get the high bucket */ - split[1] = tommy_hashlin_pos(hashlin, hashlin->split + hashlin->low_max); - - /* save the low bucket */ - j = *split[0]; - - /* reinitialize the buckets */ - *split[0] = 0; - *split[1] = 0; - - /* the bit used to identify the bucket */ - mask = hashlin->low_max; - - /* flush the bucket */ - while (j) { - tommy_hashlin_node* j_next = j->next; - tommy_count_t pos = (j->key & mask) != 0; - if (*split[pos]) - tommy_list_insert_tail_not_empty(*split[pos], j); - else - tommy_list_insert_first(split[pos], j); - j = j_next; - } - - /* go forward */ - ++hashlin->split; - - /* if we have finished, change the state */ - if (hashlin->split == hashlin->low_max) { - /* go in stable mode */ - tommy_hashlin_stable(hashlin); - break; - } - } - } -} - -/** - * Shrink one step. - */ -tommy_inline void hashlin_shrink_step(tommy_hashlin* hashlin) -{ - /* shrink if less than 12.5% full */ - if (hashlin->state != TOMMY_HASHLIN_STATE_SHRINK - && hashlin->count < hashlin->bucket_max / 8 - ) { - /* avoid to shrink the first bucket */ - if (hashlin->bucket_bit > TOMMY_HASHLIN_BIT) { - /* if we are stable, setup a new shrink state */ - /* otherwise continue with the already setup grow one */ - /* but in backward direction */ - if (hashlin->state == TOMMY_HASHLIN_STATE_STABLE) { - /* set the lower size */ - hashlin->low_max = hashlin->bucket_max / 2; - hashlin->low_mask = hashlin->bucket_mask / 2; - - /* start from the half going backward */ - hashlin->split = hashlin->low_max; - } - - /* start reallocation */ - hashlin->state = TOMMY_HASHLIN_STATE_SHRINK; - } - } - - /* if we are shrinking */ - if (hashlin->state == TOMMY_HASHLIN_STATE_SHRINK) { - /* compute the split target required to finish the reallocation before the next resize */ - tommy_count_t split_target = 8 * hashlin->count; - - /* reallocate buckets until the split target */ - while (hashlin->split + hashlin->low_max > split_target) { - tommy_hashlin_node** split[2]; - - /* go backward position */ - --hashlin->split; - - /* get the low bucket */ - split[0] = tommy_hashlin_pos(hashlin, hashlin->split); - - /* get the high bucket */ - split[1] = tommy_hashlin_pos(hashlin, hashlin->split + hashlin->low_max); - - /* concat the high bucket into the low one */ - tommy_list_concat(split[0], split[1]); - - /* if we have finished, clean up and change the state */ - if (hashlin->split == 0) { - tommy_hashlin_node** segment; - - /* shrink the hash size */ - --hashlin->bucket_bit; - hashlin->bucket_max = 1 << hashlin->bucket_bit; - hashlin->bucket_mask = hashlin->bucket_max - 1; - - /* free the last segment */ - segment = hashlin->bucket[hashlin->bucket_bit]; - tommy_free(&segment[((tommy_ptrdiff_t)1) << hashlin->bucket_bit]); - - /* go in stable mode */ - tommy_hashlin_stable(hashlin); - break; - } - } - } -} - -void tommy_hashlin_insert(tommy_hashlin* hashlin, tommy_hashlin_node* node, void* data, tommy_hash_t hash) -{ - tommy_list_insert_tail(tommy_hashlin_bucket_ref(hashlin, hash), node, data); - - node->key = hash; - - ++hashlin->count; - - hashlin_grow_step(hashlin); -} - -void* tommy_hashlin_remove_existing(tommy_hashlin* hashlin, tommy_hashlin_node* node) -{ - tommy_list_remove_existing(tommy_hashlin_bucket_ref(hashlin, node->key), node); - - --hashlin->count; - - hashlin_shrink_step(hashlin); - - return node->data; -} - -void* tommy_hashlin_remove(tommy_hashlin* hashlin, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash) -{ - tommy_hashlin_node** let_ptr = tommy_hashlin_bucket_ref(hashlin, hash); - tommy_hashlin_node* node = *let_ptr; - - while (node) { - /* we first check if the hash matches, as in the same bucket we may have multiples hash values */ - if (node->key == hash && cmp(cmp_arg, node->data) == 0) { - tommy_list_remove_existing(let_ptr, node); - - --hashlin->count; - - hashlin_shrink_step(hashlin); - - return node->data; - } - node = node->next; - } - - return 0; -} - -void tommy_hashlin_foreach(tommy_hashlin* hashlin, tommy_foreach_func* func) -{ - tommy_count_t bucket_max; - tommy_count_t pos; - - /* number of valid buckets */ - bucket_max = hashlin->low_max + hashlin->split; - - for (pos = 0; pos < bucket_max; ++pos) { - tommy_hashlin_node* node = *tommy_hashlin_pos(hashlin, pos); - - while (node) { - void* data = node->data; - node = node->next; - func(data); - } - } -} - -void tommy_hashlin_foreach_arg(tommy_hashlin* hashlin, tommy_foreach_arg_func* func, void* arg) -{ - tommy_count_t bucket_max; - tommy_count_t pos; - - /* number of valid buckets */ - bucket_max = hashlin->low_max + hashlin->split; - - for (pos = 0; pos < bucket_max; ++pos) { - tommy_hashlin_node* node = *tommy_hashlin_pos(hashlin, pos); - - while (node) { - void* data = node->data; - node = node->next; - func(arg, data); - } - } -} - -tommy_size_t tommy_hashlin_memory_usage(tommy_hashlin* hashlin) -{ - return hashlin->bucket_max * (tommy_size_t)sizeof(hashlin->bucket[0][0]) - + hashlin->count * (tommy_size_t)sizeof(tommy_hashlin_node); -} - diff --git a/tommyds/tommyds/tommyhashlin.h b/tommyds/tommyds/tommyhashlin.h deleted file mode 100644 index 477b1e7..0000000 --- a/tommyds/tommyds/tommyhashlin.h +++ /dev/null @@ -1,349 +0,0 @@ -/* - * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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. - */ - -/** \file - * Linear chained hashtable. - * - * This hashtable resizes dynamically and progressively using a variation of the - * linear hashing algorithm described in http://en.wikipedia.org/wiki/Linear_hashing - * - * It starts with the minimal size of 16 buckets, it doubles the size then it - * reaches a load factor greater than 0.5 and it halves the size with a load - * factor lower than 0.125. - * - * The progressive resize is good for real-time and interactive applications - * as it makes insert and delete operations taking always the same time. - * - * For resizing it's used a dynamic array that supports access to not contigous - * segments. - * In this way we only allocate additional table segments on the heap, without - * freeing the previous table, and then not increasing the heap fragmentation. - * - * The resize takes place inside tommy_hashlin_insert() and tommy_hashlin_remove(). - * No resize is done in the tommy_hashlin_search() operation. - * - * To initialize the hashtable you have to call tommy_hashlin_init(). - * - * \code - * tommy_hashslin hashlin; - * - * tommy_hashlin_init(&hashlin); - * \endcode - * - * To insert elements in the hashtable you have to call tommy_hashlin_insert() for - * each element. - * In the insertion call you have to specify the address of the node, the - * address of the object, and the hash value of the key to use. - * The address of the object is used to initialize the tommy_node::data field - * of the node, and the hash to initialize the tommy_node::key field. - * - * \code - * struct object { - * tommy_node node; - * // other fields - * int value; - * }; - * - * struct object* obj = malloc(sizeof(struct object)); // creates the object - * - * obj->value = ...; // initializes the object - * - * tommy_hashlin_insert(&hashlin, &obj->node, obj, tommy_inthash_u32(obj->value)); // inserts the object - * \endcode - * - * To find and element in the hashtable you have to call tommy_hashtable_search() - * providing a comparison function, its argument, and the hash of the key to search. - * - * \code - * int compare(const void* arg, const void* obj) - * { - * return *(const int*)arg != ((const struct object*)obj)->value; - * } - * - * int value_to_find = 1; - * struct object* obj = tommy_hashlin_search(&hashlin, compare, &value_to_find, tommy_inthash_u32(value_to_find)); - * if (!obj) { - * // not found - * } else { - * // found - * } - * \endcode - * - * To iterate over all the elements in the hashtable with the same key, you have to - * use tommy_hashlin_bucket() and follow the tommy_node::next pointer until NULL. - * You have also to check explicitely for the key, as the bucket may contains - * different keys. - * - * \code - * int value_to_find = 1; - * tommy_node* i = tommy_hashlin_bucket(&hashlin, tommy_inthash_u32(value_to_find)); - * while (i) { - * struct object* obj = i->data; // gets the object pointer - * - * if (obj->value == value_to_find) { - * printf("%d\n", obj->value); // process the object - * } - * - * i = i->next; // goes to the next element - * } - * \endcode - * - * To remove an element from the hashtable you have to call tommy_hashlin_remove() - * providing a comparison function, its argument, and the hash of the key to search - * and remove. - * - * \code - * struct object* obj = tommy_hashlin_remove(&hashlin, compare, &value_to_remove, tommy_inthash_u32(value_to_remove)); - * if (obj) { - * free(obj); // frees the object allocated memory - * } - * \endcode - * - * To destroy the hashtable you have to remove all the elements, and deinitialize - * the hashtable calling tommy_hashlin_done(). - * - * \code - * tommy_hashlin_done(&hashlin); - * \endcode - * - * If you need to iterate over all the elements in the hashtable, you can use - * tommy_hashlin_foreach() or tommy_hashlin_foreach_arg(). - * If you need a more precise control with a real iteration, you have to insert - * all the elements also in a ::tommy_list, and use the list to iterate. - * See the \ref multiindex example for more detail. - */ - -#ifndef __TOMMYHASHLIN_H -#define __TOMMYHASHLIN_H - -#include "tommyhash.h" - -/******************************************************************************/ -/* hashlin */ - -/** \internal - * Initial and minimal size of the hashtable expressed as a power of 2. - * The initial size is 2^TOMMY_HASHLIN_BIT. - */ -#define TOMMY_HASHLIN_BIT 6 - -/** - * Hashtable node. - * This is the node that you have to include inside your objects. - */ -typedef tommy_node tommy_hashlin_node; - -/** \internal - * Max number of elements as a power of 2. - */ -#define TOMMY_HASHLIN_BIT_MAX 32 - -/** - * Hashtable container type. - * \note Don't use internal fields directly, but access the container only using functions. - */ -typedef struct tommy_hashlin_struct { - tommy_hashlin_node** bucket[TOMMY_HASHLIN_BIT_MAX]; /**< Dynamic array of hash buckets. One list for each hash modulus. */ - tommy_uint_t bucket_bit; /**< Bits used in the bit mask. */ - tommy_count_t bucket_max; /**< Number of buckets. */ - tommy_count_t bucket_mask; /**< Bit mask to access the buckets. */ - tommy_count_t low_max; /**< Low order max value. */ - tommy_count_t low_mask; /**< Low order mask value. */ - tommy_count_t split; /**< Split position. */ - tommy_count_t count; /**< Number of elements. */ - tommy_uint_t state; /**< Reallocation state. */ -} tommy_hashlin; - -/** - * Initializes the hashtable. - */ -void tommy_hashlin_init(tommy_hashlin* hashlin); - -/** - * Deinitializes the hashtable. - * - * You can call this function with elements still contained, - * but such elements are not going to be freed by this call. - */ -void tommy_hashlin_done(tommy_hashlin* hashlin); - -/** - * Inserts an element in the hashtable. - */ -void tommy_hashlin_insert(tommy_hashlin* hashlin, tommy_hashlin_node* node, void* data, tommy_hash_t hash); - -/** - * Searches and removes an element from the hashtable. - * You have to provide a compare function and the hash of the element you want to remove. - * If the element is not found, 0 is returned. - * If more equal elements are present, the first one is removed. - * \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one. - * The function should return 0 for equal elements, anything other for different elements. - * \param cmp_arg Compare argument passed as first argument of the compare function. - * \param hash Hash of the element to find and remove. - * \return The removed element, or 0 if not found. - */ -void* tommy_hashlin_remove(tommy_hashlin* hashlin, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash); - -/** \internal - * Returns the bucket at the specified position. - */ -tommy_inline tommy_hashlin_node** tommy_hashlin_pos(tommy_hashlin* hashlin, tommy_hash_t pos) -{ - tommy_uint_t bsr; - - /* get the highest bit set, in case of all 0, return 0 */ - bsr = tommy_ilog2_u32(pos | 1); - - return &hashlin->bucket[bsr][pos]; -} - -/** \internal - * Returns a pointer to the bucket of the specified hash. - */ -tommy_inline tommy_hashlin_node** tommy_hashlin_bucket_ref(tommy_hashlin* hashlin, tommy_hash_t hash) -{ - tommy_count_t pos; - tommy_count_t high_pos; - - pos = hash & hashlin->low_mask; - high_pos = hash & hashlin->bucket_mask; - - /* if this position is already allocated in the high half */ - if (pos < hashlin->split) { - /* The following assigment is expected to be implemented */ - /* with a conditional move instruction */ - /* that results in a little better and constant performance */ - /* regardless of the split position. */ - /* This affects mostly the worst case, when the split value */ - /* is near at its half, resulting in a totally unpredictable */ - /* condition by the CPU. */ - /* In such case the use of the conditional move is generally faster. */ - - /* use also the high bit */ - pos = high_pos; - } - - return tommy_hashlin_pos(hashlin, pos); -} - -/** - * Gets the bucket of the specified hash. - * The bucket is guaranteed to contain ALL the elements with the specified hash, - * but it can contain also others. - * You can access elements in the bucket following the ::next pointer until 0. - * \param hash Hash of the element to find. - * \return The head of the bucket, or 0 if empty. - */ -tommy_inline tommy_hashlin_node* tommy_hashlin_bucket(tommy_hashlin* hashlin, tommy_hash_t hash) -{ - return *tommy_hashlin_bucket_ref(hashlin, hash); -} - -/** - * Searches an element in the hashtable. - * You have to provide a compare function and the hash of the element you want to find. - * If more equal elements are present, the first one is returned. - * \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one. - * The function should return 0 for equal elements, anything other for different elements. - * \param cmp_arg Compare argument passed as first argument of the compare function. - * \param hash Hash of the element to find. - * \return The first element found, or 0 if none. - */ -tommy_inline void* tommy_hashlin_search(tommy_hashlin* hashlin, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash) -{ - tommy_hashlin_node* i = tommy_hashlin_bucket(hashlin, hash); - - while (i) { - /* we first check if the hash matches, as in the same bucket we may have multiples hash values */ - if (i->key == hash && cmp(cmp_arg, i->data) == 0) - return i->data; - i = i->next; - } - return 0; -} - -/** - * Removes an element from the hashtable. - * You must already have the address of the element to remove. - * \return The tommy_node::data field of the node removed. - */ -void* tommy_hashlin_remove_existing(tommy_hashlin* hashlin, tommy_hashlin_node* node); - -/** - * Calls the specified function for each element in the hashtable. - * - * You can use this function to deallocate all the elements inserted. - * - * \code - * tommy_hashlin hashlin; - * - * // initializes the hashtable - * tommy_hashlin_init(&hashlin); - * - * ... - * - * // creates an object - * struct object* obj = malloc(sizeof(struct object)); - * - * ... - * - * // insert it in the hashtable - * tommy_hashlin_insert(&hashlin, &obj->node, obj, tommy_inthash_u32(obj->value)); - * - * ... - * - * // deallocates all the objects iterating the hashtable - * tommy_hashlin_foreach(&hashlin, free); - * - * // deallocates the hashtable - * tommy_hashlin_done(&hashlin); - * \endcode - */ -void tommy_hashlin_foreach(tommy_hashlin* hashlin, tommy_foreach_func* func); - -/** - * Calls the specified function with an argument for each element in the hashtable. - */ -void tommy_hashlin_foreach_arg(tommy_hashlin* hashlin, tommy_foreach_arg_func* func, void* arg); - -/** - * Gets the number of elements. - */ -tommy_inline tommy_count_t tommy_hashlin_count(tommy_hashlin* hashlin) -{ - return hashlin->count; -} - -/** - * Gets the size of allocated memory. - * It includes the size of the ::tommy_hashlin_node of the stored elements. - */ -tommy_size_t tommy_hashlin_memory_usage(tommy_hashlin* hashlin); - -#endif - diff --git a/tommyds/tommyds/tommyhashtbl.c b/tommyds/tommyds/tommyhashtbl.c deleted file mode 100644 index 007b8ec..0000000 --- a/tommyds/tommyds/tommyhashtbl.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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 "tommyhashtbl.h" -#include "tommylist.h" - -#include /* for memset */ - -/******************************************************************************/ -/* hashtable */ - -void tommy_hashtable_init(tommy_hashtable* hashtable, tommy_count_t bucket_max) -{ - if (bucket_max < 16) - bucket_max = 16; - else - bucket_max = tommy_roundup_pow2_u32(bucket_max); - - hashtable->bucket_max = bucket_max; - hashtable->bucket_mask = hashtable->bucket_max - 1; - - /* initialize the vector using malloc()+memset() instead of calloc() */ - /* to ensure that all the memory in really allocated immediately */ - /* by the OS, and not deferred at later time. */ - /* this improves performance, because we start with a fully initialized hashtable. */ - hashtable->bucket = tommy_cast(tommy_hashtable_node**, tommy_malloc(hashtable->bucket_max * sizeof(tommy_hashtable_node*))); - memset(hashtable->bucket, 0, hashtable->bucket_max * sizeof(tommy_hashtable_node*)); - - hashtable->count = 0; -} - -void tommy_hashtable_done(tommy_hashtable* hashtable) -{ - tommy_free(hashtable->bucket); -} - -void tommy_hashtable_insert(tommy_hashtable* hashtable, tommy_hashtable_node* node, void* data, tommy_hash_t hash) -{ - tommy_count_t pos = hash & hashtable->bucket_mask; - - tommy_list_insert_tail(&hashtable->bucket[pos], node, data); - - node->key = hash; - - ++hashtable->count; -} - -void* tommy_hashtable_remove_existing(tommy_hashtable* hashtable, tommy_hashtable_node* node) -{ - tommy_count_t pos = node->key & hashtable->bucket_mask; - - tommy_list_remove_existing(&hashtable->bucket[pos], node); - - --hashtable->count; - - return node->data; -} - -void* tommy_hashtable_remove(tommy_hashtable* hashtable, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash) -{ - tommy_count_t pos = hash & hashtable->bucket_mask; - tommy_hashtable_node* node = hashtable->bucket[pos]; - - while (node) { - /* we first check if the hash matches, as in the same bucket we may have multiples hash values */ - if (node->key == hash && cmp(cmp_arg, node->data) == 0) { - tommy_list_remove_existing(&hashtable->bucket[pos], node); - - --hashtable->count; - - return node->data; - } - node = node->next; - } - - return 0; -} - -void tommy_hashtable_foreach(tommy_hashtable* hashtable, tommy_foreach_func* func) -{ - tommy_count_t bucket_max = hashtable->bucket_max; - tommy_hashtable_node** bucket = hashtable->bucket; - tommy_count_t pos; - - for (pos = 0; pos < bucket_max; ++pos) { - tommy_hashtable_node* node = bucket[pos]; - - while (node) { - void* data = node->data; - node = node->next; - func(data); - } - } -} - -void tommy_hashtable_foreach_arg(tommy_hashtable* hashtable, tommy_foreach_arg_func* func, void* arg) -{ - tommy_count_t bucket_max = hashtable->bucket_max; - tommy_hashtable_node** bucket = hashtable->bucket; - tommy_count_t pos; - - for (pos = 0; pos < bucket_max; ++pos) { - tommy_hashtable_node* node = bucket[pos]; - - while (node) { - void* data = node->data; - node = node->next; - func(arg, data); - } - } -} - -tommy_size_t tommy_hashtable_memory_usage(tommy_hashtable* hashtable) -{ - return hashtable->bucket_max * (tommy_size_t)sizeof(hashtable->bucket[0]) - + tommy_hashtable_count(hashtable) * (tommy_size_t)sizeof(tommy_hashtable_node); -} - diff --git a/tommyds/tommyds/tommyhashtbl.h b/tommyds/tommyds/tommyhashtbl.h deleted file mode 100644 index 76e8062..0000000 --- a/tommyds/tommyds/tommyhashtbl.h +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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. - */ - -/** \file - * Fixed size chained hashtable. - * - * This hashtable is a standard implementation of a chained hashtable with a fixed size. - * - * Note that performances starts to degenerate after reaching a load factor greater than 0.75. - * The ::tommy_hashdyn and ::tommy_hashlin hashtables fix this problem growing dynamically. - * - * To initialize the hashtable you have to call tommy_hashtable_init() specifing - * the fixed bucket size. - * - * \code - * tommy_hashslin hashtable; - * - * tommy_hashtable_init(&hashtable, 1024); - * \endcode - * - * To insert elements in the hashtable you have to call tommy_hashtable_insert() for - * each element. - * In the insertion call you have to specify the address of the node, the - * address of the object, and the hash value of the key to use. - * The address of the object is used to initialize the tommy_node::data field - * of the node, and the hash to initialize the tommy_node::key field. - * - * \code - * struct object { - * tommy_node node; - * // other fields - * int value; - * }; - * - * struct object* obj = malloc(sizeof(struct object)); // creates the object - * - * obj->value = ...; // initializes the object - * - * tommy_hashtable_insert(&hashtable, &obj->node, obj, tommy_inthash_u32(obj->value)); // inserts the object - * \endcode - * - * To find and element in the hashtable you have to call tommy_hashtable_search() - * providing a comparison function, its argument, and the hash of the key to search. - * - * \code - * int compare(const void* arg, const void* obj) - * { - * return *(const int*)arg != ((const struct object*)obj)->value; - * } - * - * int value_to_find = 1; - * struct object* obj = tommy_hashtable_search(&hashtable, compare, &value_to_find, tommy_inthash_u32(value_to_find)); - * if (!obj) { - * // not found - * } else { - * // found - * } - * \endcode - * - * To iterate over all the elements in the hashtable with the same key, you have to - * use tommy_hashtable_bucket() and follow the tommy_node::next pointer until NULL. - * You have also to check explicitely for the key, as the bucket may contains - * different keys. - * - * \code - * tommy_node* i = tommy_hashtable_bucket(&hashtable, tommy_inthash_u32(value_to_find)); - * while (i) { - * struct object* obj = i->data; // gets the object pointer - * - * if (obj->value == value_to_find) { - * printf("%d\n", obj->value); // process the object - * } - * - * i = i->next; // goes to the next element - * } - * \endcode - * - * To remove an element from the hashtable you have to call tommy_hashtable_remove() - * providing a comparison function, its argument, and the hash of the key to search - * and remove. - * - * \code - * struct object* obj = tommy_hashtable_remove(&hashtable, compare, &value_to_remove, tommy_inthash_u32(value_to_remove)); - * if (obj) { - * free(obj); // frees the object allocated memory - * } - * \endcode - * - * To destroy the hashtable you have to remove all the elements, and deinitialize - * the hashtable calling tommy_hashtable_done(). - * - * \code - * tommy_hashtable_done(&hashtable); - * \endcode - * - * If you need to iterate over all the elements in the hashtable, you can use - * tommy_hashtable_foreach() or tommy_hashtable_foreach_arg(). - * If you need a more precise control with a real iteration, you have to insert - * all the elements also in a ::tommy_list, and use the list to iterate. - * See the \ref multiindex example for more detail. - */ - -#ifndef __TOMMYHASHTBL_H -#define __TOMMYHASHTBL_H - -#include "tommyhash.h" - -/******************************************************************************/ -/* hashtable */ - -/** - * Hashtable node. - * This is the node that you have to include inside your objects. - */ -typedef tommy_node tommy_hashtable_node; - -/** - * Hashtable container type. - * \note Don't use internal fields directly, but access the container only using functions. - */ -typedef struct tommy_hashtable_struct { - tommy_hashtable_node** bucket; /**< Hash buckets. One list for each hash modulus. */ - tommy_count_t bucket_max; /**< Number of buckets. */ - tommy_count_t bucket_mask; /**< Bit mask to access the buckets. */ - tommy_count_t count; /**< Number of elements. */ -} tommy_hashtable; - -/** - * Initializes the hashtable. - * \param buckets Minimum number of buckets to allocate. The effective number used is the next power of 2. - */ -void tommy_hashtable_init(tommy_hashtable* hashtable, tommy_count_t bucket_max); - -/** - * Deinitializes the hashtable. - * - * You can call this function with elements still contained, - * but such elements are not going to be freed by this call. - */ -void tommy_hashtable_done(tommy_hashtable* hashtable); - -/** - * Inserts an element in the hashtable. - */ -void tommy_hashtable_insert(tommy_hashtable* hashtable, tommy_hashtable_node* node, void* data, tommy_hash_t hash); - -/** - * Searches and removes an element from the hashtable. - * You have to provide a compare function and the hash of the element you want to remove. - * If the element is not found, 0 is returned. - * If more equal elements are present, the first one is removed. - * \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one. - * The function should return 0 for equal elements, anything other for different elements. - * \param cmp_arg Compare argument passed as first argument of the compare function. - * \param hash Hash of the element to find and remove. - * \return The removed element, or 0 if not found. - */ -void* tommy_hashtable_remove(tommy_hashtable* hashtable, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash); - -/** - * Gets the bucket of the specified hash. - * The bucket is guaranteed to contain ALL the elements with the specified hash, - * but it can contain also others. - * You can access elements in the bucket following the ::next pointer until 0. - * \param hash Hash of the element to find. - * \return The head of the bucket, or 0 if empty. - */ -tommy_inline tommy_hashtable_node* tommy_hashtable_bucket(tommy_hashtable* hashtable, tommy_hash_t hash) -{ - return hashtable->bucket[hash & hashtable->bucket_mask]; -} - -/** - * Searches an element in the hashtable. - * You have to provide a compare function and the hash of the element you want to find. - * If more equal elements are present, the first one is returned. - * \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one. - * The function should return 0 for equal elements, anything other for different elements. - * \param cmp_arg Compare argument passed as first argument of the compare function. - * \param hash Hash of the element to find. - * \return The first element found, or 0 if none. - */ -tommy_inline void* tommy_hashtable_search(tommy_hashtable* hashtable, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash) -{ - tommy_hashtable_node* i = tommy_hashtable_bucket(hashtable, hash); - - while (i) { - /* we first check if the hash matches, as in the same bucket we may have multiples hash values */ - if (i->key == hash && cmp(cmp_arg, i->data) == 0) - return i->data; - i = i->next; - } - return 0; -} - -/** - * Removes an element from the hashtable. - * You must already have the address of the element to remove. - * \return The tommy_node::data field of the node removed. - */ -void* tommy_hashtable_remove_existing(tommy_hashtable* hashtable, tommy_hashtable_node* node); - -/** - * Calls the specified function for each element in the hashtable. - * - * You can use this function to deallocate all the elements inserted. - * - * \code - * tommy_hashtable hashtable; - * - * // initializes the hashtable - * tommy_hashtable_init(&hashtable, ...); - * - * ... - * - * // creates an object - * struct object* obj = malloc(sizeof(struct object)); - * - * ... - * - * // insert it in the hashtable - * tommy_hashdyn_insert(&hashtable, &obj->node, obj, tommy_inthash_u32(obj->value)); - * - * ... - * - * // deallocates all the objects iterating the hashtable - * tommy_hashtable_foreach(&hashtable, free); - * - * // deallocates the hashtable - * tommy_hashdyn_done(&hashtable); - * \endcode - */ -void tommy_hashtable_foreach(tommy_hashtable* hashtable, tommy_foreach_func* func); - -/** - * Calls the specified function with an argument for each element in the hashtable. - */ -void tommy_hashtable_foreach_arg(tommy_hashtable* hashtable, tommy_foreach_arg_func* func, void* arg); - -/** - * Gets the number of elements. - */ -tommy_inline tommy_count_t tommy_hashtable_count(tommy_hashtable* hashtable) -{ - return hashtable->count; -} - -/** - * Gets the size of allocated memory. - * It includes the size of the ::tommy_hashtable_node of the stored elements. - */ -tommy_size_t tommy_hashtable_memory_usage(tommy_hashtable* hashtable); - -#endif - diff --git a/tommyds/tommyds/tommylist.c b/tommyds/tommyds/tommylist.c deleted file mode 100644 index ea85b08..0000000 --- a/tommyds/tommyds/tommylist.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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 "tommylist.h" -#include "tommychain.h" - -void tommy_list_concat(tommy_list* first, tommy_list* second) -{ - tommy_node* first_head; - tommy_node* first_tail; - tommy_node* second_head; - - if (tommy_list_empty(second)) - return; - - if (tommy_list_empty(first)) { - *first = *second; - return; - } - - first_head = tommy_list_head(first); - second_head = tommy_list_head(second); - first_tail = tommy_list_tail(first); - - /* set the "circular" prev list */ - first_head->prev = second_head->prev; - second_head->prev = first_tail; - - /* set the "0 terminated" next list */ - first_tail->next = second_head; -} - -/** \internal - * Setup a list. - */ -tommy_inline void tommy_list_set(tommy_list* list, tommy_node* head, tommy_node* tail) -{ - head->prev = tail; - tail->next = 0; - *list = head; -} - -void tommy_list_sort(tommy_list* list, tommy_compare_func* cmp) -{ - tommy_chain chain; - tommy_node* head; - - if (tommy_list_empty(list)) - return; - - head = tommy_list_head(list); - - /* create a chain from the list */ - chain.head = head; - chain.tail = head->prev; - - tommy_chain_mergesort(&chain, cmp); - - /* restore the list */ - tommy_list_set(list, chain.head, chain.tail); -} - diff --git a/tommyds/tommyds/tommylist.h b/tommyds/tommyds/tommylist.h deleted file mode 100644 index 8004a44..0000000 --- a/tommyds/tommyds/tommylist.h +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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. - */ - -/** \file - * Double linked list for collisions into hashtables. - * - * This list is a double linked list mainly targetted for handling collisions - * into an hashtables, but useable also as a generic list. - * - * The main feature of this list is to require only one pointer to represent the - * list, compared to a classic implementation requiring a head an a tail pointers. - * This reduces the memory usage in hashtables. - * - * Another feature is to support the insertion at the end of the list. This allow to store - * collisions in a stable order. Where for stable order we mean that equal elements keep - * their insertion order. - * - * To initialize the list, you have to call tommy_list_init(), or to simply assign - * to it NULL, as an empty list is represented by the NULL value. - * - * \code - * tommy_list list; - * - * tommy_list_init(&list); // initializes the list - * \endcode - * - * To insert elements in the list you have to call tommy_list_insert_tail() - * or tommy_list_insert_head() for each element. - * In the insertion call you have to specify the address of the node and the - * address of the object. - * The address of the object is used to initialize the tommy_node::data field - * of the node. - * - * \code - * struct object { - * tommy_node node; - * // other fields - * int value; - * }; - * - * struct object* obj = malloc(sizeof(struct object)); // creates the object - * - * obj->value = ...; // initializes the object - * - * tommy_list_insert_tail(&list, &obj->node, obj); // inserts the object - * \endcode - * - * To iterate over all the elements in the list you have to call - * tommy_list_head() to get the head of the list and follow the - * tommy_node::next pointer until NULL. - * - * \code - * tommy_node* i = tommy_list_head(&list); - * while (i) { - * struct object* obj = i->data; // gets the object pointer - * - * printf("%d\n", obj->value); // process the object - * - * i = i->next; // go to the next element - * } - * \endcode - * - * To destroy the list you have to remove all the elements, - * as the list is completely inplace and it doesn't allocate memory. - * This can be done with the tommy_list_foreach() function. - * - * \code - * // deallocates all the objects iterating the list - * tommy_list_foreach(&list, free); - * \endcode - */ - -#ifndef __TOMMYLIST_H -#define __TOMMYLIST_H - -#include "tommytypes.h" - -/******************************************************************************/ -/* list */ - -/** - * Double linked list type. - */ -typedef tommy_node* tommy_list; - -/** - * Initializes the list. - * The list is completely inplace, so it doesn't need to be deinitialized. - */ -tommy_inline void tommy_list_init(tommy_list* list) -{ - *list = 0; -} - -/** - * Gets the head of the list. - * \return The head node. For empty lists 0 is returned. - */ -tommy_inline tommy_node* tommy_list_head(tommy_list* list) -{ - return *list; -} - -/** - * Gets the tail of the list. - * \return The tail node. For empty lists 0 is returned. - */ -tommy_inline tommy_node* tommy_list_tail(tommy_list* list) -{ - tommy_node* head = tommy_list_head(list); - - if (!head) - return 0; - - return head->prev; -} - -/** \internal - * Creates a new list with a single element. - * \param list The list to initialize. - * \param node The node to insert. - */ -tommy_inline void tommy_list_insert_first(tommy_list* list, tommy_node* node) -{ - /* one element "circular" prev list */ - node->prev = node; - - /* one element "0 terminated" next list */ - node->next = 0; - - *list = node; -} - -/** \internal - * Inserts an element at the head of a not empty list. - * The element is inserted at the head of the list. The list cannot be empty. - * \param list The list. The list cannot be empty. - * \param node The node to insert. - */ -tommy_inline void tommy_list_insert_head_not_empty(tommy_list* list, tommy_node* node) -{ - tommy_node* head = tommy_list_head(list); - - /* insert in the "circular" prev list */ - node->prev = head->prev; - head->prev = node; - - /* insert in the "0 terminated" next list */ - node->next = head; - - *list = node; -} - -/** \internal - * Inserts an element at the tail of a not empty list. - * The element is inserted at the tail of the list. The list cannot be empty. - * \param head The node at the list head. It cannot be 0. - * \param node The node to insert. - */ -tommy_inline void tommy_list_insert_tail_not_empty(tommy_node* head, tommy_node* node) -{ - /* insert in the "circular" prev list */ - node->prev = head->prev; - head->prev = node; - - /* insert in the "0 terminated" next list */ - node->next = 0; - node->prev->next = node; -} - -/** - * Inserts an element at the head of a list. - * \param node The node to insert. - * \param data The object containing the node. It's used to set the tommy_node::data field of the node. - */ -tommy_inline void tommy_list_insert_head(tommy_list* list, tommy_node* node, void* data) -{ - tommy_node* head = tommy_list_head(list); - - if (head) - tommy_list_insert_head_not_empty(list, node); - else - tommy_list_insert_first(list, node); - - node->data = data; -} - -/** - * Inserts an element at the tail of a list. - * \param node The node to insert. - * \param data The object containing the node. It's used to set the tommy_node::data field of the node. - */ -tommy_inline void tommy_list_insert_tail(tommy_list* list, tommy_node* node, void* data) -{ - tommy_node* head = tommy_list_head(list); - - if (head) - tommy_list_insert_tail_not_empty(head, node); - else - tommy_list_insert_first(list, node); - - node->data = data; -} - -/** \internal - * Removes an element from the head of a not empty list. - * \param list The list. The list cannot be empty. - * \return The node removed. - */ -tommy_inline tommy_node* tommy_list_remove_head_not_empty(tommy_list* list) -{ - tommy_node* head = tommy_list_head(list); - - /* remove from the "circular" prev list */ - head->next->prev = head->prev; - - /* remove from the "0 terminated" next list */ - *list = head->next; /* the new head, in case 0 */ - - return head; -} - -/** - * Removes an element from the list. - * You must already have the address of the element to remove. - * \note The node content is left unchanged, including the tommy_node::next - * and tommy_node::prev fields that still contain pointers at the list. - * \param node The node to remove. The node must be in the list. - * \return The tommy_node::data field of the node removed. - */ -tommy_inline void* tommy_list_remove_existing(tommy_list* list, tommy_node* node) -{ - tommy_node* head = tommy_list_head(list); - - /* remove from the "circular" prev list */ - if (node->next) - node->next->prev = node->prev; - else - head->prev = node->prev; /* the last */ - - /* remove from the "0 terminated" next list */ - if (head == node) - *list = node->next; /* the new head, in case 0 */ - else - node->prev->next = node->next; - - return node->data; -} - -/** - * Concats two lists. - * The second list is concatenated at the first list. - * \param first The first list. - * \param second The second list. After this call the list content is undefined, - * and you should not use it anymore. - */ -void tommy_list_concat(tommy_list* first, tommy_list* second); - -/** - * Sorts a list. - * It's a stable merge sort with O(N*log(N)) worst complexity. - * It's faster on degenerated cases like partially ordered lists. - * \param cmp Compare function called with two elements. - * The function should return <0 if the first element is less than the second, ==0 if equal, and >0 if greather. - */ -void tommy_list_sort(tommy_list* list, tommy_compare_func* cmp); - -/** - * Checks if empty. - * \return If the list is empty. - */ -tommy_inline tommy_bool_t tommy_list_empty(tommy_list* list) -{ - return tommy_list_head(list) == 0; -} - -/** - * Gets the number of elements. - * \note This operation is O(n). - */ -tommy_inline tommy_count_t tommy_list_count(tommy_list* list) -{ - tommy_count_t count = 0; - tommy_node* i = tommy_list_head(list); - - while (i) { - ++count; - i = i->next; - } - - return count; -} - -/** - * Calls the specified function for each element in the list. - * - * You can use this function to deallocate all the elements - * inserted in a list. - * - * \code - * tommy_list list; - * - * // initializes the list - * tommy_list_init(&list); - * - * ... - * - * // creates an object - * struct object* obj = malloc(sizeof(struct object)); - * - * ... - * - * // insert it in the list - * tommy_list_insert_tail(&list, &obj->node, obj); - * - * ... - * - * // deallocates all the objects iterating the list - * tommy_list_foreach(&list, free); - * \endcode - */ -tommy_inline void tommy_list_foreach(tommy_list* list, tommy_foreach_func* func) -{ - tommy_node* node = tommy_list_head(list); - - while (node) { - void* data = node->data; - node = node->next; - func(data); - } -} - -/** - * Calls the specified function with an argument for each element in the list. - */ -tommy_inline void tommy_list_foreach_arg(tommy_list* list, tommy_foreach_arg_func* func, void* arg) -{ - tommy_node* node = tommy_list_head(list); - - while (node) { - void* data = node->data; - node = node->next; - func(arg, data); - } -} - -#endif - diff --git a/tommyds/tommyds/tommytrie.c b/tommyds/tommyds/tommytrie.c deleted file mode 100644 index 35ac219..0000000 --- a/tommyds/tommyds/tommytrie.c +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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 "tommytrie.h" -#include "tommylist.h" - -#include /* for assert */ - -/******************************************************************************/ -/* trie */ - -/** - * Mask for the inner branches. - */ -#define TOMMY_TRIE_TREE_MASK (TOMMY_TRIE_TREE_MAX - 1) - -/** - * Shift for the first level of branches. - */ -#define TOMMY_TRIE_BUCKET_SHIFT (TOMMY_KEY_BIT - TOMMY_TRIE_BUCKET_BIT) - -/** - * Max number of levels. - */ -#define TOMMY_TRIE_LEVEL_MAX ((TOMMY_KEY_BIT - TOMMY_TRIE_BUCKET_BIT) / TOMMY_TRIE_TREE_BIT) - -/** - * Hashtrie tree. - * A tree contains TOMMY_TRIE_TREE_MAX ordered pointers to . - * - * Each tree level uses exactly TOMMY_TRIE_TREE_BIT bits from the key. - */ -struct tommy_trie_tree_struct { - tommy_trie_node* map[TOMMY_TRIE_TREE_MAX]; -}; -typedef struct tommy_trie_tree_struct tommy_trie_tree; - -/** - * Kinds of an trie node. - */ -#define TOMMY_TRIE_TYPE_NODE 0 /**< The node is of type ::tommy_trie_node. */ -#define TOMMY_TRIE_TYPE_TREE 1 /**< The node is of type ::tommy_trie_tree. */ - -/** - * Get and set pointer of trie nodes. - * - * The pointer type is stored in the lower bit. - */ -#define trie_get_type(ptr) (((tommy_uintptr_t)(ptr)) & 1) -#define trie_get_tree(ptr) ((tommy_trie_tree*)(((tommy_uintptr_t)(ptr)) - TOMMY_TRIE_TYPE_TREE)) -#define trie_set_tree(ptr) (void*)(((tommy_uintptr_t)(ptr)) + TOMMY_TRIE_TYPE_TREE) - -void tommy_trie_init(tommy_trie* trie, tommy_allocator* alloc) -{ - tommy_uint_t i; - - for (i = 0; i < TOMMY_TRIE_BUCKET_MAX; ++i) - trie->bucket[i] = 0; - - trie->count = 0; - trie->node_count = 0; - - trie->alloc = alloc; -} - -static void trie_bucket_insert(tommy_trie* trie, tommy_uint_t shift, tommy_trie_node** let_ptr, tommy_trie_node* insert, tommy_key_t key) -{ - tommy_trie_tree* tree; - tommy_trie_node* node; - void* ptr; - tommy_uint_t i; - tommy_uint_t j; - -recurse: - ptr = *let_ptr; - - /* if null, just insert the node */ - if (!ptr) { - /* setup the node as a list */ - tommy_list_insert_first(let_ptr, insert); - return; - } - - if (trie_get_type(ptr) == TOMMY_TRIE_TYPE_TREE) { - /* repeat the process one level down */ - let_ptr = &trie_get_tree(ptr)->map[(key >> shift) & TOMMY_TRIE_TREE_MASK]; - shift -= TOMMY_TRIE_TREE_BIT; - goto recurse; - } - - node = tommy_cast(tommy_trie_node*, ptr); - - /* if it's the same key, insert in the list */ - if (node->key == key) { - tommy_list_insert_tail_not_empty(node, insert); - return; - } - -expand: - /* convert to a tree */ - tree = tommy_cast(tommy_trie_tree*, tommy_allocator_alloc(trie->alloc)); - ++trie->node_count; - *let_ptr = tommy_cast(tommy_trie_node*, trie_set_tree(tree)); - - /* initialize it */ - for (i = 0; i < TOMMY_TRIE_TREE_MAX; ++i) - tree->map[i] = 0; - - /* get the position of the two elements */ - i = (node->key >> shift) & TOMMY_TRIE_TREE_MASK; - j = (key >> shift) & TOMMY_TRIE_TREE_MASK; - - /* if they don't collide */ - if (i != j) { - /* insert the already existing element */ - tree->map[i] = node; - - /* insert the new node */ - tommy_list_insert_first(&tree->map[j], insert); - return; - } - - /* expand one more level */ - let_ptr = &tree->map[i]; - shift -= TOMMY_TRIE_TREE_BIT; - goto expand; -} - -void tommy_trie_insert(tommy_trie* trie, tommy_trie_node* node, void* data, tommy_key_t key) -{ - tommy_trie_node** let_ptr; - - node->data = data; - node->key = key; - - let_ptr = &trie->bucket[key >> TOMMY_TRIE_BUCKET_SHIFT]; - - trie_bucket_insert(trie, TOMMY_TRIE_BUCKET_SHIFT, let_ptr, node, key); - - ++trie->count; -} - -static tommy_trie_node* trie_bucket_remove_existing(tommy_trie* trie, tommy_uint_t shift, tommy_trie_node** let_ptr, tommy_trie_node* remove, tommy_key_t key) -{ - tommy_trie_node* node; - tommy_trie_tree* tree; - void* ptr; - tommy_trie_node** let_back[TOMMY_TRIE_LEVEL_MAX + 1]; - tommy_uint_t level; - tommy_uint_t i; - tommy_uint_t count; - tommy_uint_t last; - - level = 0; -recurse: - ptr = *let_ptr; - - if (!ptr) - return 0; - - if (trie_get_type(ptr) == TOMMY_TRIE_TYPE_TREE) { - tree = trie_get_tree(ptr); - - /* save the path */ - let_back[level++] = let_ptr; - - /* go down one level */ - let_ptr = &tree->map[(key >> shift) & TOMMY_TRIE_TREE_MASK]; - shift -= TOMMY_TRIE_TREE_BIT; - - goto recurse; - } - - node = tommy_cast(tommy_trie_node*, ptr); - - /* if the node to remove is not specified */ - if (!remove) { - /* remove the first */ - remove = node; - - /* check if it's really the element to remove */ - if (remove->key != key) - return 0; - } - - tommy_list_remove_existing(let_ptr, remove); - - /* if the list is not empty, try to reduce */ - if (*let_ptr || !level) - return remove; - -reduce: - /* go one level up */ - let_ptr = let_back[--level]; - - tree = trie_get_tree(*let_ptr); - - /* check if there is only one child node */ - count = 0; - last = 0; - for (i = 0; i < TOMMY_TRIE_TREE_MAX; ++i) { - if (tree->map[i]) { - /* if we have a sub tree, we cannot reduce */ - if (trie_get_type(tree->map[i]) != TOMMY_TRIE_TYPE_NODE) - return remove; - /* if more than one node, we cannot reduce */ - if (++count > 1) - return remove; - last = i; - } - } - - /* here count is never 0, as we cannot have a tree with only one sub node */ - assert(count == 1); - - *let_ptr = tree->map[last]; - - tommy_allocator_free(trie->alloc, tree); - --trie->node_count; - - /* repeat until more level */ - if (level) - goto reduce; - - return remove; -} - -void* tommy_trie_remove(tommy_trie* trie, tommy_key_t key) -{ - tommy_trie_node* ret; - tommy_trie_node** let_ptr; - - let_ptr = &trie->bucket[key >> TOMMY_TRIE_BUCKET_SHIFT]; - - ret = trie_bucket_remove_existing(trie, TOMMY_TRIE_BUCKET_SHIFT, let_ptr, 0, key); - - if (!ret) - return 0; - - --trie->count; - - return ret->data; -} - -void* tommy_trie_remove_existing(tommy_trie* trie, tommy_trie_node* node) -{ - tommy_trie_node* ret; - tommy_key_t key = node->key; - tommy_trie_node** let_ptr; - - let_ptr = &trie->bucket[key >> TOMMY_TRIE_BUCKET_SHIFT]; - - ret = trie_bucket_remove_existing(trie, TOMMY_TRIE_BUCKET_SHIFT, let_ptr, node, key); - - /* the element removed must match the one passed */ - assert(ret == node); - - --trie->count; - - return ret->data; -} - -tommy_trie_node* tommy_trie_bucket(tommy_trie* trie, tommy_key_t key) -{ - tommy_trie_node* node; - void* ptr; - tommy_uint_t type; - tommy_uint_t shift; - - ptr = trie->bucket[key >> TOMMY_TRIE_BUCKET_SHIFT]; - - shift = TOMMY_TRIE_BUCKET_SHIFT; - -recurse: - if (!ptr) - return 0; - - type = trie_get_type(ptr); - - switch (type) { - case TOMMY_TRIE_TYPE_NODE : - node = tommy_cast(tommy_trie_node*, ptr); - if (node->key != key) - return 0; - return node; - default : - case TOMMY_TRIE_TYPE_TREE : - ptr = trie_get_tree(ptr)->map[(key >> shift) & TOMMY_TRIE_TREE_MASK]; - shift -= TOMMY_TRIE_TREE_BIT; - goto recurse; - } -} - -tommy_size_t tommy_trie_memory_usage(tommy_trie* trie) -{ - return tommy_trie_count(trie) * (tommy_size_t)sizeof(tommy_trie_node) - + trie->node_count * (tommy_size_t)TOMMY_TRIE_BLOCK_SIZE; -} - diff --git a/tommyds/tommyds/tommytrie.h b/tommyds/tommyds/tommytrie.h deleted file mode 100644 index 6bf42a0..0000000 --- a/tommyds/tommyds/tommytrie.h +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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. - */ - -/** \file - * Trie optimized for cache utilization. - * - * This trie is a standard implementation that stores elements in the order defined - * by the key. - * - * It needs an external allocator for the inner nodes in the trie. - * - * You can control the number of branches of each node using the ::TOMMY_TRIE_TREE_MAX - * define. More branches imply more speed, but a bigger memory occupation. - * - * Compared to ::tommy_trie_inplace you have to provide a ::tommy_allocator allocator. - * Note that the C malloc() is too slow to futfill this role. - * - * To initialize the trie you have to call tommy_allocator_init() to initialize - * the allocator, and tommy_trie_init() for the trie. - * - * \code - * tommy_allocator alloc; - * tommy_trie trie; - * - * tommy_allocator_init(&alloc, TOMMY_TRIE_BLOCK_SIZE, TOMMY_TRIE_BLOCK_SIZE); - * - * tommy_trie_init(&trie, &alloc); - * \endcode - * - * To insert elements in the trie you have to call tommy_trie_insert() for - * each element. - * In the insertion call you have to specify the address of the node, the - * address of the object, and the key value to use. - * The address of the object is used to initialize the tommy_node::data field - * of the node, and the key to initialize the tommy_node::key field. - * - * \code - * struct object { - * tommy_node node; - * // other fields - * int value; - * }; - * - * struct object* obj = malloc(sizeof(struct object)); // creates the object - * - * obj->value = ...; // initializes the object - * - * tommy_trie_insert(&trie, &obj->node, obj, obj->value); // inserts the object - * \endcode - * - * To find and element in the trie you have to call tommy_trie_search() providing - * the key to search. - * - * \code - * int value_to_find = 1; - * struct object* obj = tommy_trie_search(&trie, value_to_find); - * if (!obj) { - * // not found - * } else { - * // found - * } - * \endcode - * - * To iterate over all the elements in the trie with the same key, you have to - * use tommy_trie_bucket() and follow the tommy_node::next pointer until NULL. - * - * \code - * int value_to_find = 1; - * tommy_node* i = tommy_trie_bucket(&trie, value_to_find); - * while (i) { - * struct object* obj = i->data; // gets the object pointer - * - * printf("%d\n", obj->value); // process the object - * - * i = i->next; // goes to the next element - * } - * \endcode - * - * To remove an element from the trie you have to call tommy_trie_remove() - * providing the key to search and remove. - * - * \code - * struct object* obj = tommy_trie_remove(&trie, value_to_remove); - * if (obj) { - * free(obj); // frees the object allocated memory - * } - * \endcode - * - * To destroy the trie you have to remove all the elements, and deinitialize - * the allocator using tommy_allocator_done(). - * - * \code - * tommy_allocator_done(&alloc); - * \endcode - * - * Note that you cannot iterate over all the elements in the trie using the - * trie itself. You have to insert all the elements also in a ::tommy_list, - * and use the list to iterate. See the \ref multiindex example for more detail. - */ - -#ifndef __TOMMYTRIE_H -#define __TOMMYTRIE_H - -#include "tommytypes.h" -#include "tommyalloc.h" - -/******************************************************************************/ -/* trie */ - -/** - * Number of branches on each inner node. It must be a power of 2. - * Suggested values are 8, 16 and 32. - * Any inner node, excluding leafs, contains a pointer to each branch. - * - * The default size is choosen to exactly fit a typical cache line of 64 bytes. - */ -#define TOMMY_TRIE_TREE_MAX (64 / sizeof(void*)) - -/** - * Trie block size. - * You must use this value to initialize the allocator. - */ -#define TOMMY_TRIE_BLOCK_SIZE (TOMMY_TRIE_TREE_MAX * sizeof(void*)) - -/** \internal - * Number of bits for each branch. - */ -#define TOMMY_TRIE_TREE_BIT TOMMY_ILOG2(TOMMY_TRIE_TREE_MAX) - -/** \internal - * Number of bits of the first level. - */ -#define TOMMY_TRIE_BUCKET_BIT ((TOMMY_KEY_BIT % TOMMY_TRIE_TREE_BIT) + TOMMY_TRIE_TREE_BIT) - -/** \internal - * Number of branches of the first level. - * It's like a inner branch, but bigger to get any remainder bits. - */ -#define TOMMY_TRIE_BUCKET_MAX (1 << TOMMY_TRIE_BUCKET_BIT) - -/** - * Trie node. - * This is the node that you have to include inside your objects. - */ -typedef tommy_node tommy_trie_node; - -/** - * Trie container type. - * \note Don't use internal fields directly, but access the container only using functions. - */ -typedef struct tommy_trie_struct { - tommy_trie_node* bucket[TOMMY_TRIE_BUCKET_MAX]; /**< First tree level. */ - tommy_count_t count; /**< Number of elements. */ - tommy_count_t node_count; /**< Number of nodes. */ - tommy_allocator* alloc; /**< Allocator for internal nodes. */ -} tommy_trie; - -/** - * Initializes the trie. - * You have to provide an allocator initialized with *both* the size and align with TOMMY_TRIE_BLOCK_SIZE. - * You can share this allocator with other tries. - * - * The tries is completely allocated through the allocator, and it doesn't need to be deinitialized. - * \param alloc Allocator initialized with *both* the size and align with TOMMY_TRIE_BLOCK_SIZE. - */ -void tommy_trie_init(tommy_trie* trie, tommy_allocator* alloc); - -/** - * Inserts an element in the trie. - * You have to provide the pointer of the node embedded into the object, - * the pointer at the object and the key to use. - * \param node Pointer at the node embedded into the object to insert. - * \param data Pointer at the object to insert. - * \param key Key to use to insert the object. - */ -void tommy_trie_insert(tommy_trie* trie, tommy_trie_node* node, void* data, tommy_key_t key); - -/** - * Searches and removes the first element with the specified key. - * If the element is not found, 0 is returned. - * If more equal elements are present, the first one is removed. - * This operation is faster than calling tommy_trie_bucket() and tommy_trie_remove_existing() separately. - * \param key Key of the element to find and remove. - * \return The removed element, or 0 if not found. - */ -void* tommy_trie_remove(tommy_trie* trie, tommy_key_t key); - -/** - * Gets the bucket of the specified key. - * The bucket is guaranteed to contain ALL and ONLY the elements with the specified key. - * You can access elements in the bucket following the ::next pointer until 0. - * \param key Key of the element to find. - * \return The head of the bucket, or 0 if empty. - */ -tommy_trie_node* tommy_trie_bucket(tommy_trie* trie, tommy_key_t key); - -/** - * Searches an element in the trie. - * You have to provide the key of the element you want to find. - * If more elements with the same key are present, the first one is returned. - * \param key Key of the element to find. - * \return The first element found, or 0 if none. - */ -tommy_inline void* tommy_trie_search(tommy_trie* trie, tommy_key_t key) -{ - tommy_trie_node* i = tommy_trie_bucket(trie, key); - - if (!i) - return 0; - - return i->data; -} - -/** - * Removes an element from the trie. - * You must already have the address of the element to remove. - * \return The tommy_node::data field of the node removed. - */ -void* tommy_trie_remove_existing(tommy_trie* trie, tommy_trie_node* node); - -/** - * Gets the number of elements. - */ -tommy_inline tommy_count_t tommy_trie_count(tommy_trie* trie) -{ - return trie->count; -} - -/** - * Gets the size of allocated memory. - * It includes the size of the ::tommy_trie_node of the stored elements. - */ -tommy_size_t tommy_trie_memory_usage(tommy_trie* trie); - -#endif - diff --git a/tommyds/tommyds/tommytrieinp.c b/tommyds/tommyds/tommytrieinp.c deleted file mode 100644 index 26ec2c3..0000000 --- a/tommyds/tommyds/tommytrieinp.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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 "tommytrieinp.h" - -#include /* for assert */ - -/******************************************************************************/ -/* trie_inplace */ - -/** - * Mask for the inner branches. - */ -#define TOMMY_TRIE_INPLACE_TREE_MASK (TOMMY_TRIE_INPLACE_TREE_MAX - 1) - -/** - * Shift for the first level of branches. - */ -#define TOMMY_TRIE_INPLACE_BUCKET_SHIFT (TOMMY_KEY_BIT - TOMMY_TRIE_INPLACE_BUCKET_BIT) - -/** - * Create a new list with a single element. - */ -tommy_inline tommy_trie_inplace_node* tommy_trie_inplace_list_insert_first(tommy_trie_inplace_node* node) -{ - /* one element "circular" prev list */ - node->prev = node; - - /* one element "0 terminated" next list */ - node->next = 0; - - return node; -} - -/** - * Add an element to an existing list. - * \note The element is inserted at the end of the list. - */ -tommy_inline void tommy_trie_inplace_list_insert_tail_not_empty(tommy_trie_inplace_node* head, tommy_trie_inplace_node* node) -{ - /* insert in the list in the last position */ - - /* insert in the "circular" prev list */ - node->prev = head->prev; - head->prev = node; - - /* insert in the "0 terminated" next list */ - node->next = 0; - node->prev->next = node; -} - -/** - * Remove an element from the list. - */ -tommy_inline void tommy_trie_inplace_list_remove(tommy_trie_inplace_node** let_ptr, tommy_trie_inplace_node* node) -{ - tommy_trie_inplace_node* head = *let_ptr; - - /* remove from the "circular" prev list */ - if (node->next) - node->next->prev = node->prev; - else - head->prev = node->prev; /* the last */ - - /* remove from the "0 terminated" next list */ - if (head == node) - *let_ptr = node->next; /* the new first */ - else - node->prev->next = node->next; -} - -void tommy_trie_inplace_init(tommy_trie_inplace* trie_inplace) -{ - tommy_uint_t i; - - for (i = 0; i < TOMMY_TRIE_INPLACE_BUCKET_MAX; ++i) - trie_inplace->bucket[i] = 0; - - trie_inplace->count = 0; -} - -static void trie_inplace_bucket_insert(tommy_uint_t shift, tommy_trie_inplace_node** let_ptr, tommy_trie_inplace_node* insert, tommy_key_t key) -{ - tommy_trie_inplace_node* node; - - node = *let_ptr; - while (node && node->key != key) { - let_ptr = &node->map[(key >> shift) & TOMMY_TRIE_INPLACE_TREE_MASK]; - node = *let_ptr; - shift -= TOMMY_TRIE_INPLACE_TREE_BIT; - } - - /* if null, just insert the node */ - if (!node) { - /* setup the node as a list */ - *let_ptr = tommy_trie_inplace_list_insert_first(insert); - } else { - /* if it's the same key, insert in the list */ - tommy_trie_inplace_list_insert_tail_not_empty(node, insert); - } -} - -void tommy_trie_inplace_insert(tommy_trie_inplace* trie_inplace, tommy_trie_inplace_node* node, void* data, tommy_key_t key) -{ - tommy_trie_inplace_node** let_ptr; - tommy_uint_t i; - - node->data = data; - node->key = key; - /* clear the child pointers */ - for (i = 0; i < TOMMY_TRIE_INPLACE_TREE_MAX; ++i) - node->map[i] = 0; - - let_ptr = &trie_inplace->bucket[key >> TOMMY_TRIE_INPLACE_BUCKET_SHIFT]; - - trie_inplace_bucket_insert(TOMMY_TRIE_INPLACE_BUCKET_SHIFT, let_ptr, node, key); - - ++trie_inplace->count; -} - -static tommy_trie_inplace_node* trie_inplace_bucket_remove(tommy_uint_t shift, tommy_trie_inplace_node** let_ptr, tommy_trie_inplace_node* remove, tommy_key_t key) -{ - tommy_trie_inplace_node* node; - int i; - tommy_trie_inplace_node** leaf_let_ptr; - tommy_trie_inplace_node* leaf; - - node = *let_ptr; - while (node && node->key != key) { - let_ptr = &node->map[(key >> shift) & TOMMY_TRIE_INPLACE_TREE_MASK]; - node = *let_ptr; - shift -= TOMMY_TRIE_INPLACE_TREE_BIT; - } - - if (!node) - return 0; - - /* if the node to remove is not specified */ - if (!remove) - remove = node; /* remove the first */ - - tommy_trie_inplace_list_remove(let_ptr, remove); - - /* if not change in the node, nothing more to do */ - if (*let_ptr == node) - return remove; - - /* if we have a substitute */ - if (*let_ptr != 0) { - /* copy the child pointers to the new one */ - node = *let_ptr; - for (i = 0; i < TOMMY_TRIE_INPLACE_TREE_MAX; ++i) - node->map[i] = remove->map[i]; - - return remove; - } - - /* find a leaf */ - leaf_let_ptr = 0; - leaf = remove; - - /* search backward, statistically we have more zeros than ones */ - i = TOMMY_TRIE_INPLACE_TREE_MAX - 1; - while (i >= 0) { - if (leaf->map[i]) { - leaf_let_ptr = &leaf->map[i]; - leaf = *leaf_let_ptr; - i = TOMMY_TRIE_INPLACE_TREE_MAX - 1; - continue; - } - --i; - } - - /* if it's itself a leaf */ - if (!leaf_let_ptr) - return remove; - - /* remove the leaf */ - *leaf_let_ptr = 0; - - /* copy the child pointers */ - for (i = 0; i < TOMMY_TRIE_INPLACE_TREE_MAX; ++i) - leaf->map[i] = remove->map[i]; - - /* put it in place */ - *let_ptr = leaf; - - return remove; -} - -void* tommy_trie_inplace_remove(tommy_trie_inplace* trie_inplace, tommy_key_t key) -{ - tommy_trie_inplace_node* ret; - tommy_trie_inplace_node** let_ptr; - - let_ptr = &trie_inplace->bucket[key >> TOMMY_TRIE_INPLACE_BUCKET_SHIFT]; - - ret = trie_inplace_bucket_remove(TOMMY_TRIE_INPLACE_BUCKET_SHIFT, let_ptr, 0, key); - - if (!ret) - return 0; - - --trie_inplace->count; - - return ret->data; -} - -void* tommy_trie_inplace_remove_existing(tommy_trie_inplace* trie_inplace, tommy_trie_inplace_node* node) -{ - tommy_trie_inplace_node* ret; - tommy_key_t key = node->key; - tommy_trie_inplace_node** let_ptr; - - let_ptr = &trie_inplace->bucket[key >> TOMMY_TRIE_INPLACE_BUCKET_SHIFT]; - - ret = trie_inplace_bucket_remove(TOMMY_TRIE_INPLACE_BUCKET_SHIFT, let_ptr, node, key); - - /* the element removed must match the one passed */ - assert(ret == node); - - --trie_inplace->count; - - return ret->data; -} - -tommy_trie_inplace_node* tommy_trie_inplace_bucket(tommy_trie_inplace* trie_inplace, tommy_key_t key) -{ - tommy_trie_inplace_node* node; - tommy_uint_t shift; - - node = trie_inplace->bucket[key >> TOMMY_TRIE_INPLACE_BUCKET_SHIFT]; - shift = TOMMY_TRIE_INPLACE_BUCKET_SHIFT; - - while (node && node->key != key) { - node = node->map[(key >> shift) & TOMMY_TRIE_INPLACE_TREE_MASK]; - shift -= TOMMY_TRIE_INPLACE_TREE_BIT; - } - - return node; -} - -tommy_size_t tommy_trie_inplace_memory_usage(tommy_trie_inplace* trie_inplace) -{ - return tommy_trie_inplace_count(trie_inplace) * (tommy_size_t)sizeof(tommy_trie_inplace_node); -} - diff --git a/tommyds/tommyds/tommytrieinp.h b/tommyds/tommyds/tommytrieinp.h deleted file mode 100644 index c30f676..0000000 --- a/tommyds/tommyds/tommytrieinp.h +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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. - */ - -/** \file - * Inplace trie. - * - * This trie is a inplace implementation not needing any external allocation. - * - * Elements are not stored in order, like ::tommy_trie, because some elements - * should be used to represent the inner nodes in the trie. - * - * You can control the number of branches of each node using the ::TOMMY_TRIE_INPLACE_TREE_MAX define. - * More branches imply more speed, but a bigger memory occupation. - * - * Compared to ::tommy_trie you should use a lower number of branches to limit the unused memory - * occupation of the leaf nodes. This imply a lower speed, but without the need of an external allocator. - * - * To initialize the trie you have to call tommy_trie_inplace_init(). - * - * \code - * tommy_trie_inplace trie_inplace; - * - * tommy_trie_inplace_init(&trie_inplace); - * \endcode - * - * To insert elements in the trie you have to call tommy_trie_inplace_insert() for - * each element. - * In the insertion call you have to specify the address of the node, the - * address of the object, and the key value to use. - * The address of the object is used to initialize the tommy_node::data field - * of the node, and the key to initialize the tommy_node::key field. - * - * \code - * struct object { - * tommy_node node; - * // other fields - * int value; - * }; - * - * struct object* obj = malloc(sizeof(struct object)); // creates the object - * - * obj->value = ...; // initializes the object - * - * tommy_trie_inplace_insert(&trie_inplace, &obj->node, obj, obj->value); // inserts the object - * \endcode - * - * To find and element in the trie you have to call tommy_trie_inplace_search() providing - * the key to search. - * - * \code - * int value_to_find = 1; - * struct object* obj = tommy_trie_inplace_search(&trie_inplace, value_to_find); - * if (!obj) { - * // not found - * } else { - * // found - * } - * \endcode - * - * To iterate over all the elements in the trie with the same key, you have to - * use tommy_trie_inplace_bucket() and follow the tommy_node::next pointer until NULL. - * - * \code - * int value_to_find = 1; - * tommy_node* i = tommy_trie_inplace_bucket(&trie_inplace, value_to_find); - * while (i) { - * struct object* obj = i->data; // gets the object pointer - * - * printf("%d\n", obj->value); // process the object - * - * i = i->next; // goes to the next element - * } - * \endcode - * - * To remove an element from the trie you have to call tommy_trie_inplace_remove() - * providing the key to search and remove. - * - * \code - * struct object* obj = tommy_trie_inplace_remove(&trie_inplace, value_to_remove); - * if (obj) { - * free(obj); // frees the object allocated memory - * } - * \endcode - * - * To destroy the trie you have only to remove all the elements, as the trie is - * completely inplace and it doesn't allocate memory. - * - * Note that you cannot iterate over all the elements in the trie using the - * trie itself. You have to insert all the elements also in a ::tommy_list, - * and use the list to iterate. See the \ref multiindex example for more detail. - */ - -#ifndef __TOMMYTRIEINP_H -#define __TOMMYTRIEINP_H - -#include "tommytypes.h" - -/******************************************************************************/ -/* trie_inplace */ - -/** - * Number of branches on each node. It must be a power of 2. - * Suggested values are 2, 4 and 8. - * Any node, including leafs, contains a pointer to each branch. - */ -#define TOMMY_TRIE_INPLACE_TREE_MAX 4 - -/** \internal - * Number of bits for each branch. - */ -#define TOMMY_TRIE_INPLACE_TREE_BIT TOMMY_ILOG2(TOMMY_TRIE_INPLACE_TREE_MAX) - -/** \internal - * Number of bits of the first level. - */ -#define TOMMY_TRIE_INPLACE_BUCKET_BIT ((TOMMY_KEY_BIT % TOMMY_TRIE_INPLACE_TREE_BIT) + 3 * TOMMY_TRIE_INPLACE_TREE_BIT) - -/** \internal - * Number of branches of the first level. - * It's like a inner branch, but bigger to get any remainder bits. - */ -#define TOMMY_TRIE_INPLACE_BUCKET_MAX (1 << TOMMY_TRIE_INPLACE_BUCKET_BIT) - -/** - * Trie node. - * This is the node that you have to include inside your objects. - */ -typedef struct tommy_trie_inplace_node_struct { - struct tommy_trie_inplace_node_struct* next; /**< Next element. 0 if it's the last. */ - struct tommy_trie_inplace_node_struct* prev; /**< Circular previous element. */ - void* data; /**< Pointer at the data. */ - tommy_key_t key; /**< Used to store the key or the hash. */ - struct tommy_trie_inplace_node_struct* map[TOMMY_TRIE_INPLACE_TREE_MAX]; /** Branches of the node. */ -} tommy_trie_inplace_node; - -/** - * Trie container type. - * \note Don't use internal fields directly, but access the container only using functions. - */ -typedef struct tommy_trie_inplace_struct { - tommy_trie_inplace_node* bucket[TOMMY_TRIE_INPLACE_BUCKET_MAX]; /**< First tree level. */ - tommy_count_t count; /**< Number of elements. */ -} tommy_trie_inplace; - -/** - * Initializes the trie. - * - * The tries is completely inplace, and it doesn't need to be deinitialized. - */ -void tommy_trie_inplace_init(tommy_trie_inplace* trie_inplace); - -/** - * Inserts an element in the trie. - */ -void tommy_trie_inplace_insert(tommy_trie_inplace* trie_inplace, tommy_trie_inplace_node* node, void* data, tommy_key_t key); - -/** - * Searches and removes the first element with the specified key. - * If the element is not found, 0 is returned. - * If more equal elements are present, the first one is removed. - * This operation is faster than calling tommy_trie_inplace_bucket() and tommy_trie_inplace_remove_existing() separately. - * \param key Key of the element to find and remove. - * \return The removed element, or 0 if not found. - */ -void* tommy_trie_inplace_remove(tommy_trie_inplace* trie_inplace, tommy_key_t key); - -/** - * Gets the bucket of the specified key. - * The bucket is guaranteed to contain ALL and ONLY the elements with the specified key. - * You can access elements in the bucket following the ::next pointer until 0. - * \param key Key of the element to find. - * \return The head of the bucket, or 0 if empty. - */ -tommy_trie_inplace_node* tommy_trie_inplace_bucket(tommy_trie_inplace* trie_inplace, tommy_key_t key); - -/** - * Searches an element in the trie. - * You have to provide the key of the element you want to find. - * If more elements with the same key are present, the first one is returned. - * \param key Key of the element to find. - * \return The first element found, or 0 if none. - */ -tommy_inline void* tommy_trie_inplace_search(tommy_trie_inplace* trie_inplace, tommy_key_t key) -{ - tommy_trie_inplace_node* i = tommy_trie_inplace_bucket(trie_inplace, key); - - if (!i) - return 0; - - return i->data; -} - -/** - * Removes an element from the trie. - * You must already have the address of the element to remove. - * \return The tommy_node::data field of the node removed. - */ -void* tommy_trie_inplace_remove_existing(tommy_trie_inplace* trie_inplace, tommy_trie_inplace_node* node); - -/** - * Gets the number of elements. - */ -tommy_inline tommy_count_t tommy_trie_inplace_count(tommy_trie_inplace* trie_inplace) -{ - return trie_inplace->count; -} - -/** - * Gets the size of allocated memory. - * It includes the size of the ::tommy_inplace_node of the stored elements. - */ -tommy_size_t tommy_trie_inplace_memory_usage(tommy_trie_inplace* trie_inplace); - -#endif - diff --git a/tommyds/tommyds/tommytypes.h b/tommyds/tommyds/tommytypes.h deleted file mode 100644 index 45d3d1a..0000000 --- a/tommyds/tommyds/tommytypes.h +++ /dev/null @@ -1,410 +0,0 @@ -/* - * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE 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. - */ - -/** \file - * Generic types. - */ - -#ifndef __TOMMYTYPES_H -#define __TOMMYTYPES_H - -/******************************************************************************/ -/* types */ - -#include - -#if defined(_MSC_VER) -typedef unsigned tommy_uint32_t; /**< Generic uint32_t type. */ -typedef unsigned _int64 tommy_uint64_t; /**< Generic uint64_t type. */ -typedef size_t tommy_uintptr_t; /**< Generic uintptr_t type. */ -#else -#include -typedef uint32_t tommy_uint32_t; /**< Generic uint32_t type. */ -typedef uint64_t tommy_uint64_t; /**< Generic uint64_t type. */ -typedef uintptr_t tommy_uintptr_t; /**< Generic uintptr_t type. */ -#endif -typedef size_t tommy_size_t; /**< Generic size_t type. */ -typedef ptrdiff_t tommy_ptrdiff_t; /**< Generic ptrdiff_t type. */ -typedef int tommy_bool_t; /**< Generic boolean type. */ - -/** - * Generic unsigned integer type. - * - * It has no specific size, as is used to store only small values. - * To make the code more efficient, a full 32 bit integer is used. - */ -typedef tommy_uint32_t tommy_uint_t; - -/** - * Generic unsigned integer for counting objects. - * - * TommyDS doesn't support more than 2^32-1 objects. - */ -typedef tommy_uint32_t tommy_count_t; - -/** \internal - * Type cast required for the C++ compilation. - * When compiling in C++ we cannot convert a void* pointer to another pointer. - * In such case we need an explicit cast. - */ -#ifdef __cplusplus -#define tommy_cast(type, value) static_cast(value) -#else -#define tommy_cast(type, value) (value) -#endif - -/******************************************************************************/ -/* heap */ - -/* by default uses malloc/calloc/realloc/free */ - -/** - * Generic malloc(), calloc(), realloc() and free() functions. - * Redefine them to what you need. By default they map to the C malloc(), calloc(), realloc() and free(). - */ -#if !defined(tommy_malloc) || !defined(tommy_calloc) || !defined(tommy_realloc) || !defined(tommy_free) -#include -#endif -#if !defined(tommy_malloc) -#define tommy_malloc malloc -#endif -#if !defined(tommy_calloc) -#define tommy_calloc calloc -#endif -#if !defined(tommy_realloc) -#define tommy_realloc realloc -#endif -#if !defined(tommy_free) -#define tommy_free free -#endif - -/******************************************************************************/ -/* modificators */ - -/** \internal - * Definition of the inline keyword if available. - */ -#if !defined(tommy_inline) -#if defined(_MSC_VER) || defined(__GNUC__) -#define tommy_inline static __inline -#else -#define tommy_inline static -#endif -#endif - -/** \internal - * Definition of the restrict keyword if available. - */ -#if !defined(tommy_restrict) -#if __STDC_VERSION__ >= 199901L -#define tommy_restrict restrict -#elif defined(_MSC_VER) || defined(__GNUC__) -#define tommy_restrict __restrict -#else -#define tommy_restrict -#endif -#endif - -/** \internal - * Hints the compiler that a condition is likely true. - */ -#if !defined(tommy_likely) -#if defined(__GNUC__) -#define tommy_likely(x) __builtin_expect(!!(x), 1) -#else -#define tommy_likely(x) (x) -#endif -#endif - -/** \internal - * Hints the compiler that a condition is likely false. - */ -#if !defined(tommy_unlikely) -#if defined(__GNUC__) -#define tommy_unlikely(x) __builtin_expect(!!(x), 0) -#else -#define tommy_unlikely(x) (x) -#endif -#endif - -/******************************************************************************/ -/* key */ - -/** - * Key type used in indexed data structures to store the key or the hash value. - */ -typedef tommy_uint32_t tommy_key_t; - -/** - * Bits into the ::tommy_key_t type. - */ -#define TOMMY_KEY_BIT (sizeof(tommy_key_t) * 8) - -/******************************************************************************/ -/* node */ - -/** - * Data structure node. - * This node type is shared between all the data structures and used to store some - * info directly into the objects you want to store. - * - * A typical declaration is: - * \code - * struct object { - * tommy_node node; - * // other fields - * }; - * \endcode - */ -typedef struct tommy_node_struct { - /** - * Next node. - * The tail node has it at 0, like a 0 terminated list. - */ - struct tommy_node_struct* next; - - /** - * Previous node. - * The head node points to the tail node, like a circular list. - */ - struct tommy_node_struct* prev; - - /** - * Pointer at the object containing the node. - * This field is initialized when inserting nodes into a data structure. - */ - void* data; - - /** - * Key used to store the node. - * With hashtables this field is used to store the hash value. - * With lists this field is not used. - */ - tommy_key_t key; -} tommy_node; - -/******************************************************************************/ -/* compare */ - -/** - * Compare function for elements. - * \param obj_a Pointer at the first object to compare. - * \param obj_b Pointer at the second object to compare. - * \return <0 if the first element is less than the second, ==0 equal, >0 if greather. - * - * This function is like the C strcmp(). - * - * \code - * struct object { - * tommy_node node; - * int value; - * }; - * - * int compare(const void* obj_a, const void* obj_b) - * { - * if (((const struct object*)obj_a)->value < ((const struct object*)obj_b)->value) - * return -1; - * if (((const struct object*)obj_a)->value > ((const struct object*)obj_b)->value) - * return 1; - * return 0; - * } - * - * tommy_list_sort(&list, compare); - * \endcode - * - */ -typedef int tommy_compare_func(const void* obj_a, const void* obj_b); - -/** - * Search function for elements. - * \param arg Pointer at the value to search. - * \param obj Pointer at the object to compare to. - * \return ==0 if the value matches the element. !=0 if different. - * - * Note that the first argument is a pointer to the value to search and - * the second one is a pointer to the object to compare. - * They are pointers of two different types. - * - * \code - * struct object { - * tommy_node node; - * int value; - * }; - * - * int compare(const void* arg, const void* obj) - * { - * return *(const int*)arg != ((const struct object*)obj)->value; - * } - * - * int value_to_find = 1; - * struct object* obj = tommy_hashtable_search(&hashtable, compare, &value_to_find, tommy_inthash_u32(value_to_find)); - * if (!obj) { - * // not found - * } else { - * // found - * } - * \endcode - * - */ -typedef int tommy_search_func(const void* arg, const void* obj); - -/** - * Foreach function. - * \param obj Pointer at the object to iterate. - * - * A typical example is to use free() to deallocate all the objects in a list. - * \code - * tommy_list_foreach(&list, (tommy_foreach_func*)free); - * \endcode - */ -typedef void tommy_foreach_func(void* obj); - -/** - * Foreach function with an argument. - * \param arg Pointer at a generic argument. - * \param obj Pointer at the object to iterate. - */ -typedef void tommy_foreach_arg_func(void* arg, void* obj); - -/******************************************************************************/ -/* bit hacks */ - -#if defined(_MSC_VER) && !defined(__cplusplus) -#include -#pragma intrinsic(_BitScanReverse) -#pragma intrinsic(_BitScanForward) -#endif - -/** \internal - * Integer log2 for constants. - * You can use it only for exact power of 2 up to 256. - */ -#define TOMMY_ILOG2(value) ((value) == 256 ? 8 : (value) == 128 ? 7 : (value) == 64 ? 6 : (value) == 32 ? 5 : (value) == 16 ? 4 : (value) == 8 ? 3 : (value) == 4 ? 2 : (value) == 2 ? 1 : 0) - -/** - * Bit scan reverse or integer log2. - * Return the bit index of the most significant 1 bit. - * - * If no bit is set, the result is undefined. - * To force a return 0 in this case, you can use tommy_ilog2_u32(value | 1). - * - * Other interesting ways for bitscan are at: - * - * Bit Twiddling Hacks - * http://graphics.stanford.edu/~seander/bithacks.html - * - * Chess Programming BitScan - * http://chessprogramming.wikispaces.com/BitScan - * - * \param value Value to scan. 0 is not allowed. - * \return The index of the most significant bit set. - */ -tommy_inline tommy_uint_t tommy_ilog2_u32(tommy_uint32_t value) -{ -#if defined(_MSC_VER) - unsigned long count; - _BitScanReverse(&count, value); - return count; -#elif defined(__GNUC__) - /* - * GCC implements __builtin_clz(x) as "__builtin_clz(x) = bsr(x) ^ 31" - * - * Where "x ^ 31 = 31 - x", but gcc does not optimize "31 - __builtin_clz(x)" to bsr(x), - * but generates 31 - (bsr(x) xor 31). - * - * So we write "__builtin_clz(x) ^ 31" instead of "31 - __builtin_clz(x)", - * to allow the double xor to be optimized out. - */ - return __builtin_clz(value) ^ 31; -#else - /* Find the log base 2 of an N-bit integer in O(lg(N)) operations with multiply and lookup */ - /* from http://graphics.stanford.edu/~seander/bithacks.html */ - static unsigned char TOMMY_DE_BRUIJN_INDEX_ILOG2[32] = { - 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, - 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 - }; - - value |= value >> 1; - value |= value >> 2; - value |= value >> 4; - value |= value >> 8; - value |= value >> 16; - - return TOMMY_DE_BRUIJN_INDEX_ILOG2[(tommy_uint32_t)(value * 0x07C4ACDDU) >> 27]; -#endif -} - -/** - * Bit scan forward or trailing zero count. - * Return the bit index of the least significant 1 bit. - * - * If no bit is set, the result is undefined. - * \param value Value to scan. 0 is not allowed. - * \return The index of the least significant bit set. - */ -tommy_inline tommy_uint_t tommy_ctz_u32(tommy_uint32_t value) -{ -#if defined(_MSC_VER) - unsigned long count; - _BitScanForward(&count, value); - return count; -#elif defined(__GNUC__) - return __builtin_ctz(value); -#else - /* Count the consecutive zero bits (trailing) on the right with multiply and lookup */ - /* from http://graphics.stanford.edu/~seander/bithacks.html */ - static const unsigned char TOMMY_DE_BRUIJN_INDEX_CTZ[32] = { - 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, - 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 - }; - - return TOMMY_DE_BRUIJN_INDEX_CTZ[(tommy_uint32_t)(((value & - value) * 0x077CB531U)) >> 27]; -#endif -} - -/** - * Rounds up to the next power of 2. - * For the value 0, the result is undefined. - * \return The smallest power of 2 not less than the specified value. - */ -tommy_inline tommy_uint32_t tommy_roundup_pow2_u32(tommy_uint32_t value) -{ - /* Round up to the next highest power of 2 */ - /* from http://www-graphics.stanford.edu/~seander/bithacks.html */ - - --value; - value |= value >> 1; - value |= value >> 2; - value |= value >> 4; - value |= value >> 8; - value |= value >> 16; - ++value; - - return value; -} -#endif - diff --git a/tommyds/tommyweb-header.html b/tommyds/tommyweb-header.html deleted file mode 100644 index d835a60..0000000 --- a/tommyds/tommyweb-header.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - -TommyDS - - - - - - - - diff --git a/tommyds/tommyweb.doxygen b/tommyds/tommyweb.doxygen deleted file mode 100644 index 72b76ea..0000000 --- a/tommyds/tommyweb.doxygen +++ /dev/null @@ -1,5 +0,0 @@ -@INCLUDE = tommy.doxygen -OUTPUT_DIRECTORY = web -HTML_HEADER = tommyweb-header.html - -