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 e82cbd6..0000000 Binary files a/tommyds/benchmark/lib/judy/Judy.lib and /dev/null differ diff --git a/tommyds/benchmark/lib/judy/build.bat b/tommyds/benchmark/lib/judy/build.bat deleted file mode 100644 index 2174eba..0000000 --- a/tommyds/benchmark/lib/judy/build.bat +++ /dev/null @@ -1,185 +0,0 @@ -@ECHO OFF - -echo Set Compiler -SET CC=cl - -echo Set Options -SET COPT=-DJU_WIN /Ox /GS- -SET O=-DJUDY1 -SET L=-DJUDYL -SET INC=-I.. -I..\JudyCommon - -echo Deleting Old Files -del JudyCommon\*.obj JudySL\*.obj JudyHS\*.obj Judy1\*.obj JudyL\*.obj *.dll - -echo Giving Judy1 the proper Names -copy JudyCommon\JudyByCount.c Judy1\Judy1ByCount.c -copy JudyCommon\JudyCascade.c Judy1\Judy1Cascade.c -copy JudyCommon\JudyCount.c Judy1\Judy1Count.c -copy JudyCommon\JudyCreateBranch.c Judy1\Judy1CreateBranch.c -copy JudyCommon\JudyDecascade.c Judy1\Judy1Decascade.c -copy JudyCommon\JudyDel.c Judy1\Judy1Unset.c -copy JudyCommon\JudyFirst.c Judy1\Judy1First.c -copy JudyCommon\JudyFreeArray.c Judy1\Judy1FreeArray.c -copy JudyCommon\JudyGet.c Judy1\Judy1Test.c -copy JudyCommon\JudyGet.c Judy1\j__udy1Test.c -copy JudyCommon\JudyInsArray.c Judy1\Judy1SetArray.c -copy JudyCommon\JudyIns.c Judy1\Judy1Set.c -copy JudyCommon\JudyInsertBranch.c Judy1\Judy1InsertBranch.c -copy JudyCommon\JudyMallocIF.c Judy1\Judy1MallocIF.c -copy JudyCommon\JudyMemActive.c Judy1\Judy1MemActive.c -copy JudyCommon\JudyMemUsed.c Judy1\Judy1MemUsed.c -copy JudyCommon\JudyPrevNext.c Judy1\Judy1Next.c -copy JudyCommon\JudyPrevNext.c Judy1\Judy1Prev.c -copy JudyCommon\JudyPrevNextEmpty.c Judy1\Judy1NextEmpty.c -copy JudyCommon\JudyPrevNextEmpty.c Judy1\Judy1PrevEmpty.c -copy JudyCommon\JudyTables.c Judy1\Judy1TablesGen.c - -echo Giving JudyL the proper Names -copy JudyCommon\JudyByCount.c JudyL\JudyLByCount.c -copy JudyCommon\JudyCascade.c JudyL\JudyLCascade.c -copy JudyCommon\JudyCount.c JudyL\JudyLCount.c -copy JudyCommon\JudyCreateBranch.c JudyL\JudyLCreateBranch.c -copy JudyCommon\JudyDecascade.c JudyL\JudyLDecascade.c -copy JudyCommon\JudyDel.c JudyL\JudyLDel.c -copy JudyCommon\JudyFirst.c JudyL\JudyLFirst.c -copy JudyCommon\JudyFreeArray.c JudyL\JudyLFreeArray.c -copy JudyCommon\JudyGet.c JudyL\JudyLGet.c -copy JudyCommon\JudyGet.c JudyL\j__udyLGet.c -copy JudyCommon\JudyInsArray.c JudyL\JudyLInsArray.c -copy JudyCommon\JudyIns.c JudyL\JudyLIns.c -copy JudyCommon\JudyInsertBranch.c JudyL\JudyLInsertBranch.c -copy JudyCommon\JudyMallocIF.c JudyL\JudyLMallocIF.c -copy JudyCommon\JudyMemActive.c JudyL\JudyLMemActive.c -copy JudyCommon\JudyMemUsed.c JudyL\JudyLMemUsed.c -copy JudyCommon\JudyPrevNext.c JudyL\JudyLNext.c -copy JudyCommon\JudyPrevNext.c JudyL\JudyLPrev.c -copy JudyCommon\JudyPrevNextEmpty.c JudyL\JudyLNextEmpty.c -copy JudyCommon\JudyPrevNextEmpty.c JudyL\JudyLPrevEmpty.c -copy JudyCommon\JudyTables.c JudyL\JudyLTablesGen.c - - -echo Compile JudyCommon\JudyMalloc - common to Judy1 and JudyL -cd JudyCommon -%CC% -I. -I.. -DJU_WIN -c JudyMalloc.c - -cd .. - -echo This table is constructed from Juudy1.h data to match malloc(3) needs -cd Judy1 -%CC% %INC% %COPT% %O% Judy1TablesGen.c -o Judy1TablesGen -del Judy1TablesGen.obj -Judy1TablesGen -%CC% %INC% %COPT% %O% -c Judy1Tables.c - -echo compile the main line Judy1 modules -echo %CC% %INC% %COPT% %O% -c Judy1Test.c -%CC% %INC% %COPT% %O% -c Judy1Test.c -echo %CC% %INC% %COPT% %O% -c -DJUDYGETINLINE j__udy1Test.c -%CC% %INC% %COPT% %O% -c -DJUDYGETINLINE j__udy1Test.c -echo %CC% %INC% %COPT% %O% -c Judy1Set.c -%CC% %INC% %COPT% %O% -c Judy1Set.c -echo %CC% %INC% %COPT% %O% -c Judy1SetArray.c -%CC% %INC% %COPT% %O% -c Judy1SetArray.c -echo %CC% %INC% %COPT% %O% -c Judy1Unset.c -%CC% %INC% %COPT% %O% -c Judy1Unset.c -echo %CC% %INC% %COPT% %O% -c Judy1First.c -%CC% %INC% %COPT% %O% -c Judy1First.c -echo %CC% %INC% %COPT% %O% -DJUDYNEXT -c Judy1Next.c -%CC% %INC% %COPT% %O% -DJUDYNEXT -c Judy1Next.c -echo %CC% %INC% %COPT% %O% -DJUDYPREV -c Judy1Prev.c -%CC% %INC% %COPT% %O% -DJUDYPREV -c Judy1Prev.c -echo %CC% %INC% %COPT% %O% -DJUDYNEXT -c Judy1NextEmpty.c -%CC% %INC% %COPT% %O% -DJUDYNEXT -c Judy1NextEmpty.c -echo %CC% %INC% %COPT% %O% -DJUDYPREV -c Judy1PrevEmpty.c -%CC% %INC% %COPT% %O% -DJUDYPREV -c Judy1PrevEmpty.c -echo %CC% %INC% %COPT% %O% -c Judy1Count.c -%CC% %INC% %COPT% %O% -c Judy1Count.c -echo %CC% %INC% %COPT% %O% -c -DNOSMARTJBB -DNOSMARTJBU -DNOSMARTJLB Judy1ByCount.c -%CC% %INC% %COPT% %O% -c -DNOSMARTJBB -DNOSMARTJBU -DNOSMARTJLB Judy1ByCount.c -echo %CC% %INC% %COPT% %O% -c Judy1FreeArray.c -%CC% %INC% %COPT% %O% -c Judy1FreeArray.c -echo %CC% %INC% %COPT% %O% -c Judy1MemUsed.c -%CC% %INC% %COPT% %O% -c Judy1MemUsed.c -echo %CC% %INC% %COPT% %O% -c Judy1MemActive.c -%CC% %INC% %COPT% %O% -c Judy1MemActive.c -echo %CC% %INC% %COPT% %O% -c Judy1Cascade.c -%CC% %INC% %COPT% %O% -c Judy1Cascade.c -echo %CC% %INC% %COPT% %O% -c Judy1Decascade.c -%CC% %INC% %COPT% %O% -c Judy1Decascade.c -echo %CC% %INC% %COPT% %O% -c Judy1CreateBranch.c -%CC% %INC% %COPT% %O% -c Judy1CreateBranch.c -echo %CC% %INC% %COPT% %O% -c Judy1InsertBranch.C -%CC% %INC% %COPT% %O% -c Judy1InsertBranch.C -echo %CC% %INC% %COPT% %O% -c Judy1MallocIF.c -%CC% %INC% %COPT% %O% -c Judy1MallocIF.c - -cd .. -cd JudyL - -echo This table is constructed from Juudy1.h data to match malloc(3) needs -%CC% %INC% %COPT% JudyLTablesGen.c %L% -o JudyLTablesGen -del JudyLTablesGen.obj -JudyLTablesGen -%CC% %INC% %COPT% %L% -c JudyLTables.c - -echo compile the main line JudyL modules -echo %CC% %INC% %COPT% %L% -c JudyLGet.c -%CC% %INC% %COPT% %L% -c JudyLGet.c -echo %CC% %INC% %COPT% %L% -c -DJUDYGETINLINE j__udyLGet.c -%CC% %INC% %COPT% %L% -c -DJUDYGETINLINE j__udyLGet.c -echo %CC% %INC% %COPT% %L% -c JudyLIns.c -%CC% %INC% %COPT% %L% -c JudyLIns.c -echo %CC% %INC% %COPT% %L% -c JudyLInsArray.c -%CC% %INC% %COPT% %L% -c JudyLInsArray.c -echo %CC% %INC% %COPT% %L% -c JudyLDel.c -%CC% %INC% %COPT% %L% -c JudyLDel.c -echo %CC% %INC% %COPT% %L% -c JudyLFirst.c -%CC% %INC% %COPT% %L% -c JudyLFirst.c -echo %CC% %INC% %COPT% %L% -c -DJUDYNEXT JudyLNext.c -%CC% %INC% %COPT% %L% -c -DJUDYNEXT JudyLNext.c -echo %CC% %INC% %COPT% %L% -c -DJUDYPREV JudyLPrev.c -%CC% %INC% %COPT% %L% -c -DJUDYPREV JudyLPrev.c -echo %CC% %INC% %COPT% %L% -c -DJUDYNEXT JudyLNextEmpty.c -%CC% %INC% %COPT% %L% -c -DJUDYNEXT JudyLNextEmpty.c -echo %CC% %INC% %COPT% %L% -c -DJUDYPREV JudyLPrevEmpty.c -%CC% %INC% %COPT% %L% -c -DJUDYPREV JudyLPrevEmpty.c -echo %CC% %INC% %COPT% %L% -c JudyLCount.c -%CC% %INC% %COPT% %L% -c JudyLCount.c -echo %CC% %INC% %COPT% %L% -c -DNOSMARTJBB -DNOSMARTJBU -DNOSMARTJLB JudyLByCount.c -%CC% %INC% %COPT% %L% -c -DNOSMARTJBB -DNOSMARTJBU -DNOSMARTJLB JudyLByCount.c -echo %CC% %INC% %COPT% %L% -c JudyLFreeArray.c -%CC% %INC% %COPT% %L% -c JudyLFreeArray.c -echo %CC% %INC% %COPT% %L% -c JudyLMemUsed.c -%CC% %INC% %COPT% %L% -c JudyLMemUsed.c -echo %CC% %INC% %COPT% %L% -c JudyLMemActive.c -%CC% %INC% %COPT% %L% -c JudyLMemActive.c -echo %CC% %INC% %COPT% %L% -c JudyLCascade.c -%CC% %INC% %COPT% %L% -c JudyLCascade.c -echo %CC% %INC% %COPT% %L% -c JudyLDecascade.c -%CC% %INC% %COPT% %L% -c JudyLDecascade.c -echo %CC% %INC% %COPT% %L% -c JudyLCreateBranch.c -%CC% %INC% %COPT% %L% -c JudyLCreateBranch.c -echo %CC% %INC% %COPT% %L% -c JudyLInsertBranch.c -%CC% %INC% %COPT% %L% -c JudyLInsertBranch.c -echo %CC% %INC% %COPT% %L% -c JudyLMallocIF.c -%CC% %INC% %COPT% %L% -c JudyLMallocIF.c - -cd .. -cd JudySL -echo Compile the JudySL routine -echo %CC% %INC% %COPT% -c JudySL.c -%CC% %INC% %COPT% -c JudySL.c - -cd .. -cd JudyHS -echo Compile the JudyHS routine -echo %CC% %INC% %COPT% -c JudyHS.c -%CC% %INC% %COPT% -c JudyHS.c - -cd .. -echo Make a Judy dll by linking all the objects togeather -link /DLL JudyCommon\*.obj Judy1\*.obj JudyL\*.obj JudySL\*.obj JudyHS\*.obj /OUT:Judy.dll - -echo Make a Judy archive library by linking all the objects togeather -link /LIB JudyCommon\*.obj Judy1\*.obj JudyL\*.obj JudySL\*.obj JudyHS\*.obj /OUT:Judy.lib diff --git a/tommyds/benchmark/lib/judy/libJudyL.a b/tommyds/benchmark/lib/judy/libJudyL.a deleted file mode 100644 index 725c716..0000000 Binary files a/tommyds/benchmark/lib/judy/libJudyL.a and /dev/null differ diff --git a/tommyds/benchmark/lib/judy/libJudyMalloc.a b/tommyds/benchmark/lib/judy/libJudyMalloc.a deleted file mode 100644 index 433c82a..0000000 Binary files a/tommyds/benchmark/lib/judy/libJudyMalloc.a and /dev/null differ diff --git a/tommyds/benchmark/lib/judyarray/judy64na.c b/tommyds/benchmark/lib/judyarray/judy64na.c deleted file mode 100644 index 68012ee..0000000 --- a/tommyds/benchmark/lib/judyarray/judy64na.c +++ /dev/null @@ -1,2099 +0,0 @@ -// Judy arrays 23 NOV 2012 - -// Author Karl Malbrain, malbrain@yahoo.com -// with assistance from Jan Weiss. - -// Simplified judy arrays for strings and integers -// Adapted from the ideas of Douglas Baskins of HP. - -// The -D ASKITIS benchmarking option was implemented with -// assistance from Dr. Nikolas Askitis (www.naskitis.com). - -// Map a set of keys to corresponding memory cells (uints). -// Each cell must be set to a non-zero value by the caller. - -// STANDALONE is defined to compile into a string sorter. - -// String mappings are denoted by calling judy_open with zero as -// the second argument. Integer mappings are denoted by calling -// judy_open with the Integer depth of the Judy Trie as the second -// argument. - -//#define STANDALONE - -// functions: -// judy_open: open a new judy array returning a judy object. -// judy_close: close an open judy array, freeing all memory. -// judy_clone: clone an open judy array, duplicating the stack. -// judy_data: allocate data memory within judy array for external use. -// judy_cell: insert a string into the judy array, return cell pointer. -// judy_strt: retrieve the cell pointer greater than or equal to given key -// judy_slot: retrieve the cell pointer, or return NULL for a given key. -// judy_key: retrieve the string value for the most recent judy query. -// judy_end: retrieve the cell pointer for the last string in the array. -// judy_nxt: retrieve the cell pointer for the next string in the array. -// judy_prv: retrieve the cell pointer for the prev string in the array. -// judy_del: delete the key and cell for the current stack entry. - -#include -#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 - -