From 272853e40a3ea0f0890eb7b1b78c35426e8cce7f Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Wed, 26 Oct 2016 23:23:05 +0200 Subject: [PATCH] Update to latest TommyDS --- cmdline/selftest.c | 51 +++++++++++++++++++++++++++-- tommyds/tommychain.h | 4 +-- tommyds/tommyhash.c | 74 ++++++++++++++++++++++++++++++++++++++++++ tommyds/tommyhash.h | 38 +++++++++++++++++++--- tommyds/tommyhashdyn.h | 7 ++-- tommyds/tommylist.c | 26 --------------- tommyds/tommylist.h | 37 ++++++++++++++++++--- tommyds/tommytree.h | 23 +++++++------ tommyds/tommytypes.h | 40 +++++++++++++++-------- 9 files changed, 234 insertions(+), 66 deletions(-) diff --git a/cmdline/selftest.c b/cmdline/selftest.c index d00b3bbc..13ce6faa 100644 --- a/cmdline/selftest.c +++ b/cmdline/selftest.c @@ -39,6 +39,11 @@ struct hash32_test_vector { uint32_t digest; }; +struct strhash32_test_vector { + char* data; + uint32_t digest; +}; + struct hash64_test_vector { const char* data; int len; @@ -77,6 +82,36 @@ static struct hash32_test_vector TEST_HASH32[] = { { 0, 0, 0 } }; +/** + * Test vectors for tommy_strhash32 + */ +struct strhash32_test_vector TEST_STRHASH32[] = { + { "", 0x0af1416d }, + { "a", 0x68fa0f3f }, + { "abc", 0xfc68ffc5 }, + { "message digest", 0x08477b63 }, + { "abcdefghijklmnopqrstuvwxyz", 0x5b9c25e5 }, + { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 0x1e530ce7 }, + { "The quick brown fox jumps over the lazy dog", 0xaf93eefe }, + { "\xff", 0xfc88801b }, + { "\x16\x27", 0xcd7216db }, + { "\xe2\x56\xb4", 0x05f98d02 }, + { "\xc9\x4d\x9c\xda", 0xf65206f8 }, + { "\x79\xf1\x29\x69\x5d", 0x72bd6bda }, + { "\xff\x7e\xdf\x1e\x31\x1c", 0x57dfb9b4 }, + { "\x2a\x4c\xe1\xff\x9e\x6f\x53", 0x499ff634 }, + { "\xba\x02\xab\x18\x30\xc5\x0e\x8a", 0xe896b7ce }, + { "\xec\x4e\x7a\x72\x1e\x71\x2a\xc9\x33", 0xfe3939f0 }, + { "\xfd\xe2\x9c\x0f\x72\xb7\x08\xea\xd0\x78", 0x4351d482 }, + { "\x65\xc4\x8a\xb8\x80\x86\x9a\x79\xff\xb7\xae", 0x88e92135 }, + { "\x77\xe9\xd7\x80\x0e\x3f\x5c\x43\xc8\xc2\x46\x39", 0x01109c16 }, + { "\x87\xd8\x61\x61\x4c\x89\x17\x4e\xa1\xa4\xef\x13\xa9", 0xbcb050dc }, + { "\xfe\xa6\x5b\xc2\xda\xe8\x95\xd4\x64\xab\x4c\x39\x58\x29", 0xbe5e1fd5 }, + { "\x94\x49\xc0\x78\xa0\x80\xda\xc7\x71\x4e\x17\x37\xa9\x7c\x40", 0x70d8c97f }, + { "\x53\x7e\x36\xb4\x2e\xc9\xb9\xcc\x18\x3e\x9a\x5f\xfc\xb7\xb0\x61", 0x957440a9 }, + { 0, 0 } +}; + /** * Test vectors for tommy_hash64 */ @@ -157,7 +192,7 @@ static void test_hash(void) for (i = 0; TEST_HASH32[i].data; ++i) { uint32_t digest; memcpy(buffer_aligned, TEST_HASH32[i].data, TEST_HASH32[i].len); - digest = tommy_hash_u32(seed32, buffer_aligned, TEST_MURMUR3[i].len); + digest = tommy_hash_u32(seed32, buffer_aligned, TEST_HASH32[i].len); if (digest != TEST_HASH32[i].digest) { /* LCOV_EXCL_START */ log_fatal("Failed hash32 test\n"); @@ -166,10 +201,22 @@ static void test_hash(void) } } + for (i = 0; TEST_STRHASH32[i].data; ++i) { + uint32_t digest; + memcpy(buffer_aligned, TEST_STRHASH32[i].data, strlen(TEST_STRHASH32[i].data) + 1); + digest = tommy_strhash_u32(seed32, buffer_aligned); + if (digest != TEST_STRHASH32[i].digest) { + /* LCOV_EXCL_START */ + log_fatal("Failed strhash32 test\n"); + exit(EXIT_FAILURE); + /* LCOV_EXCL_STOP */ + } + } + for (i = 0; TEST_HASH64[i].data; ++i) { uint64_t digest; memcpy(buffer_aligned, TEST_HASH64[i].data, TEST_HASH64[i].len); - digest = tommy_hash_u64(seed64, buffer_aligned, TEST_MURMUR3[i].len); + digest = tommy_hash_u64(seed64, buffer_aligned, TEST_HASH64[i].len); if (digest != TEST_HASH64[i].digest) { /* LCOV_EXCL_START */ log_fatal("Failed hash64 test\n"); diff --git a/tommyds/tommychain.h b/tommyds/tommychain.h index 71d3cccf..b15bc6ae 100644 --- a/tommyds/tommychain.h +++ b/tommyds/tommychain.h @@ -50,8 +50,8 @@ * - 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_node* head; /**< Pointer to the head of the chain. */ + tommy_node* tail; /**< Pointer to the tail of the chain. */ } tommy_chain; /** diff --git a/tommyds/tommyhash.c b/tommyds/tommyhash.c index 004c2ae2..05d78d25 100644 --- a/tommyds/tommyhash.c +++ b/tommyds/tommyhash.c @@ -165,3 +165,77 @@ tommy_uint64_t tommy_hash_u64(tommy_uint64_t init_val, const void* void_key, tom return c + ((tommy_uint64_t)b << 32); } +tommy_uint32_t tommy_strhash_u32(tommy_uint64_t init_val, const void* void_key) +{ + const unsigned char* key = tommy_cast(const unsigned char*, void_key); + tommy_uint32_t a, b, c; + tommy_uint32_t m[3] = { 0xff, 0xff00, 0xff0000 }; + + a = b = c = 0xdeadbeef + init_val; + /* this is different than original lookup3 and the result won't match */ + + while (1) { + tommy_uint32_t v = tommy_le_uint32_read(key); + + if (tommy_haszero_u32(v)) { + if (v & m[0]) { + a += v & m[0]; + if (v & m[1]) { + a += v & m[1]; + if (v & m[2]) + a += v & m[2]; + } + } + + break; + } + + a += v; + + v = tommy_le_uint32_read(key + 4); + + if (tommy_haszero_u32(v)) { + if (v & m[0]) { + b += v & m[0]; + if (v & m[1]) { + b += v & m[1]; + if (v & m[2]) + b += v & m[2]; + } + } + + break; + } + + b += v; + + v = tommy_le_uint32_read(key + 8); + + if (tommy_haszero_u32(v)) { + if (v & m[0]) { + c += v & m[0]; + if (v & m[1]) { + c += v & m[1]; + if (v & m[2]) + c += v & m[2]; + } + } + + break; + } + + c += v; + + tommy_mix(a, b, c); + + key += 12; + } + + /* for lengths that are multiplers of 12 we already have called mix */ + /* this is different than the original lookup3 and the result won't match */ + + tommy_final(a, b, c); + + return c; +} + diff --git a/tommyds/tommyhash.h b/tommyds/tommyhash.h index 702e8ef4..738e33d2 100644 --- a/tommyds/tommyhash.h +++ b/tommyds/tommyhash.h @@ -46,10 +46,15 @@ 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(). + * + * This hash is designed to provide a good overall performance in all platforms, + * including 32 bits. If you target only 64 bits, you can use faster hashes, + * like SpookyHash or FarmHash. + * * \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 void_key Pointer to the data to hash. * \param key_len Size of the data to hash. * \note * This function is endianess independent. @@ -61,10 +66,15 @@ tommy_uint32_t tommy_hash_u32(tommy_uint32_t init_val, const void* void_key, tom * 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(). + * + * This hash is designed to provide a good overall performance in all platforms, + * including 32 bits. If you target only 64 bits, you can use faster hashes, + * like SpookyHash or FarmHash. + * * \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 void_key Pointer to the data to hash. * \param key_len Size of the data to hash. * \note * This function is endianess independent. @@ -73,7 +83,25 @@ tommy_uint32_t tommy_hash_u32(tommy_uint32_t init_val, const void* void_key, tom tommy_uint64_t tommy_hash_u64(tommy_uint64_t init_val, const void* void_key, tommy_size_t key_len); /** - * Integer hash of 32 bits. + * String hash function with a 32 bits result. + * Implementation is based on the the Robert Jenkins "lookup3" hash 32 bits version, + * from http://www.burtleburtle.net/bob/hash/doobs.html, function hashlittle(). + * + * This hash is designed to handle strings with an unknown length. If you + * know the string length, the other hash functions are surely faster. + * + * \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 to the string to hash. It has to be 0 terminated. + * \note + * This function is endianess independent. + * \return The hash value of 32 bits. + */ +tommy_uint32_t tommy_strhash_u32(tommy_uint64_t init_val, const void* void_key); + +/** + * Integer reversible hash function for 32 bits. * Implementation of the Robert Jenkins "4-byte Integer Hashing", * from http://burtleburtle.net/bob/hash/integer.html */ @@ -91,9 +119,9 @@ tommy_inline tommy_uint32_t tommy_inthash_u32(tommy_uint32_t key) } /** - * Integer hash of 64 bits. + * Integer reversible hash function for 64 bits. * Implementation of the Thomas Wang "Integer Hash Function", - * from http://www.cris.com/~Ttwang/tech/inthash.htm + * 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) { diff --git a/tommyds/tommyhashdyn.h b/tommyds/tommyhashdyn.h index afbdfabb..ed4a6074 100644 --- a/tommyds/tommyhashdyn.h +++ b/tommyds/tommyhashdyn.h @@ -60,9 +60,9 @@ * * \code * struct object { - * tommy_node node; - * // other fields * int value; + * // other fields + * tommy_node node; * }; * * struct object* obj = malloc(sizeof(struct object)); // creates the object @@ -243,7 +243,8 @@ void* tommy_hashdyn_remove_existing(tommy_hashdyn* hashdyn, tommy_hashdyn_node* /** * Calls the specified function for each element in the hashtable. * - * You can use this function to deallocate all the elements inserted. + * You cannot add or remove elements from the inside of the callback, + * but can use it to deallocate them. * * \code * tommy_hashdyn hashdyn; diff --git a/tommyds/tommylist.c b/tommyds/tommylist.c index ea85b08c..1fa1f243 100644 --- a/tommyds/tommylist.c +++ b/tommyds/tommylist.c @@ -28,32 +28,6 @@ #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. */ diff --git a/tommyds/tommylist.h b/tommyds/tommylist.h index 8004a44a..d88cee7b 100644 --- a/tommyds/tommylist.h +++ b/tommyds/tommylist.h @@ -57,9 +57,9 @@ * * \code * struct object { - * tommy_node node; - * // other fields * int value; + * // other fields + * tommy_node node; * }; * * struct object* obj = malloc(sizeof(struct object)); // creates the object @@ -278,7 +278,34 @@ tommy_inline void* tommy_list_remove_existing(tommy_list* list, tommy_node* node * \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); +tommy_inline void tommy_list_concat(tommy_list* first, tommy_list* second) +{ + tommy_node* first_head; + tommy_node* first_tail; + tommy_node* second_head; + + /* if the second is empty, nothing to do */ + second_head = tommy_list_head(second); + if (second_head == 0) + return; + + /* if the first is empty, copy the second */ + first_head = tommy_list_head(first); + if (first_head == 0) { + *first = *second; + return; + } + + /* tail of the first list */ + first_tail = first_head->prev; + + /* 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; +} /** * Sorts a list. @@ -318,8 +345,8 @@ tommy_inline tommy_count_t tommy_list_count(tommy_list* list) /** * Calls the specified function for each element in the list. * - * You can use this function to deallocate all the elements - * inserted in a list. + * You cannot add or remove elements from the inside of the callback, + * but can use it to deallocate them. * * \code * tommy_list list; diff --git a/tommyds/tommytree.h b/tommyds/tommytree.h index a183f6cd..1eeda24d 100644 --- a/tommyds/tommytree.h +++ b/tommyds/tommytree.h @@ -29,16 +29,16 @@ * AVL tree. * * This tree is a standard AVL tree implementation that stores elements in the - * order defined by the comparsion function. + * order defined by the comparison function. * - * As difference from other tommy containers, duplicate elements cannot be inserted. - * - * \code - * tommy_tree tree; + * As difference than other tommy containers, duplicate elements cannot be inserted. * * To initialize a tree you have to call tommy_tree_init() specifing a comparison * function that will define the order in the tree. * + * \code + * tommy_tree tree; + * * tommy_tree_init(&tree, cmp); * \endcode * @@ -79,7 +79,7 @@ * To remove an element from the tree you have to call tommy_tree_remove() * providing the key to search and remove. * - * \code + * \code * struct object value_to_remove = { 1 }; * struct object* obj = tommy_tree_remove(&tree, &value_to_remove); * if (obj) { @@ -87,7 +87,8 @@ * } * \endcode * - * To destroy the tree you have to remove all the elements. + * To destroy the tree you have to remove or destroy all the contained elements. + * The tree itself doesn't have or need a deallocation function. * * If you need to iterate over all the elements in the tree, you can use * tommy_tree_foreach() or tommy_tree_foreach_arg(). @@ -129,10 +130,11 @@ void tommy_tree_init(tommy_tree* tree, tommy_compare_func* cmp); /** * Inserts an element in the tree. * If the element is already present, it's not inserted again. + * Check the return value to identify if the element was already present or not. * You have to provide the pointer of the node embedded into the object and * the pointer at the object. - * \param node Pointer at the node embedded into the object to insert. - * \param data Pointer at the object to insert. + * \param node Pointer to the node embedded into the object to insert. + * \param data Pointer to the object to insert. * \return The element in the tree. Either the already existing one, or the one just inserted. */ void* tommy_tree_insert(tommy_tree* tree, tommy_tree_node* node, void* data); @@ -176,7 +178,8 @@ void* tommy_tree_remove_existing(tommy_tree* tree, tommy_tree_node* node); * * The elements are processed in order. * - * You can use this function to deallocate all the elements inserted. + * You cannot add or remove elements from the inside of the callback, + * but can use it to deallocate them. * * \code * tommy_tree tree; diff --git a/tommyds/tommytypes.h b/tommyds/tommytypes.h index 45d3d1a5..ff834cf6 100644 --- a/tommyds/tommytypes.h +++ b/tommyds/tommytypes.h @@ -194,7 +194,7 @@ typedef struct tommy_node_struct { struct tommy_node_struct* prev; /** - * Pointer at the object containing the node. + * Pointer to the object containing the node. * This field is initialized when inserting nodes into a data structure. */ void* data; @@ -212,8 +212,8 @@ typedef struct tommy_node_struct { /** * Compare function for elements. - * \param obj_a Pointer at the first object to compare. - * \param obj_b Pointer at the second object to compare. + * \param obj_a Pointer to the first object to compare. + * \param obj_b Pointer to 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(). @@ -241,13 +241,15 @@ 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. + * \param arg Pointer to the value to search as passed at the search function. + * \param obj Pointer to 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. + * The first argument is a pointer to the value to search exactly + * as it's passed at the search function called. + * The second argument is a pointer to the object inside the hashtable to compare. + * + * The return value has to be 0 if the values are equal. != 0 if they are different. * * \code * struct object { @@ -257,7 +259,10 @@ typedef int tommy_compare_func(const void* obj_a, const void* obj_b); * * int compare(const void* arg, const void* obj) * { - * return *(const int*)arg != ((const struct object*)obj)->value; + * const int* value_to_find = arg; + * const struct object* object_to_compare = obj; + * + * return *value_to_find != object_to_compare->value; * } * * int value_to_find = 1; @@ -274,7 +279,7 @@ typedef int tommy_search_func(const void* arg, const void* obj); /** * Foreach function. - * \param obj Pointer at the object to iterate. + * \param obj Pointer to the object to iterate. * * A typical example is to use free() to deallocate all the objects in a list. * \code @@ -285,8 +290,8 @@ 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. + * \param arg Pointer to a generic argument. + * \param obj Pointer to the object to iterate. */ typedef void tommy_foreach_arg_func(void* arg, void* obj); @@ -394,7 +399,7 @@ tommy_inline tommy_uint_t tommy_ctz_u32(tommy_uint32_t 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 */ + /* from http://graphics.stanford.edu/~seander/bithacks.html */ --value; value |= value >> 1; @@ -406,5 +411,14 @@ tommy_inline tommy_uint32_t tommy_roundup_pow2_u32(tommy_uint32_t value) return value; } + +/** + * Check if the specified word has a byte at 0. + * \return 0 or 1. + */ +tommy_inline int tommy_haszero_u32(tommy_uint32_t value) +{ + return ((value - 0x01010101) & ~value & 0x80808080) != 0; +} #endif