diff options
author | Xavier Roche <roche@httrack.com> | 2015-03-15 16:38:31 +0100 |
---|---|---|
committer | Xavier Roche <roche@httrack.com> | 2015-03-15 16:38:31 +0100 |
commit | cc494ce3aab54fc6761c4797b1ae4eb891056531 (patch) | |
tree | f68504b0eb2c7817eef46d8e949a2f6884927ee1 | |
parent | 51662ccc56aefb20ac9166fa81b0dc0f7f73c7a6 (diff) |
Import coucal as a regular submodule
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | src/Makefile.am | 9 | ||||
m--------- | src/coucal | 0 | ||||
-rw-r--r-- | src/coucal.c | 1561 | ||||
-rw-r--r-- | src/coucal.h | 529 |
5 files changed, 8 insertions, 2094 deletions
diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..4608a5f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/coucal"] + path = src/coucal + url = git@github.com:xroche/coucal.git diff --git a/src/Makefile.am b/src/Makefile.am index 56788fb..a7a0ebd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -23,7 +23,8 @@ AM_CPPFLAGS = \ -DPREFIX=\""$(prefix)"\" \ -DSYSCONFDIR=\""$(sysconfdir)"\" \ -DDATADIR=\""$(datadir)"\" \ - -DLIBDIR=\""$(libdir)"\" + -DLIBDIR=\""$(libdir)"\" \ + -I \""$(srcdir)/coucal"\" bin_PROGRAMS = proxytrack httrack htsserver @@ -38,14 +39,14 @@ lib_LTLIBRARIES = libhttrack.la libhtsjava.la htsserver_SOURCES = htsserver.c htsserver.h htsweb.c htsweb.h proxytrack_SOURCES = proxy/main.c \ proxy/proxytrack.c proxy/store.c \ - coucal.c htsmd5.c md5.c \ + coucal/coucal.c htsmd5.c md5.c \ minizip/ioapi.c minizip/mztools.c minizip/unzip.c minizip/zip.c whttrackrundir = $(bindir) whttrackrun_SCRIPTS = webhttrack libhttrack_la_SOURCES = htscore.c htsparse.c htsback.c htscache.c \ - htscatchurl.c htsfilters.c htsftp.c htshash.c coucal.c \ + htscatchurl.c htsfilters.c htsftp.c htshash.c coucal/coucal.c \ htshelp.c htslib.c htscoremain.c \ htsname.c htsrobots.c htstools.c htswizard.c \ htsalias.c htsthread.c htsindex.c htsbauth.c \ @@ -56,7 +57,7 @@ libhttrack_la_SOURCES = htscore.c htsparse.c htsback.c htscache.c \ hts-indextmpl.h htsalias.h htsback.h htsbase.h htssafe.h \ htsbasenet.h htsbauth.h htscache.h htscatchurl.h \ htsconfig.h htscore.h htsparse.h htscoremain.h htsdefines.h \ - htsfilters.h htsftp.h htsglobal.h htshash.h coucal.h \ + htsfilters.h htsftp.h htsglobal.h htshash.h coucal/coucal.h \ htshelp.h htsindex.h htslib.h htsmd5.h \ htsmodules.h htsname.h htsnet.h \ htsopt.h htsrobots.h htsthread.h \ diff --git a/src/coucal b/src/coucal new file mode 160000 +Subproject 7d5326958b2b86073684c223d315f07dc452070 diff --git a/src/coucal.c b/src/coucal.c deleted file mode 100644 index 7d0c4d9..0000000 --- a/src/coucal.c +++ /dev/null @@ -1,1561 +0,0 @@ -/* ------------------------------------------------------------ */ -/* -Coucal, Cuckoo hashing-based hashtable with stash area. -Copyright (C) 2013-2014 Xavier Roche (http://www.httrack.com/) -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. - -3. Neither the name of the copyright holder 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 HOLDER 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 <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <assert.h> -#include <stdarg.h> - -#include "coucal.h" - -/* We use murmur hashing by default, even if md5 can be a good candidate, - for its quality regarding diffusion and collisions. - MD5 is slower than other hashing functions, but is known to be an excellent - hashing function. FNV-1 is generally good enough for this purpose, too, but - the performance gain is not sufficient to use it by default. - - On several benchmarks, both MD5 and FNV were quite good (0.45 cuckoo moved - on average for each new item inserted in the hashtable), but FNV-1 was more - prone to mutual collisions (creating cycles requiring stash handling), and - was causing the stash area to be more filled than the MD5 variant. - - Simpler hashing functions, such as rolling hashes (LCG) were also tested, - but with collision rate and diffusion were terrible. - - [ On a 10M key tests, both variants acheived 0.45 cuckoo/add ration, - but the FNV-1 variant collided 11 times with a maximum stash area - filled with 4 entries ; whereas the MD5 variant did only collide - once ] -*/ -#if (!defined(HTS_INTHASH_USES_MD5) \ - && !defined(HTS_INTHASH_USES_OPENSSL_MD5) \ - && !defined(HTS_INTHASH_USES_MURMUR) \ - && !defined(HTS_INTHASH_USES_FNV1) \ - ) -/* Temporry: fixing Invalid address alignment issues */ -#if (defined(HAVE_ALIGNED_ACCESS_REQUIRED) \ - || defined(__sparc__) \ - || defined(mips) || defined(__mips__) || defined(MIPS) || defined(_MIPS_) \ - || defined(arm) || defined(__arm__) || defined(ARM) || defined(_ARM_) \ - ) -#ifndef LIBHTTRACK_EXPORTS -#define HTS_INTHASH_USES_OPENSSL_MD5 1 -#else -#define HTS_INTHASH_USES_MD5 1 -#endif -#else -#define HTS_INTHASH_USES_MURMUR 1 -#endif -#endif - -/* Dispatch includes */ -#if (defined(HTS_INTHASH_USES_MURMUR)) -#include "murmurhash3.h" -#elif (defined(HTS_INTHASH_USES_MD5)) -#include "md5.h" -#define HashMD5Init(CTX, FLAG) MD5Init(CTX, FLAG) -#define HashMD5Update(CTX, DATA, SIZE) MD5Update(CTX, DATA, SIZE) -#define HashMD5Final(DIGEST, CTX) MD5Final(DIGEST, CTX) -#define HashMD5Context MD5CTX -#elif (defined(HTS_INTHASH_USES_OPENSSL_MD5)) -#include <openssl/md5.h> -#define HashMD5Init(CTX, FLAG) MD5_Init(CTX) -#define HashMD5Update(CTX, DATA, SIZE) MD5_Update(CTX, DATA, SIZE) -#define HashMD5Final(DIGEST, CTX) MD5_Final(DIGEST, CTX) -#define HashMD5Context MD5_CTX -#else -#error "No hash method defined" -#endif - -/** Size of auxiliary stash. **/ -#define STASH_SIZE 16 - -/** Minimum value for lg_size. **/ -#define MIN_LG_SIZE 4 - -/** Minimum value for pool.capacity. **/ -#define MIN_POOL_CAPACITY 256 - -/* 64-bit constant */ -#if (defined(WIN32)) -#define UINT_64_CONST(X) ((uint64_t) (X)) -#define UINT_64_FORMAT "I64d" -#elif (defined(_LP64) || defined(__x86_64__) \ - || defined(__powerpc64__) || defined(__64BIT__)) -#define UINT_64_CONST(X) ((uint64_t) X##UL) -#define UINT_64_FORMAT "ld" -#else -#define UINT_64_CONST(X) ((uint64_t) X##ULL) -#define UINT_64_FORMAT "lld" -#endif - -/** Hashtable. **/ -struct struct_coucal { - /** Hashtable items. **/ - coucal_item *items; - - /** Log-2 of the hashtable size. **/ - size_t lg_size; - - /** Number of used items (<= POW2(lg_size)). **/ - size_t used; - - /** Stash area for collisions. **/ - struct { - /** Stash items. **/ - coucal_item items[STASH_SIZE]; - - /** Stash size (<= STASH_SIZE). **/ - size_t size; - } stash; - - /** String pool. **/ - struct { - /** String buffer. **/ - char *buffer; - /** Buffer used size (high watermark). **/ - size_t size; - /** Buffer capacity. **/ - size_t capacity; - /** Used chars (== size if compacted). **/ - size_t used; - } pool; - - /** Statistics **/ - struct { - /** Highest stash.size seen. **/ - size_t max_stash_size; - /** Number of writes. **/ - size_t write_count; - /** Number of writes causing an add. **/ - size_t add_count; - /** Number of cuckoo moved during adds. **/ - size_t cuckoo_moved; - /** Number of items added to stash. **/ - size_t stash_added; - /** Number of hashtable rehash/expand operations. **/ - size_t rehash_count; - /** Number of pool compact operations. **/ - size_t pool_compact_count; - /** Number of pool realloc operations. **/ - size_t pool_realloc_count; - } stats; - - /** Settings. **/ - struct { - /** How to handle values (might be NULL). **/ - struct { - /** free() **/ - t_coucal_value_freehandler free; - /** opaque argument **/ - coucal_opaque arg; - } value; - - /** How to handle names (might be NULL). **/ - struct { - /** strdup() **/ - t_coucal_duphandler dup; - /** free() **/ - t_coucal_key_freehandler free; - /** hash **/ - t_coucal_hasheshandler hash; - /** comparison **/ - t_coucal_cmphandler equals; - /** opaque argument **/ - coucal_opaque arg; - } key; - - /** How to handle fatal assertions (might be NULL). **/ - struct { - /** logging **/ - t_coucal_loghandler log; - /** abort() **/ - t_coucal_asserthandler fatal; - /** opaque argument **/ - coucal_opaque arg; - /** hashtable name for logging **/ - coucal_key_const name; - } error; - - /** How to handle pretty-print (debug) (might be NULL). **/ - struct { - /** key print() **/ - t_coucal_printkeyhandler key; - /** value print() **/ - t_coucal_printvaluehandler value; - /** opaque argument **/ - coucal_opaque arg; - } print; - } custom; -}; - -/* Assertion check. */ -#define coucal_assert(HASHTABLE, EXP) \ - (void)( (EXP) || (coucal_assert_failed(HASHTABLE, #EXP, __FILE__, __LINE__), 0) ) - -/* Compiler-specific. */ -#ifdef __GNUC__ -#define INTHASH_PRINTF_FUN(fmt, arg) __attribute__ ((format (printf, fmt, arg))) -#define INTHASH_INLINE __inline__ -#elif (defined(_MSC_VER)) -#define INTHASH_PRINTF_FUN(FMT, ARGS) -#define INTHASH_INLINE __inline -#else -#define INTHASH_PRINTF_FUN(FMT, ARGS) -#define INTHASH_INLINE -#endif - -/* Logging level. */ -static void coucal_log(const coucal hashtable, coucal_loglevel level, - const char *format, va_list args); -#define DECLARE_LOG_FUNCTION(NAME, LEVEL) \ -static void NAME(const coucal hashtable, const char *format, ...) \ - INTHASH_PRINTF_FUN(2, 3); \ -static void NAME(const coucal hashtable, const char *format, ...) { \ - va_list args; \ - va_start(args, format); \ - coucal_log(hashtable, LEVEL, format, args); \ - va_end(args); \ -} -#if 0 -/* Verbose. */ -DECLARE_LOG_FUNCTION(coucal_crit, coucal_log_critical) -DECLARE_LOG_FUNCTION(coucal_warning, coucal_log_warning) -DECLARE_LOG_FUNCTION(coucal_info, coucal_log_info) -DECLARE_LOG_FUNCTION(coucal_debug, coucal_log_debug) -DECLARE_LOG_FUNCTION(coucal_trace, coucal_log_trace) -#elif 0 -/* Info. */ -DECLARE_LOG_FUNCTION(coucal_crit, coucal_log_critical) -DECLARE_LOG_FUNCTION(coucal_warning, coucal_log_warning) -DECLARE_LOG_FUNCTION(coucal_info, coucal_log_info) -#define coucal_debug coucal_log -#define coucal_trace coucal_nolog -#else -/* No logging except stats and critical. */ -DECLARE_LOG_FUNCTION(coucal_crit, coucal_log_critical) -DECLARE_LOG_FUNCTION(coucal_warning, coucal_log_warning) -DECLARE_LOG_FUNCTION(coucal_info, coucal_log_info) -#define coucal_debug coucal_nolog -#define coucal_trace coucal_nolog -#endif - -/* 2**X */ -#define POW2(X) ( (size_t) 1 << (X) ) - -/* the empty string for the string pool */ -static char the_empty_string[1] = { 0 }; - -/* global assertion handler */ -static t_coucal_asserthandler global_assert_handler = NULL; - -/* global assertion handler */ -static t_coucal_loghandler global_log_handler = NULL; - -/* default assertion handler, if neither hashtable one nor global one - were defined */ -static void coucal_fail(const char* exp, const char* file, int line) { - fprintf(stderr, "assertion '%s' failed at %s:%d\n", exp, file, line); - abort(); -} - -/* assert failed handler. */ -static void coucal_assert_failed(const coucal hashtable, const char* exp, const char* file, int line) { - const char *const name = coucal_get_name(hashtable); - coucal_crit(hashtable, "hashtable %s: %s failed at %s:%d", - name != NULL ? name : "<unknown>", exp, file, line); - if (hashtable != NULL && hashtable->custom.error.fatal != NULL) { - hashtable->custom.error.fatal(hashtable->custom.error.arg, exp, file, line); - } else if (global_assert_handler != NULL) { - global_assert_handler(hashtable, exp, file, line); - } else { - coucal_fail(exp, file, line); - } - abort(); -} - -/* Logging */ -static void coucal_log(const coucal hashtable, coucal_loglevel level, - const char *format, va_list args) { - coucal_assert(hashtable, format != NULL); - if (hashtable != NULL && hashtable->custom.error.log != NULL) { - hashtable->custom.error.log(hashtable->custom.error.arg, level, format, args); - } else if (global_log_handler != NULL) { - global_log_handler(hashtable, level, format, args); - } else { - fprintf(stderr, "[%p] ", (void*) hashtable); - (void) vfprintf(stderr, format, args); - putc('\n', stderr); - } -} - -/* No logging (should be dropped by the compiler) */ -static INTHASH_INLINE void coucal_nolog(const coucal hashtable, - const char *format, ...) - INTHASH_PRINTF_FUN(2, 3); -static INTHASH_INLINE void coucal_nolog(const coucal hashtable, - const char *format, ...) { - (void) hashtable; - (void) format; -} - -const char* coucal_get_name(coucal hashtable) { - return hashtable->custom.error.name; -} - -static void coucal_log_stats(coucal hashtable) { - const char *const name = coucal_get_name(hashtable); - coucal_info(hashtable, "hashtable %s%s%ssummary: " - "size=%"UINT_64_FORMAT" (lg2=%"UINT_64_FORMAT") " - "used=%"UINT_64_FORMAT" " - "stash-size=%"UINT_64_FORMAT" " - "pool-size=%"UINT_64_FORMAT" " - "pool-capacity=%"UINT_64_FORMAT" " - "pool-used=%"UINT_64_FORMAT" " - "writes=%"UINT_64_FORMAT" " - "(new=%"UINT_64_FORMAT") " - "moved=%"UINT_64_FORMAT " " - "stashed=%"UINT_64_FORMAT" " - "max-stash-size=%"UINT_64_FORMAT" " - "avg-moved=%g " - "rehash=%"UINT_64_FORMAT" " - "pool-compact=%"UINT_64_FORMAT" " - "pool-realloc=%"UINT_64_FORMAT" " - "memory=%"UINT_64_FORMAT, - name != NULL ? "\"" : "", - name != NULL ? name : "", - name != NULL ? "\" " : "", - (uint64_t) POW2(hashtable->lg_size), - (uint64_t) hashtable->lg_size, - (uint64_t) hashtable->used, - (uint64_t) hashtable->stash.size, - (uint64_t) hashtable->pool.size, - (uint64_t) hashtable->pool.capacity, - (uint64_t) hashtable->pool.used, - (uint64_t) hashtable->stats.write_count, - (uint64_t) hashtable->stats.add_count, - (uint64_t) hashtable->stats.cuckoo_moved, - (uint64_t) hashtable->stats.stash_added, - (uint64_t) hashtable->stats.max_stash_size, - (double) hashtable->stats.cuckoo_moved / (double) hashtable->stats.add_count, - (uint64_t) hashtable->stats.rehash_count, - (uint64_t) hashtable->stats.pool_compact_count, - (uint64_t) hashtable->stats.pool_realloc_count, - (uint64_t) coucal_memory_size(hashtable) - ); -} - -/* default hash function when key is a regular C-string */ -coucal_hashkeys coucal_hash_data(const void *data_, size_t size) { - const unsigned char *const data = (const unsigned char *) data_; -#if (defined(HTS_INTHASH_USES_MD5) || defined(HTS_INTHASH_USES_OPENSSL_MD5)) - /* compute a regular MD5 and extract two 32-bit integers */ - HashMD5Context ctx; - union { - unsigned char md5digest[16]; -#if (COUCAL_HASH_SIZE == 32) - coucal_hashkeys mhashes[2]; -#endif - coucal_hashkeys hashes; - } u; - - /* compute MD5 */ - HashMD5Init(&ctx, 0); - HashMD5Update(&ctx, data, (unsigned int) size); - HashMD5Final(u.md5digest, &ctx); - -#if (COUCAL_HASH_SIZE == 32) - /* mix mix mix */ - u.mhashes[0].hash1 ^= u.mhashes[1].hash1; - u.mhashes[0].hash2 ^= u.mhashes[1].hash2; -#endif - - /* do not keep identical hashes */ - if (u.hashes.hash1 == u.hashes.hash2) { - u.hashes.hash2 = ~u.hashes.hash2; - } - - return u.hashes; -#elif (defined(HTS_INTHASH_USES_MURMUR)) - union { - uint32_t result[4]; - coucal_hashkeys hashes; - } u; - MurmurHash3_x86_128(data, (const int) size, 42, &u.result); - -#if (COUCAL_HASH_SIZE == 32) - /* mix mix mix */ - u.result[0] ^= u.result[2]; - u.result[1] ^= u.result[3]; -#endif - - /* do not keep identical hashes */ - if (u.hashes.hash1 == u.hashes.hash2) { - u.hashes.hash2 = ~u.hashes.hash2; - } - - return u.hashes; -#elif (defined(HTS_INTHASH_USES_FNV1)) - /* compute two Fowler-Noll-Vo hashes (64-bit FNV-1 variant) ; - each 64-bit hash being XOR-folded into a single 32-bit hash. */ - size_t i; - coucal_hashkeys hashes; - uint64_t h1, h2; - - /* FNV-1, 64-bit. */ -#define FNV1_PRIME UINT_64_CONST(1099511628211) -#define FNV1_OFFSET_BASIS UINT_64_CONST(14695981039346656037) - - /* compute the hashes ; second variant is using xored data */ - h1 = FNV1_OFFSET_BASIS; - h2 = ~FNV1_OFFSET_BASIS; - for(i = 0 ; i < size ; i++) { - const unsigned char c1 = data[i]; - const unsigned char c2 = ~c1; - h1 = ( h1 * FNV1_PRIME ) ^ c1; - h2 = ( h2 * FNV1_PRIME ) ^ c2; - } - -#if (COUCAL_HASH_SIZE == 32) - /* XOR-folding to improve diffusion (Wikipedia) */ - hashes.hash1 = ( (uint32_t) h1 ^ (uint32_t) ( h1 >> 32 ) ); - hashes.hash2 = ( (uint32_t) h2 ^ (uint32_t) ( h2 >> 32 ) ); -#elif (COUCAL_HASH_SIZE == 64) - /* Direct hashes */ - hashes.hash1 = h1; - hashes.hash2 = h2; -#else -#error "Unsupported COUCAL_HASH_SIZE" -#endif - -#undef FNV1_PRIME -#undef FNV1_OFFSET_BASIS - - /* do not keep identical hashes */ - if (hashes.hash1 == hashes.hash2) { - hashes.hash2 = ~hashes.hash2; - } - - return hashes; - -#else -#error "Undefined hashing method" -#endif -} - -INTHASH_INLINE coucal_hashkeys coucal_hash_string(const char *name) { - return coucal_hash_data(name, strlen(name)); -} - -INTHASH_INLINE coucal_hashkeys coucal_calc_hashes(coucal hashtable, - coucal_key_const value) { - return hashtable->custom.key.hash == NULL - ? coucal_hash_string(value) - : hashtable->custom.key.hash(hashtable->custom.key.arg, value); -} - -/* position 'pos' is free ? */ -static INTHASH_INLINE int coucal_is_free(const coucal hashtable, size_t pos) { - return hashtable->items[pos].name == NULL; -} - -/* compare two keys ; by default using strcmp() */ -static INTHASH_INLINE int coucal_equals(coucal hashtable, - coucal_key_const a, - coucal_key_const b) { - return hashtable->custom.key.equals == NULL - ? strcmp((const char*) a, (const char*) b) == 0 - : hashtable->custom.key.equals(hashtable->custom.key.arg, a, b); -} - -static INTHASH_INLINE int coucal_matches_(coucal hashtable, - const coucal_item *const item, - coucal_key_const name, - const coucal_hashkeys *hashes) { - return item->name != NULL - && item->hashes.hash1 == hashes->hash1 - && item->hashes.hash2 == hashes->hash2 - && coucal_equals(hashtable, item->name, name); -} - -static INTHASH_INLINE int coucal_matches(coucal hashtable, size_t pos, - coucal_key_const name, - const coucal_hashkeys *hashes) { - const coucal_item *const item = &hashtable->items[pos]; - return coucal_matches_(hashtable, item, name, hashes); -} - -/* compact string pool ; does not necessarily change the capacity */ -static void coucal_compact_pool(coucal hashtable, size_t capacity) { - const size_t hash_size = POW2(hashtable->lg_size); - size_t i; - char*const old_pool = hashtable->pool.buffer; - const size_t old_size = hashtable->pool.size; - size_t count = 0; - - /* we manage the string pool */ - coucal_assert(hashtable, hashtable->custom.key.dup == NULL); - - /* statistics */ - hashtable->stats.pool_compact_count++; - - /* change capacity now */ - if (hashtable->pool.capacity != capacity) { - hashtable->pool.capacity = capacity; - } - - /* realloc */ - hashtable->pool.buffer = malloc(hashtable->pool.capacity); - hashtable->pool.size = 0; - hashtable->pool.used = 0; - if (hashtable->pool.buffer == NULL) { - coucal_debug(hashtable, - "** hashtable string pool compaction error: could not allocate " - "%"UINT_64_FORMAT" bytes", - (uint64_t) hashtable->pool.capacity); - coucal_assert(hashtable, ! "hashtable string pool compaction error"); - } - - /* relocate a string on a different pool */ -#define RELOCATE_STRING(S) do { \ - if (S != NULL && S != the_empty_string) { \ - const char *const src = (S); \ - char *const dest = \ - &hashtable->pool.buffer[hashtable->pool.size]; \ - const size_t capacity = hashtable->pool.capacity; \ - char *const max_dest = \ - &hashtable->pool.buffer[capacity]; \ - /* copy string */ \ - coucal_assert(hashtable, dest < max_dest); \ - dest[0] = src[0]; \ - { \ - size_t i; \ - for(i = 1 ; src[i - 1] != '\0' ; i++) { \ - coucal_assert(hashtable, &dest[i] < max_dest); \ - dest[i] = src[i]; \ - } \ - /* update pool size */ \ - hashtable->pool.size += i; \ - coucal_assert(hashtable, \ - hashtable->pool.size <= capacity); \ - } \ - /* update source */ \ - S = dest; \ - count++; \ - } \ -} while(0) - - /* relocate */ - for(i = 0 ; i < hash_size ; i++) { - RELOCATE_STRING(hashtable->items[i].name); - } - for(i = 0 ; i < hashtable->stash.size ; i++) { - RELOCATE_STRING(hashtable->stash.items[i].name); - } - -#undef RELOCATE_STRING - - /* compacted (used chars == current size) */ - hashtable->pool.used = hashtable->pool.size; - - /* wipe previous pool */ - free(old_pool); - - coucal_debug(hashtable, - "compacted string pool for %"UINT_64_FORMAT" strings: " - "%"UINT_64_FORMAT" bytes => %"UINT_64_FORMAT" bytes", - (uint64_t) count, (uint64_t) old_size, - (uint64_t) hashtable->pool.size); -} - -/* realloc (expand) string pool ; does not change the compacity */ -static void coucal_realloc_pool(coucal hashtable, size_t capacity) { - const size_t hash_size = POW2(hashtable->lg_size); - char *const oldbase = hashtable->pool.buffer; - size_t count = 0; - - /* we manage the string pool */ - coucal_assert(hashtable, hashtable->custom.key.dup == NULL); - - /* compact instead ? */ - if (hashtable->pool.used < ( hashtable->pool.size*3 ) / 4) { - coucal_compact_pool(hashtable, capacity); - return ; - } - - /* statistics */ - hashtable->stats.pool_realloc_count++; - - /* change capacity now */ - hashtable->pool.capacity = capacity; - - /* realloc */ - hashtable->pool.buffer = realloc(hashtable->pool.buffer, - hashtable->pool.capacity); - if (hashtable->pool.buffer == NULL) { - coucal_crit(hashtable, - "** hashtable string pool allocation error: could not allocate " - "%"UINT_64_FORMAT" bytes", - (uint64_t) hashtable->pool.capacity); - coucal_assert(hashtable, ! "hashtable string pool allocation error"); - } - - /* recompute string address */ -#define RECOMPUTE_STRING(S) do { \ - if (S != NULL && S != the_empty_string) { \ - const size_t offset = (const char*) (S) - oldbase; \ - coucal_assert(hashtable, offset < hashtable->pool.capacity); \ - S = &hashtable->pool.buffer[offset]; \ - count++; \ - } \ -} while(0) - - /* recompute string addresses */ - if (hashtable->pool.buffer != oldbase) { - size_t i; - for(i = 0 ; i < hash_size ; i++) { - RECOMPUTE_STRING(hashtable->items[i].name); - } - for(i = 0 ; i < hashtable->stash.size ; i++) { - RECOMPUTE_STRING(hashtable->stash.items[i].name); - } - } - -#undef RECOMPUTE_STRING - - coucal_debug(hashtable, "reallocated string pool for " - "%"UINT_64_FORMAT" strings: %"UINT_64_FORMAT" bytes", - (uint64_t) count, (uint64_t) hashtable->pool.capacity); -} - -static coucal_key coucal_dup_name_internal(coucal hashtable, - coucal_key_const name_) { - const char *const name = (const char*) name_; - const size_t len = strlen(name) + 1; - char *s; - - /* the pool does not allow empty strings for safety purpose ; handhe that - (keys are being emptied when free'd to detect duplicate free) */ - if (len == 1) { - coucal_assert(hashtable, the_empty_string[0] == '\0'); - return the_empty_string; - } - - /* expand pool capacity */ - coucal_assert(hashtable, hashtable->pool.size <= hashtable->pool.capacity); - if (hashtable->pool.capacity - hashtable->pool.size < len) { - size_t capacity; - for(capacity = MIN_POOL_CAPACITY ; capacity < hashtable->pool.size + len - ; capacity <<= 1) ; - coucal_assert(hashtable, hashtable->pool.size < capacity); - coucal_realloc_pool(hashtable, capacity); - } - - /* copy */ - coucal_assert(hashtable, len + hashtable->pool.size <= hashtable->pool.capacity); - s = &hashtable->pool.buffer[hashtable->pool.size]; - memcpy(s, name, len); - hashtable->pool.size += len; - hashtable->pool.used += len; - - return s; -} - -/* duplicate a key. default is to use the internal pool. */ -static INTHASH_INLINE coucal_key coucal_dup_name(coucal hashtable, - coucal_key_const name) { - return hashtable->custom.key.dup == NULL - ? coucal_dup_name_internal(hashtable, name) - : hashtable->custom.key.dup(hashtable->custom.key.arg, name); -} - -/* internal pool free handler. - note: pointer must have been kicked from the pool first */ -static void coucal_free_key_internal(coucal hashtable, coucal_key name_) { - char *const name = (char*) name_; - const size_t len = strlen(name) + 1; - - /* see coucal_dup_name_internal() handling */ - if (len == 1 && name == the_empty_string) { - coucal_assert(hashtable, the_empty_string[0] == '\0'); - return ; - } - - coucal_assert(hashtable, *name != '\0' || !"duplicate or bad string pool release"); - hashtable->pool.used -= len; - *name = '\0'; /* the string is now invalidated */ - - /* compact the pool is too many holes */ - if (hashtable->pool.used < hashtable->pool.size / 2) { - size_t capacity = hashtable->pool.capacity; - /* compact and shrink */ - if (hashtable->pool.used < capacity / 4) { - capacity /= 2; - } - coucal_assert(hashtable, hashtable->pool.used < capacity); - coucal_compact_pool(hashtable, capacity); - } -} - -/* free a key. default is to use the internal pool. - note: pointer must have been kicked from the pool first */ -static void coucal_free_key(coucal hashtable, coucal_key name) { - if (hashtable->custom.key.free == NULL) { - coucal_free_key_internal(hashtable, name); - } else { - hashtable->custom.key.free(hashtable->custom.key.arg, name); - } -} - -static INTHASH_INLINE size_t coucal_hash_to_pos_(size_t lg_size, - coucal_hashkey hash) { - const coucal_hashkey mask = POW2(lg_size) - 1; - return hash & mask; -} - -static INTHASH_INLINE size_t coucal_hash_to_pos(const coucal hashtable, - coucal_hashkey hash) { - return coucal_hash_to_pos_(hashtable->lg_size, hash); -} - -int coucal_read_pvoid(coucal hashtable, coucal_key_const name, void **pvalue) { - coucal_value value = INTHASH_VALUE_NULL; - const int ret = - coucal_read_value(hashtable, name, (pvalue != NULL) ? &value : NULL); - if (pvalue != NULL) - *pvalue = value.ptr; - return ret; -} - -void* coucal_get_pvoid(coucal hashtable, coucal_key_const name) { - void *value; - if (!coucal_read_pvoid(hashtable, name, &value)) { - return NULL; - } - return value; -} - -int coucal_write_pvoid(coucal hashtable, coucal_key_const name, void *pvalue) { - coucal_value value = INTHASH_VALUE_NULL; - - value.ptr = pvalue; - return coucal_write_value(hashtable, name, value); -} - -void coucal_add_pvoid(coucal hashtable, coucal_key_const name, void *pvalue) { - coucal_value value = INTHASH_VALUE_NULL; - - value.ptr = pvalue; - coucal_write_value(hashtable, name, value); -} - -int coucal_write(coucal hashtable, coucal_key_const name, intptr_t intvalue) { - coucal_value value = INTHASH_VALUE_NULL; - - value.intg = intvalue; - return coucal_write_value(hashtable, name, value); -} - -static void coucal_default_free_handler(coucal_opaque arg, - coucal_value value) { - (void) arg; - if (value.ptr != NULL) - free(value.ptr); -} - -static INTHASH_INLINE void coucal_del_value_(coucal hashtable, coucal_value *pvalue) { - if (pvalue->ptr != NULL) { - if (hashtable->custom.value.free != NULL) - hashtable->custom.value.free(hashtable->custom.value.arg, *pvalue); - pvalue->ptr = NULL; - } -} - -static INTHASH_INLINE void coucal_del_value(coucal hashtable, size_t pos) { - coucal_del_value_(hashtable, &hashtable->items[pos].value); -} - -static void coucal_del_name(coucal hashtable, coucal_item *item) { - const coucal_hashkeys nullHash = INTHASH_KEYS_NULL; - char *const name = (char*) item->name; - item->name = NULL; /* there must be no reference remaining */ - item->hashes = nullHash; - /* free after detach (we may compact the pool) */ - coucal_free_key(hashtable, name); -} - -static void coucal_del_item(coucal hashtable, coucal_item *pitem) { - coucal_del_value_(hashtable, &pitem->value); - coucal_del_name(hashtable, pitem); -} - -static int coucal_add_item_(coucal hashtable, coucal_item item); - -/* Write (add or replace) a value in the hashtable. */ -static int coucal_write_value_(coucal hashtable, coucal_key_const name, - coucal_value value) { - coucal_item item; - size_t pos; - const coucal_hashkeys hashes = coucal_calc_hashes(hashtable, name); - - /* Statistics */ - hashtable->stats.write_count++; - - /* replace at position 1 ? */ - pos = coucal_hash_to_pos(hashtable, hashes.hash1); - if (coucal_matches(hashtable, pos, name, &hashes)) { - coucal_del_value(hashtable, pos); - hashtable->items[pos].value = value; - return 0; /* replaced */ - } - - /* replace at position 2 ? */ - pos = coucal_hash_to_pos(hashtable, hashes.hash2); - if (coucal_matches(hashtable, pos, name, &hashes)) { - coucal_del_value(hashtable, pos); - hashtable->items[pos].value = value; - return 0; /* replaced */ - } - - /* replace in the stash ? */ - if (hashtable->stash.size != 0) { - size_t i; - for(i = 0 ; i < hashtable->stash.size ; i++) { - if (coucal_matches_(hashtable, &hashtable->stash.items[i], name, - &hashes)) { - coucal_del_value_(hashtable, &hashtable->stash.items[i].value); - hashtable->stash.items[i].value = value; - return 0; /* replaced */ - } - } - } - - /* Statistics */ - hashtable->stats.add_count++; - - /* otherwise we need to create a new item */ - item.name = coucal_dup_name(hashtable, name); - item.value = value; - item.hashes = hashes; - - return coucal_add_item_(hashtable, item); -} - -/* Return the string representation of a key */ -static const char* coucal_print_key(coucal hashtable, - coucal_key_const name) { - return hashtable->custom.print.key != NULL - ? hashtable->custom.print.key(hashtable->custom.print.arg, name) - : (const char*) name; -} - -/* Add a new item in the hashtable. The item SHALL NOT be already present. */ -static int coucal_add_item_(coucal hashtable, coucal_item item) { - coucal_hashkey cuckoo_hash, initial_cuckoo_hash; - size_t loops; - size_t pos; - - /* place at free position 1 ? */ - pos = coucal_hash_to_pos(hashtable, item.hashes.hash1); - if (coucal_is_free(hashtable, pos)) { - hashtable->items[pos] = item; - return 1; /* added */ - } else { - /* place at free position 2 ? */ - pos = coucal_hash_to_pos(hashtable, item.hashes.hash2); - if (coucal_is_free(hashtable, pos)) { - hashtable->items[pos] = item; - return 1; /* added */ - } - /* prepare cuckoo ; let's take position 1 */ - else { - cuckoo_hash = initial_cuckoo_hash = item.hashes.hash1; - coucal_trace(hashtable, - "debug:collision with '%s' at %"UINT_64_FORMAT" (%x)", - coucal_print_key(hashtable, item.name), - (uint64_t) pos, cuckoo_hash); - } - } - - /* put 'item' in place with hash 'cuckoo_hash' */ - for(loops = POW2(hashtable->lg_size) ; loops != 0 ; --loops) { - const size_t pos = coucal_hash_to_pos(hashtable, cuckoo_hash); - - coucal_trace(hashtable, - "\tdebug:placing cuckoo '%s' at %"UINT_64_FORMAT" (%x)", - coucal_print_key(hashtable, item.name), - (uint64_t) pos, cuckoo_hash); - - /* place at alternate free position ? */ - if (coucal_is_free(hashtable, pos)) { - coucal_trace(hashtable, "debug:free position"); - hashtable->items[pos] = item; - return 1; /* added */ - } - /* then cuckoo's place it is */ - else { - /* replace */ - const coucal_item backup_item = hashtable->items[pos]; - hashtable->items[pos] = item; - - /* statistics */ - hashtable->stats.cuckoo_moved++; - - /* take care of new lost item */ - item = backup_item; - - /* we just kicked this item from its position 1 */ - if (pos == coucal_hash_to_pos(hashtable, item.hashes.hash1)) { - /* then place it on position 2 on next run */ - coucal_trace(hashtable, "\tdebug:position 1"); - cuckoo_hash = item.hashes.hash2; - } - /* we just kicked this item from its position 2 */ - else if (pos == coucal_hash_to_pos(hashtable, item.hashes.hash2)) { - /* then place it on position 1 on next run */ - coucal_trace(hashtable, "\tdebug:position 2"); - cuckoo_hash = item.hashes.hash1; - } - else { - coucal_assert(hashtable, ! "hashtable internal error: unexpected position"); - } - - /* we are looping (back to same hash) */ - /* TODO FIXME: we should actually check the positions */ - if (cuckoo_hash == initial_cuckoo_hash) { - /* emergency stash */ - break; - } - } - } - - /* emergency stashing for the rare cases of collisions */ - if (hashtable->stash.size < STASH_SIZE) { - hashtable->stash.items[hashtable->stash.size] = item; - hashtable->stash.size++; - /* for statistics */ - hashtable->stats.stash_added++; - if (hashtable->stash.size > hashtable->stats.max_stash_size) { - hashtable->stats.max_stash_size = hashtable->stash.size; - } - coucal_debug(hashtable, "used stash because of collision (%d entries)", - (int) hashtable->stash.size); - return 1; /* added */ - } else { - /* debugging */ - if (hashtable->custom.print.key != NULL - && hashtable->custom.print.value != NULL) { - size_t i; - for(i = 0 ; i < hashtable->stash.size ; i++) { - coucal_item *const item = &hashtable->stash.items[i]; - const size_t pos1 = coucal_hash_to_pos(hashtable, item->hashes.hash1); - const size_t pos2 = coucal_hash_to_pos(hashtable, item->hashes.hash2); - coucal_crit(hashtable, - "stash[%u]: key='%s' value='%s' pos1=%d pos2=%d hash1=%04x hash2=%04x", - (int) i, - hashtable->custom.print.key(hashtable->custom.print.arg, item->name), - hashtable->custom.print.value(hashtable->custom.print.arg, item->value), - (int) pos1, (int) pos2, - item->hashes.hash1, item->hashes.hash2); - if (!coucal_is_free(hashtable, pos1)) { - coucal_item *const item = &hashtable->items[pos1]; - const size_t pos1 = coucal_hash_to_pos(hashtable, item->hashes.hash1); - const size_t pos2 = coucal_hash_to_pos(hashtable, item->hashes.hash2); - coucal_crit(hashtable, - "\t.. collisionning with key='%s' value='%s' pos1=%d pos2=%d hash1=%04x hash2=%04x", - hashtable->custom.print.key(hashtable->custom.print.arg, item->name), - hashtable->custom.print.value(hashtable->custom.print.arg, item->value), - (int) pos1, (int) pos2, - item->hashes.hash1, item->hashes.hash2); - } else { - coucal_crit(hashtable, "\t.. collisionning with a free slot (%d)!", (int) pos1); - } - if (!coucal_is_free(hashtable, pos2)) { - coucal_item *const item = &hashtable->items[pos2]; - const size_t pos1 = coucal_hash_to_pos(hashtable, item->hashes.hash1); - const size_t pos2 = coucal_hash_to_pos(hashtable, item->hashes.hash2); - coucal_crit(hashtable, - "\t.. collisionning with key='%s' value='%s' pos1=%d pos2=%d hash1=%04x hash2=%04x", - hashtable->custom.print.key(hashtable->custom.print.arg, item->name), - hashtable->custom.print.value(hashtable->custom.print.arg, item->value), - (int) pos1, (int) pos2, - item->hashes.hash1, item->hashes.hash2); - } else { - coucal_crit(hashtable, "\t.. collisionning with a free slot (%d)!", (int) pos2); - } - } - } - - /* we are doomed. hopefully the probability is lower than being killed - by a wandering radioactive monkey */ - coucal_log_stats(hashtable); - coucal_assert(hashtable, ! "hashtable internal error: cuckoo/stash collision"); - - /* not reachable code */ - return -1; - } -} - -static INTHASH_INLINE int coucal_is_acceptable_pow2(size_t lg_size) { - return lg_size <= COUCAL_HASH_SIZE && lg_size < sizeof(size_t)*8; -} - -int coucal_write_value(coucal hashtable, coucal_key_const name, - coucal_value_const value) { - /* replace of add item */ - const int ret = coucal_write_value_(hashtable, name, value); - - /* added ? */ - if (ret) { - /* size of half of the table */ - const size_t half_size = POW2(hashtable->lg_size - 1); - - /* size of half of the stash */ - const size_t half_stash_size = STASH_SIZE / 2; - - /* item was added: increase count */ - hashtable->used++; - - /* table is more than half-full, or stash is more than half-full */ - if (hashtable->used >= half_size - || hashtable->stash.size >= half_stash_size) { - size_t i; - - /* size before */ - const size_t prev_power = hashtable->lg_size; - const size_t prev_size = half_size * 2; - const size_t prev_alloc_size = prev_size*sizeof(coucal_item); - - /* size after doubling it */ - const size_t alloc_size = prev_alloc_size * 2; - - /* log stash issues */ - if (hashtable->stash.size >= half_stash_size - && half_size > POW2(16) - && hashtable->used < half_size / 4) { - coucal_warning(hashtable, - "stash size still full despite %"UINT_64_FORMAT - " elements used out of %"UINT_64_FORMAT, - (uint64_t) hashtable->used, (uint64_t) half_size*2); - } - - /* statistics */ - hashtable->stats.rehash_count++; - - /* realloc */ - hashtable->lg_size++; - coucal_assert(hashtable, coucal_is_acceptable_pow2(hashtable->lg_size)); - hashtable->items = - (coucal_item *) realloc(hashtable->items, alloc_size); - if (hashtable->items == NULL) { - coucal_crit(hashtable, - "** hashtable allocation error: " - "could not allocate %"UINT_64_FORMAT" bytes", - (uint64_t) alloc_size); - coucal_assert(hashtable, ! "hashtable allocation error"); - } - - /* clear upper half */ - memset(&hashtable->items[prev_size], 0, prev_alloc_size); - - /* relocate lower half items when needed */ - for(i = 0 ; i < prev_size ; i++) { - if (!coucal_is_free(hashtable, i)) { - const coucal_hashkeys *const hashes = &hashtable->items[i].hashes; - - /* currently at old position 1 */ - if (coucal_hash_to_pos_(prev_power, hashes->hash1) == i) { - const size_t pos = coucal_hash_to_pos(hashtable, hashes->hash1); - /* no more the expected position */ - if (pos != i) { - coucal_assert(hashtable, pos >= prev_size); - hashtable->items[pos] = hashtable->items[i]; - memset(&hashtable->items[i], 0, sizeof(hashtable->items[i])); - } - } - else if (coucal_hash_to_pos_(prev_power, hashes->hash2) == i) { - const size_t pos = coucal_hash_to_pos(hashtable, hashes->hash2); - /* no more the expected position */ - if (pos != i) { - coucal_assert(hashtable, pos >= prev_size); - hashtable->items[pos] = hashtable->items[i]; - memset(&hashtable->items[i], 0, sizeof(hashtable->items[i])); - } - } - else { - coucal_assert(hashtable, ! "hashtable unexpected internal error (bad position)"); - } - } - } - - coucal_debug(hashtable, - "expanded hashtable to %"UINT_64_FORMAT" elements", - (uint64_t) POW2(hashtable->lg_size)); - - /* attempt to merge the stash if present */ - if (hashtable->stash.size != 0) { - const size_t old_size = hashtable->stash.size; - size_t i; - - /* backup stash and reset it */ - coucal_item stash[STASH_SIZE]; - memcpy(&stash, hashtable->stash.items, sizeof(hashtable->stash.items)); - hashtable->stash.size = 0; - - /* insert all items */ - for(i = 0 ; i < old_size ; i++) { - const int ret = coucal_add_item_(hashtable, stash[i]); - if (ret == 0) { - coucal_assert(hashtable, ! "hashtable duplicate key when merging the stash"); - } - } - - /* logging */ - coucal_assert(hashtable, hashtable->stash.size <= old_size); - if (hashtable->stash.size < old_size) { - coucal_debug(hashtable, "reduced stash size from %"UINT_64_FORMAT" " - "to %"UINT_64_FORMAT, - (uint64_t) old_size, (uint64_t) hashtable->stash.size); - } else { - coucal_trace(hashtable, "stash has still %"UINT_64_FORMAT" elements", - (uint64_t) hashtable->stash.size); - } - } - - } - } - - return ret; -} - -void coucal_add(coucal hashtable, coucal_key_const name, intptr_t intvalue) { - coucal_value value = INTHASH_VALUE_NULL; - - memset(&value, 0, sizeof(value)); - value.intg = intvalue; - coucal_write_value(hashtable, name, value); -} - -int coucal_read(coucal hashtable, coucal_key_const name, intptr_t * intvalue) { - coucal_value value = INTHASH_VALUE_NULL; - int ret = - coucal_read_value(hashtable, name, (intvalue != NULL) ? &value : NULL); - if (intvalue != NULL) - *intvalue = value.intg; - return ret; -} - -coucal_value* coucal_fetch_value_hashes(coucal hashtable, - coucal_key_const name, - const coucal_hashkeys *hashes) { - size_t pos; - - /* found at position 1 ? */ - pos = coucal_hash_to_pos(hashtable, hashes->hash1); - if (coucal_matches(hashtable, pos, name, hashes)) { - return &hashtable->items[pos].value; - } - - /* found at position 2 ? */ - pos = coucal_hash_to_pos(hashtable, hashes->hash2); - if (coucal_matches(hashtable, pos, name, hashes)) { - return &hashtable->items[pos].value; - } - - /* find in stash ? */ - if (hashtable->stash.size != 0) { - size_t i; - for(i = 0 ; i < hashtable->stash.size ; i++) { - if (coucal_matches_(hashtable, &hashtable->stash.items[i], name, - hashes)) { - return &hashtable->stash.items[i].value; - } - } - } - - /* not found */ - return NULL; -} - -INTHASH_INLINE coucal_value* coucal_fetch_value(coucal hashtable, - coucal_key_const name) { - const coucal_hashkeys hashes = coucal_calc_hashes(hashtable, name); - return coucal_fetch_value_hashes(hashtable, name, &hashes); -} - -int coucal_read_value(coucal hashtable, coucal_key_const name, - coucal_value * pvalue) { - coucal_value* const value = coucal_fetch_value(hashtable, name); - if (value != NULL) { - if (pvalue != NULL) { - *pvalue = *value; - } - return 1; - } - return 0; -} - -static size_t coucal_inc_(coucal hashtable, coucal_key_const name, - size_t inc) { - coucal_value* const value = coucal_fetch_value(hashtable, name); - if (value != NULL) { - value->uintg += inc; - return value->uintg; - } else { - /* create a new value */ - const int ret = coucal_write(hashtable, name, inc); - coucal_assert(hashtable, ret); - return inc; - } -} - -int coucal_inc(coucal hashtable, coucal_key_const name) { - return (int) coucal_inc_(hashtable, name, 1); -} - -int coucal_dec(coucal hashtable, coucal_key_const name) { - return (int) coucal_inc_(hashtable, name, (size_t) -1); -} - -int coucal_exists(coucal hashtable, coucal_key_const name) { - return coucal_read_value(hashtable, name, NULL); -} - -static int coucal_remove_(coucal hashtable, coucal_key_const name, - const coucal_hashkeys *hashes, size_t *removed) { - size_t pos; - - /* found at position 1 ? */ - pos = coucal_hash_to_pos(hashtable, hashes->hash1); - if (coucal_matches(hashtable, pos, name, hashes)) { - coucal_del_item(hashtable, &hashtable->items[pos]); - *removed = pos; - return 1; - } - - /* found at position 2 ? */ - pos = coucal_hash_to_pos(hashtable, hashes->hash2); - if (coucal_matches(hashtable, pos, name, hashes)) { - coucal_del_item(hashtable, &hashtable->items[pos]); - *removed = pos; - return 1; - } - - /* find in stash ? */ - if (hashtable->stash.size != 0) { - size_t i; - for(i = 0 ; i < hashtable->stash.size ; i++) { - if (coucal_matches_(hashtable, &hashtable->stash.items[i], name, - hashes)) { - coucal_del_item(hashtable, &hashtable->stash.items[i]); - for( ; i + 1 < hashtable->stash.size ; i++) { - hashtable->stash.items[i] = hashtable->stash.items[i + 1]; - } - hashtable->stash.size--; - *removed = (size_t) -1; - coucal_debug(hashtable, "debug:deleted item in stash (%d entries)", - (int) hashtable->stash.size); - return 1; - } - } - } - - /* not found */ - return 0; -} - -int coucal_remove(coucal hashtable, coucal_key_const name) { - const coucal_hashkeys hashes = coucal_calc_hashes(hashtable, name); - size_t removed; - const int ret = coucal_remove_(hashtable, name, &hashes, &removed); - - if (ret) { - /* item was removed: decrease count */ - coucal_assert(hashtable, hashtable->used != 0); - hashtable->used--; - - /* can we place stash entry back to the table ? */ - if (hashtable->stash.size != 0 && removed != (size_t) -1) { - size_t i; - for(i = 0 ; i < hashtable->stash.size ; i++) { - const size_t pos1 = - coucal_hash_to_pos(hashtable, hashtable->stash.items[i].hashes.hash1); - const size_t pos2 = - coucal_hash_to_pos(hashtable, hashtable->stash.items[i].hashes.hash2); - if (pos1 == removed || pos2 == removed) { - if (pos1 == removed) { - hashtable->items[pos1] = hashtable->stash.items[i]; - } else if (pos2 == removed) { - hashtable->items[pos2] = hashtable->stash.items[i]; - } - for( ; i + 1 < hashtable->stash.size ; i++) { - hashtable->stash.items[i] = hashtable->stash.items[i + 1]; - } - hashtable->stash.size--; - coucal_debug(hashtable, "debug:moved item from stash (%d entries)", - (int) hashtable->stash.size); - break; - } - } - } - } - - return ret; -} - -int coucal_readptr(coucal hashtable, coucal_key_const name, intptr_t * value) { - int ret; - - *value = 0; - ret = coucal_read(hashtable, name, value); - if (*value == 0) - ret = 0; - return ret; -} - -intptr_t coucal_get_intptr(coucal hashtable, coucal_key_const name) { - intptr_t value; - if (!coucal_read(hashtable, name, &value)) { - return 0; - } - return value; -} - -static INTHASH_INLINE size_t coucal_get_pow2(size_t initial_size) { - size_t size; - for(size = MIN_LG_SIZE - ; size <= COUCAL_HASH_SIZE && POW2(size) < initial_size - ; size++) ; - return size; -} - -coucal coucal_new(size_t initial_size) { - const size_t lg_size = coucal_get_pow2(initial_size); - const int lg_valid = coucal_is_acceptable_pow2(lg_size); - coucal hashtable = lg_valid - ? (coucal) calloc(1, sizeof(struct_coucal)) : NULL; - coucal_item *const items = - (coucal_item *) calloc(POW2(lg_size), sizeof(coucal_item)); - - if (lg_valid && items != NULL && hashtable != NULL) { - hashtable->lg_size = lg_size; - hashtable->items = items; - hashtable->used = 0; - hashtable->stash.size = 0; - hashtable->pool.buffer = NULL; - hashtable->pool.size = 0; - hashtable->pool.capacity = 0; - hashtable->pool.used = 0; - hashtable->stats.max_stash_size = 0; - hashtable->stats.write_count = 0; - hashtable->stats.add_count = 0; - hashtable->stats.cuckoo_moved = 0; - hashtable->stats.stash_added= 0; - hashtable->stats.pool_compact_count = 0; - hashtable->stats.pool_realloc_count = 0; - hashtable->stats.rehash_count = 0; - hashtable->custom.value.free = NULL; - hashtable->custom.value.arg = NULL; - hashtable->custom.key.dup = NULL; - hashtable->custom.key.free = NULL; - hashtable->custom.key.hash = NULL; - hashtable->custom.key.equals = NULL; - hashtable->custom.key.arg = NULL; - hashtable->custom.error.log = NULL; - hashtable->custom.error.fatal = NULL; - hashtable->custom.error.name = NULL; - hashtable->custom.error.arg = NULL; - hashtable->custom.print.key = NULL; - hashtable->custom.print.value = NULL; - hashtable->custom.print.arg = NULL; - return hashtable; - } - if (items != NULL) { - free(items); - } - if (hashtable != NULL) { - free(hashtable); - } - return NULL; -} - -int coucal_created(coucal hashtable) { - return hashtable != NULL && hashtable->items != NULL; -} - -void coucal_value_is_malloc(coucal hashtable, int flag) { - if (flag) { - if (hashtable->custom.value.free == NULL) { - hashtable->custom.value.free = coucal_default_free_handler; - hashtable->custom.value.arg = NULL; - } - } else { - hashtable->custom.value.free = NULL; - hashtable->custom.value.arg = NULL; - } -} - -void coucal_set_name(coucal hashtable, coucal_key_const name) { - hashtable->custom.error.name = name; -} - -void coucal_value_set_value_handler(coucal hashtable, - t_coucal_value_freehandler free, - coucal_opaque arg) { - hashtable->custom.value.free = free; - hashtable->custom.value.arg = arg; -} - -void coucal_value_set_key_handler(coucal hashtable, - t_coucal_duphandler dup, - t_coucal_key_freehandler free, - t_coucal_hasheshandler hash, - t_coucal_cmphandler equals, - coucal_opaque arg) { - /* dup and free must be consistent */ - coucal_assert(hashtable, ( dup == NULL ) == ( free == NULL ) ); - hashtable->custom.key.dup = dup; - hashtable->custom.key.free = free; - hashtable->custom.key.hash = hash; - hashtable->custom.key.equals = equals; - hashtable->custom.key.arg = arg; -} - -void coucal_set_assert_handler(coucal hashtable, - t_coucal_loghandler log, - t_coucal_asserthandler fatal, - coucal_opaque arg) { - hashtable->custom.error.log = log; - hashtable->custom.error.fatal = fatal; - hashtable->custom.error.arg = arg; -} - -void coucal_set_print_handler(coucal hashtable, - t_coucal_printkeyhandler key, - t_coucal_printvaluehandler value, - coucal_opaque arg) { - hashtable->custom.print.key = key; - hashtable->custom.print.value = value; - hashtable->custom.print.arg = arg; -} - -size_t coucal_nitems(coucal hashtable) { - if (hashtable != NULL) - return hashtable->used; - return 0; -} - -size_t coucal_memory_size(coucal hashtable) { - const size_t size_struct = sizeof(struct_coucal); - const size_t hash_size = POW2(hashtable->lg_size)*sizeof(coucal_item); - const size_t pool_size = hashtable->pool.capacity*sizeof(char); - return size_struct + hash_size + pool_size; -} - -size_t coucal_hash_size(void) { - return COUCAL_HASH_SIZE; -} - -void coucal_delete(coucal *phashtable) { - if (phashtable != NULL) { - coucal hashtable = *phashtable; - if (hashtable != NULL) { - coucal_log_stats(hashtable); - if (hashtable->items != NULL) { - /* we need to delete values */ - const size_t hash_size = POW2(hashtable->lg_size); - size_t i; - - /* wipe hashtable values (not names) */ - for(i = 0 ; i < hash_size ; i++) { - if (!coucal_is_free(hashtable, i)) { - coucal_del_value(hashtable, i); - } - } - - /* wipe auxiliary stash values (not names) if any */ - for(i = 0 ; i < hashtable->stash.size ; i++) { - coucal_del_value_(hashtable, &hashtable->stash.items[i].value); - } - } - /* wipe top-level */ - hashtable->lg_size = 0; - hashtable->used = 0; - free(hashtable->pool.buffer); - hashtable->pool.buffer = NULL; - free(hashtable->items); - hashtable->items = NULL; - free(hashtable); - *phashtable = NULL; - } - } -} - -/* Enumerator */ - -struct_coucal_enum coucal_enum_new(coucal hashtable) { - struct_coucal_enum e; - - e.index = 0; - e.table = hashtable; - return e; -} - -coucal_item *coucal_enum_next(struct_coucal_enum * e) { - const size_t hash_size = POW2(e->table->lg_size); - for( ; e->index < hash_size - && coucal_is_free(e->table, e->index) ; e->index++) ; - /* enumerate all table */ - if (e->index < hash_size) { - coucal_item *const next = &e->table->items[e->index]; - e->index++; - return next; - } - /* enumerate stash if present */ - else if (e->index < hash_size + e->table->stash.size) { - const size_t index = e->index - hash_size; - coucal_item *const next = &e->table->stash.items[index]; - e->index++; - return next; - } - /* eof */ - else { - return NULL; - } -} - -void coucal_set_global_assert_handler(t_coucal_loghandler log, - t_coucal_asserthandler fatal) { - global_log_handler = log; - global_assert_handler = fatal; -} diff --git a/src/coucal.h b/src/coucal.h deleted file mode 100644 index 5fb03e4..0000000 --- a/src/coucal.h +++ /dev/null @@ -1,529 +0,0 @@ -/* ------------------------------------------------------------ */ -/* -Coucal, Cuckoo hashing-based hashtable with stash area. -Copyright (C) 2013-2014 Xavier Roche (http://www.httrack.com/) -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. - -3. Neither the name of the copyright holder 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 HOLDER 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. -*/ - -/** - * Coucal, a Cuckoo-hashing-based hashtable with stash area C library. - * - * This hashtable library provides key/value hashtable, with by default a - * string key, and integer/pointer value (with an associated optional - * allocator). Both key and value can be of any type, using custom settings. - * By default, the string key is dup'ed using a string pool, and the opaque - * value is stored as-is (either the pointer, or an uintptr_t integer value). - * It features O(1) average insertion, and guaranteed O(1) lookup, replace, - * and delete operations. - * - * Implementation notes: - * Implementation is auto-rehashable, and uses cuckoo hashing of size 2**n - * with a Murmur or MD5 hash function, with one additional auxiliary hash - * function. - * It also uses a small stash area to handle rare cases of collisions. - * Enumeration of all key/values is possible, deletion is also possible, but - * currently without any auto-shrinking (ie. table will never shrink). - * Overall, two main blocks are allocated: one for the items, and one for - * the keys (pool). - * If the string pool is being used (to store dup'ed keys), deleting is only - * O(1) average, because the pool needs to be compacted time to time. - * Currently the default maximum number of elements in the hashtable is - * 2**31 - 1 (that is, 2,147,483,648 elements), but this default can be changed - * by setting COUCAL_HASH_SIZE to a higher value (64 is the only higher value - * currently supported), and rebuilding the library. - * - * References: - * - * Cuckoo Hashing, by Rasmus Pagh and Flemming Friche Rodler - * http://www.it-c.dk/people/pagh/papers/cuckoo-jour.pdf - * - * Cuckoo Stash, by Adam Kirsch, Michael Mitzenmacher and Udi Wieder - * http://research.microsoft.com/pubs/73856/stash-full.9-30.pdf - * - * MurmurHash3, by Austin Appleby - * http://en.wikipedia.org/wiki/MurmurHash - * - * MD5 http://en.wikipedia.org/wiki/MD5 - * FNV http://www.isthe.com/chongo/tech/comp/fnv/ - **/ - -#ifndef COUCAL_DEFH -#define COUCAL_DEFH - -/* Includes */ -#ifdef _WIN32 -#include <stddef.h> -typedef unsigned __int32 uint32_t; -typedef unsigned __int64 uint64_t; -#elif (defined(SOLARIS) || defined(sun) || defined(HAVE_INTTYPES_H) \ - || defined(BSD) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD_kernel__)) -#include <inttypes.h> -#else -#include <stdint.h> -#endif -#include <stdarg.h> - -/* External definitions */ -#ifndef COUCAL_EXTERN -#ifdef _WIN32 -#ifdef COUCAL_BUILDING -#define COUCAL_EXTERN __declspec(dllimport) -#else -#define COUCAL_EXTERN __declspec(dllexport) -#endif -#elif ( ( defined(__GNUC__) && ( __GNUC__ >= 4 ) ) \ - || ( defined(HAVE_VISIBILITY) && HAVE_VISIBILITY ) ) -#define COUCAL_EXTERN extern __attribute__ ((visibility("default"))) -#else -#define COUCAL_EXTERN extern -#endif -#endif - -/** Key opaque type. May be a regular 'const char*'. **/ -typedef void* coucal_key; - -/** Key constant (can not be modified) opaque type. **/ -typedef const void* coucal_key_const; - -/** Opaque user-defined pointer. **/ -typedef void* coucal_opaque; - -/** Value (union of any value). **/ -typedef union coucal_value { - /** Integer value. **/ - intptr_t intg; - - /** Unsigned integer value. **/ - uintptr_t uintg; - - /** Pointer value. **/ - void *ptr; -} coucal_value; - -/** Value constant. **/ -typedef const coucal_value coucal_value_const; - -/** NULL Value. **/ -#define INTHASH_VALUE_NULL { 0 } - -#ifndef HTS_DEF_FWSTRUCT_coucal_item -#define HTS_DEF_FWSTRUCT_coucal_item -typedef struct coucal_item coucal_item; -#endif - -/** Coucal primary hash size. Default is 32-bit. **/ -#ifndef COUCAL_HASH_SIZE -#define COUCAL_HASH_SIZE 32 -#endif - -/** Hash integer type. **/ -#if (COUCAL_HASH_SIZE == 32) - -/** 32-bit hash key. **/ -typedef uint32_t coucal_hashkey; - -#elif (COUCAL_HASH_SIZE == 64) - -/** 64-bit hash key. **/ -typedef uint64_t coucal_hashkey; - -#else -#error "Unsupported COUCAL_HASH_SIZE" -#endif - -/** Pair of hashes **/ -typedef struct coucal_hashkeys { - coucal_hashkey hash1; - coucal_hashkey hash2; -} coucal_hashkeys; - -/** NULL pair of hashes. **/ -#define INTHASH_KEYS_NULL { 0, 0 } - -/** Item holding a value. **/ -struct coucal_item { - /** Key. **/ - coucal_key name; - - /** Value. **/ - coucal_value value; - - /** Hashes of the key. **/ - coucal_hashkeys hashes; -}; - -/** Log level. **/ -typedef enum coucal_loglevel { - coucal_log_critical, - coucal_log_warning, - coucal_log_info, - coucal_log_debug, - coucal_log_trace -} coucal_loglevel; - -/** free handler. Only used when values are markes as xxc **/ -typedef void (*t_coucal_key_freehandler)(coucal_opaque arg, - coucal_key key); - -/** Value free handler. Only used when values are markes as xxc **/ -typedef void (*t_coucal_value_freehandler)(coucal_opaque arg, - coucal_value value); - -/** Key dup handler. **/ -typedef coucal_key (*t_coucal_duphandler)(coucal_opaque arg, - coucal_key_const name); - -/** Key hash computation handler. **/ -typedef coucal_hashkeys (*t_coucal_hasheshandler)(coucal_opaque arg, - coucal_key_const name); - -/** Hashtable logging handler. **/ -typedef void (*t_coucal_loghandler)(coucal_opaque arg, coucal_loglevel level, - const char* format, va_list args); - -/** Hashtable fatal assertion failure. **/ -typedef void (*t_coucal_asserthandler)(coucal_opaque arg, const char* exp, - const char* file, int line); - -/** Key printer (debug) **/ -typedef const char* (*t_coucal_printkeyhandler)(coucal_opaque arg, - coucal_key_const name); - -/** Value printer (debug) **/ -typedef const char* (*t_coucal_printvaluehandler)(coucal_opaque arg, - coucal_value_const value); - -/** - * Value comparison handler (returns non-zero value if strings are equal). - **/ -typedef int (*t_coucal_cmphandler)(coucal_opaque arg, - coucal_key_const a, - coucal_key_const b); - -/** Hashtable (opaque structure). **/ -#ifndef HTS_DEF_FWSTRUCT_struct_coucal -#define HTS_DEF_FWSTRUCT_struct_coucal -typedef struct struct_coucal struct_coucal, *coucal; -#endif - -/** Hashtable enumeration (opaque structure). **/ -#ifndef HTS_DEF_FWSTRUCT_struct_coucal_enum -#define HTS_DEF_FWSTRUCT_struct_coucal_enum -typedef struct struct_coucal_enum struct_coucal_enum; -#endif - -/** Enumeration. **/ -struct struct_coucal_enum { - coucal table; - size_t index; -}; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Create a new hashtable, with initial bucket size of 'size'. - * If size is 0, use the default minimal bucket size. - * Return a non-NULL pointer upon success. - * - * By default, keys are supposed to be '\0'-terminated strings, which are - * duplicated by the library (the passed pointer does not need to be - * persistent), and values are opaque pointers (or integers) which are copied - * "as is", without further processing. Use coucal_value_set_key_handler() - * and coucal_value_set_value_handler() to alter this default behavior. - **/ -COUCAL_EXTERN coucal coucal_new(size_t size); - -/** - * Was the hashtable successfully created ? - * Return non-zero value if the hashtable is valid. - **/ -COUCAL_EXTERN int coucal_created(coucal hashtable); - -/** - * Delete a hashtable, freeing all entries. - **/ -COUCAL_EXTERN void coucal_delete(coucal * hashtable); - -/** - * Return the number of items in the hashtable. - **/ -COUCAL_EXTERN size_t coucal_nitems(coucal hashtable); - -/** - * Return the memory size taken by the hashtable. - * (This does not take account of the possible memory taken by values) - **/ -COUCAL_EXTERN size_t coucal_memory_size(coucal hashtable); - -/** - * Return the library hash size compiled. - * The returned value MUST match COUCAL_HASH_SIZE. - **/ -COUCAL_EXTERN size_t coucal_hash_size(void); - -/** - * If 'flag' is non-zero, calls coucal_value_set_value_handler() with - * default system free() handler function, otherwise, free the value handlers. - **/ -COUCAL_EXTERN void coucal_value_is_malloc(coucal hashtable, int flag); - -/** - * Set handlers for values. - * free: this handler will be called when a value is to be removed from - * the hashtable. if NULL, values won't be free'd. - * arg: opaque custom argument to be used by functions. - * Handler(s) MUST NOT be changed once elements have been added. - **/ -COUCAL_EXTERN void coucal_value_set_value_handler(coucal hashtable, - t_coucal_value_freehandler free, - coucal_opaque arg); - -/** - * Set handlers for keys. - * dup: handler called to duplicate a key. if NULL, the internal pool is used. - * free: handler called to free a key. if NULL, the internal pool is used. - * hash: hashing handler, called to hash a key. if NULL, the default hash - * function is used. - * equals: comparison handler, returning non-zero value when two keys are - * identical. if NULL, the default comparison function is used. - * arg: opaque custom argument to be used by functions. - * Handler(s) MUST NOT be changed once elements have been added. - **/ -COUCAL_EXTERN void coucal_value_set_key_handler(coucal hashtable, - t_coucal_duphandler dup, - t_coucal_key_freehandler free, - t_coucal_hasheshandler hash, - t_coucal_cmphandler equals, - coucal_opaque arg); - -/** - * Set assertion failure handler. - * log: handler called upon serious programming error - * fatal: handler called upon serious programming error - **/ -COUCAL_EXTERN void coucal_set_assert_handler(coucal hashtable, - t_coucal_loghandler log, - t_coucal_asserthandler fatal, - coucal_opaque arg); - -/** - * Set pretty print loggers (debug). Both handlers must return a string - * pointer which shall be valid until the next call. Both key and value - * pointers shall be valid at the same time. - * name: handler called to print the string representation of the name - * value: handler called to print the string representation of the value - **/ -COUCAL_EXTERN void coucal_set_print_handler(coucal hashtable, - t_coucal_printkeyhandler key, - t_coucal_printvaluehandler value, - coucal_opaque arg); - -/** - * Set the hashtable name, for degugging purpose. - * name: the hashtable name (ASCII or UTF-8) - */ -COUCAL_EXTERN void coucal_set_name(coucal hashtable, coucal_key_const name); - -/** - * Get the hashtable name, for degugging purpose. - * Return NULL if no name was defined. - **/ -COUCAL_EXTERN const char* coucal_get_name(coucal hashtable); - -/** - * Read an integer entry from the hashtable. - * Return non-zero value upon success and sets intvalue. - **/ -COUCAL_EXTERN int coucal_read(coucal hashtable, coucal_key_const name, - intptr_t * intvalue); - -/** - * Same as coucal_read(), but return 0 is the value was zero. - **/ -COUCAL_EXTERN int coucal_readptr(coucal hashtable, coucal_key_const name, - intptr_t * intvalue); - -/** - * Read an integer entry from the hashtable. - * Return 0 if the entry could not be found. - **/ -COUCAL_EXTERN intptr_t coucal_get_intptr(coucal hashtable, - coucal_key_const name); - -/** - * Return non-zero value if the given entry exists. - **/ -COUCAL_EXTERN int coucal_exists(coucal hashtable, coucal_key_const name); - -/** - * Read an entry from the hashtable. - * Return non-zero value upon success and sets value. - **/ -COUCAL_EXTERN int coucal_read_value(coucal hashtable, coucal_key_const name, - coucal_value *value); - -/** - * Write an entry to the hashtable. - * Return non-zero value if the entry was added, zero if it was replaced. - **/ -COUCAL_EXTERN int coucal_write_value(coucal hashtable, coucal_key_const name, - coucal_value_const value); - -/** - * Read a pointer entry from the hashtable. - * Return non-zero value upon success and sets value. - **/ -COUCAL_EXTERN int coucal_read_pvoid(coucal hashtable, coucal_key_const name, - void **value); - -/** - * Read a pointer entry from the hashtable and returns its value. - * Return NULL if the entry could not be found. - **/ -COUCAL_EXTERN void* coucal_get_pvoid(coucal hashtable, coucal_key_const name); - -/** - * Write a pointer entry to the hashtable. - * Return non-zero value if the entry was added, zero if it was replaced. - **/ -COUCAL_EXTERN int coucal_write_pvoid(coucal hashtable, coucal_key_const name, - void *value); - -/** - * Alias to coucal_write_pvoid() - **/ -COUCAL_EXTERN void coucal_add_pvoid(coucal hashtable, coucal_key_const name, - void *value); - -/** - * Write an integer entry to the hashtable. - * Return non-zero value if the entry was added, zero if it was replaced. - **/ -COUCAL_EXTERN int coucal_write(coucal hashtable, coucal_key_const name, - intptr_t value); - -/** - * Alias to coucal_write() - **/ -COUCAL_EXTERN void coucal_add(coucal hashtable, coucal_key_const name, - intptr_t value); - -/** - * Increment an entry value in the hashtable - * (or create a new entry with value 1 if it does not yet exist) - * Return non-zero value if the entry was added, zero if it was changed. - **/ -COUCAL_EXTERN int coucal_inc(coucal hashtable, coucal_key_const name); - -/** - * Decrement an entry value in the hashtable - * (or create a new entry with value -1 if it does not yet exist) - * Return non-zero value if the entry was added, zero if it was changed. - **/ -COUCAL_EXTERN int coucal_dec(coucal hashtable, coucal_key_const name); - -/** - * Fetch an entry value from the hashtable. - * Returns NULL if the entry could not be found. - * The returned pointer is only valid until next call to this library, and can - * be used for read or write operations. - **/ -COUCAL_EXTERN coucal_value* coucal_fetch_value(coucal hashtable, - coucal_key_const name); - -/** - * Fetch an entry value from the hashtable, given a name, and its hashes. - * Returns NULL if the entry could not be found. - * The returned pointer is only valid until next call to this library, and can - * be used for read or write operations. - * The hashes MUST have been computed using coucal_calc_hashes(), or by - * copying an existing hash during an enumeration. - * The use of a non-matching hash is safe, but will return NULL. - **/ -COUCAL_EXTERN coucal_value* coucal_fetch_value_hashes(coucal hashtable, - coucal_key_const name, - const coucal_hashkeys - *hashes); - -/** - * Remove an entry from the hashtable - * Return non-zero value if the entry was removed, zero otherwise. - **/ -COUCAL_EXTERN int coucal_remove(coucal hashtable, coucal_key_const name); - -/** - * Return a new enumerator. - * Note: deleting entries is safe while enumerating, but adding entries - * lead to undefined enumeration behavior (yet safe). - **/ -COUCAL_EXTERN struct_coucal_enum coucal_enum_new(coucal hashtable); - -/** - * Enumerate the next entry. - **/ -COUCAL_EXTERN coucal_item *coucal_enum_next(struct_coucal_enum * e); - -/** - * Compute a hash of a key for the hashtable 'hashtable'. - * The returned hash is suitable for use with coucal_fetch_value_hashes() - * Note: the default implementation uses coucal_hash_string() - **/ -COUCAL_EXTERN coucal_hashkeys coucal_calc_hashes(coucal hashtable, - coucal_key_const value); - -/** - * Compute a hash, given a string. This is the default function used for - * hashing keys, which are by default strings. This function uses - * coucal_hash_data() as backend. - **/ -COUCAL_EXTERN coucal_hashkeys coucal_hash_string(const char *value); - -/** - * Compute a hash, given an opaque buffer. - **/ -COUCAL_EXTERN coucal_hashkeys coucal_hash_data(const void *data, size_t size); - -/** - * Set default global assertion failure handler. - * The handler will be used if no specific handler was defined in the - * hashtable itself. - * log: handler called upon serious error log (opaque argument - * is the hashtable itself) - * fatal: handler called upon serious programming error (opaque argument - * is the hashtable itself) - **/ -COUCAL_EXTERN void coucal_set_global_assert_handler(t_coucal_loghandler log, - t_coucal_asserthandler fatal); - -#ifdef __cplusplus -} -#endif - -#endif |