diff --git a/c_src/mqtree/mqtree.c b/c_src/.mqtree/mqtree.c similarity index 100% rename from c_src/mqtree/mqtree.c rename to c_src/.mqtree/mqtree.c diff --git a/c_src/mqtree/rebar.config b/c_src/.mqtree/rebar.config similarity index 100% rename from c_src/mqtree/rebar.config rename to c_src/.mqtree/rebar.config diff --git a/c_src/mqtree/uthash.h b/c_src/.mqtree/uthash.h similarity index 100% rename from c_src/mqtree/uthash.h rename to c_src/.mqtree/uthash.h diff --git a/c_src/couchdb-khash/hash.c b/c_src/couchdb-khash/hash.c new file mode 100644 index 0000000..e3da0da --- /dev/null +++ b/c_src/couchdb-khash/hash.c @@ -0,0 +1,843 @@ +/* + * Hash Table Data Type + * Copyright (C) 1997 Kaz Kylheku + * + * Free Software License: + * + * All rights are reserved by the author, with the following exceptions: + * Permission is granted to freely reproduce and distribute this software, + * possibly in exchange for a fee, provided that this copyright notice appears + * intact. Permission is also granted to adapt this software to produce + * derivative works, as long as the modified versions carry this copyright + * notice and additional notices stating that the work has been modified. + * This source code may be translated into executable form and incorporated + * into proprietary software; there is no requirement for such software to + * contain a copyright notice related to this source. + * + * $Id: hash.c,v 1.36.2.11 2000/11/13 01:36:45 kaz Exp $ + * $Name: kazlib_1_20 $ + */ + +#include +#include +#include +#include +#define HASH_IMPLEMENTATION +#include "hash.h" + +#ifdef KAZLIB_RCSID +static const char rcsid[] = "$Id: hash.c,v 1.36.2.11 2000/11/13 01:36:45 kaz Exp $"; +#endif + +#define INIT_BITS 6 +#define INIT_SIZE (1UL << (INIT_BITS)) /* must be power of two */ +#define INIT_MASK ((INIT_SIZE) - 1) + +#define next hash_next +#define key hash_key +#define data hash_data +#define hkey hash_hkey + +#define table hash_table +#define nchains hash_nchains +#define nodecount hash_nodecount +#define maxcount hash_maxcount +#define highmark hash_highmark +#define lowmark hash_lowmark +#define compare hash_compare +#define function hash_function +#define allocnode hash_allocnode +#define freenode hash_freenode +#define context hash_context +#define mask hash_mask +#define dynamic hash_dynamic + +#define table hash_table +#define chain hash_chain + +static hnode_t *kl_hnode_alloc(void *context); +static void kl_hnode_free(hnode_t *node, void *context); +static hash_val_t hash_fun_default(const void *key); +static int hash_comp_default(const void *key1, const void *key2); + +int hash_val_t_bit; + +/* + * Compute the number of bits in the hash_val_t type. We know that hash_val_t + * is an unsigned integral type. Thus the highest value it can hold is a + * Mersenne number (power of two, less one). We initialize a hash_val_t + * object with this value and then shift bits out one by one while counting. + * Notes: + * 1. HASH_VAL_T_MAX is a Mersenne number---one that is one less than a power + * of two. This means that its binary representation consists of all one + * bits, and hence ``val'' is initialized to all one bits. + * 2. While bits remain in val, we increment the bit count and shift it to the + * right, replacing the topmost bit by zero. + */ + +static void compute_bits(void) +{ + hash_val_t val = HASH_VAL_T_MAX; /* 1 */ + int bits = 0; + + while (val) { /* 2 */ + bits++; + val >>= 1; + } + + hash_val_t_bit = bits; +} + +/* + * Verify whether the given argument is a power of two. + */ + +static int is_power_of_two(hash_val_t arg) +{ + if (arg == 0) + return 0; + while ((arg & 1) == 0) + arg >>= 1; + return (arg == 1); +} + +/* + * Compute a shift amount from a given table size + */ + +static hash_val_t compute_mask(hashcount_t size) +{ + assert (is_power_of_two(size)); + assert (size >= 2); + + return size - 1; +} + +/* + * Initialize the table of pointers to null. + */ + +static void clear_table(hash_t *hash) +{ + hash_val_t i; + + for (i = 0; i < hash->nchains; i++) + hash->table[i] = NULL; +} + +/* + * Double the size of a dynamic table. This works as follows. Each chain splits + * into two adjacent chains. The shift amount increases by one, exposing an + * additional bit of each hashed key. For each node in the original chain, the + * value of this newly exposed bit will decide which of the two new chains will + * receive the node: if the bit is 1, the chain with the higher index will have + * the node, otherwise the lower chain will receive the node. In this manner, + * the hash table will continue to function exactly as before without having to + * rehash any of the keys. + * Notes: + * 1. Overflow check. + * 2. The new number of chains is twice the old number of chains. + * 3. The new mask is one bit wider than the previous, revealing a + * new bit in all hashed keys. + * 4. Allocate a new table of chain pointers that is twice as large as the + * previous one. + * 5. If the reallocation was successful, we perform the rest of the growth + * algorithm, otherwise we do nothing. + * 6. The exposed_bit variable holds a mask with which each hashed key can be + * AND-ed to test the value of its newly exposed bit. + * 7. Now loop over each chain in the table and sort its nodes into two + * chains based on the value of each node's newly exposed hash bit. + * 8. The low chain replaces the current chain. The high chain goes + * into the corresponding sister chain in the upper half of the table. + * 9. We have finished dealing with the chains and nodes. We now update + * the various bookeeping fields of the hash structure. + */ + +static void grow_table(hash_t *hash) +{ + hnode_t **newtable; + + assert (2 * hash->nchains > hash->nchains); /* 1 */ + + newtable = realloc(hash->table, + sizeof *newtable * hash->nchains * 2); /* 4 */ + + if (newtable) { /* 5 */ + hash_val_t mask = (hash->mask << 1) | 1; /* 3 */ + hash_val_t exposed_bit = mask ^ hash->mask; /* 6 */ + hash_val_t chain; + + assert (mask != hash->mask); + + for (chain = 0; chain < hash->nchains; chain++) { /* 7 */ + hnode_t *low_chain = 0, *high_chain = 0, *hptr, *next; + + for (hptr = newtable[chain]; hptr != 0; hptr = next) { + next = hptr->next; + + if (hptr->hkey & exposed_bit) { + hptr->next = high_chain; + high_chain = hptr; + } else { + hptr->next = low_chain; + low_chain = hptr; + } + } + + newtable[chain] = low_chain; /* 8 */ + newtable[chain + hash->nchains] = high_chain; + } + + hash->table = newtable; /* 9 */ + hash->mask = mask; + hash->nchains *= 2; + hash->lowmark *= 2; + hash->highmark *= 2; + } + assert (kl_hash_verify(hash)); +} + +/* + * Cut a table size in half. This is done by folding together adjacent chains + * and populating the lower half of the table with these chains. The chains are + * simply spliced together. Once this is done, the whole table is reallocated + * to a smaller object. + * Notes: + * 1. It is illegal to have a hash table with one slot. This would mean that + * hash->shift is equal to hash_val_t_bit, an illegal shift value. + * Also, other things could go wrong, such as hash->lowmark becoming zero. + * 2. Looping over each pair of sister chains, the low_chain is set to + * point to the head node of the chain in the lower half of the table, + * and high_chain points to the head node of the sister in the upper half. + * 3. The intent here is to compute a pointer to the last node of the + * lower chain into the low_tail variable. If this chain is empty, + * low_tail ends up with a null value. + * 4. If the lower chain is not empty, we simply tack the upper chain onto it. + * If the upper chain is a null pointer, nothing happens. + * 5. Otherwise if the lower chain is empty but the upper one is not, + * If the low chain is empty, but the high chain is not, then the + * high chain is simply transferred to the lower half of the table. + * 6. Otherwise if both chains are empty, there is nothing to do. + * 7. All the chain pointers are in the lower half of the table now, so + * we reallocate it to a smaller object. This, of course, invalidates + * all pointer-to-pointers which reference into the table from the + * first node of each chain. + * 8. Though it's unlikely, the reallocation may fail. In this case we + * pretend that the table _was_ reallocated to a smaller object. + * 9. Finally, update the various table parameters to reflect the new size. + */ + +static void shrink_table(hash_t *hash) +{ + hash_val_t chain, nchains; + hnode_t **newtable, *low_tail, *low_chain, *high_chain; + + assert (hash->nchains >= 2); /* 1 */ + nchains = hash->nchains / 2; + + for (chain = 0; chain < nchains; chain++) { + low_chain = hash->table[chain]; /* 2 */ + high_chain = hash->table[chain + nchains]; + for (low_tail = low_chain; low_tail && low_tail->next; low_tail = low_tail->next) + ; /* 3 */ + if (low_chain != 0) /* 4 */ + low_tail->next = high_chain; + else if (high_chain != 0) /* 5 */ + hash->table[chain] = high_chain; + else + assert (hash->table[chain] == NULL); /* 6 */ + } + newtable = realloc(hash->table, + sizeof *newtable * nchains); /* 7 */ + if (newtable) /* 8 */ + hash->table = newtable; + hash->mask >>= 1; /* 9 */ + hash->nchains = nchains; + hash->lowmark /= 2; + hash->highmark /= 2; + assert (kl_hash_verify(hash)); +} + + +/* + * Create a dynamic hash table. Both the hash table structure and the table + * itself are dynamically allocated. Furthermore, the table is extendible in + * that it will automatically grow as its load factor increases beyond a + * certain threshold. + * Notes: + * 1. If the number of bits in the hash_val_t type has not been computed yet, + * we do so here, because this is likely to be the first function that the + * user calls. + * 2. Allocate a hash table control structure. + * 3. If a hash table control structure is successfully allocated, we + * proceed to initialize it. Otherwise we return a null pointer. + * 4. We try to allocate the table of hash chains. + * 5. If we were able to allocate the hash chain table, we can finish + * initializing the hash structure and the table. Otherwise, we must + * backtrack by freeing the hash structure. + * 6. INIT_SIZE should be a power of two. The high and low marks are always set + * to be twice the table size and half the table size respectively. When the + * number of nodes in the table grows beyond the high size (beyond load + * factor 2), it will double in size to cut the load factor down to about + * about 1. If the table shrinks down to or beneath load factor 0.5, + * it will shrink, bringing the load up to about 1. However, the table + * will never shrink beneath INIT_SIZE even if it's emptied. + * 7. This indicates that the table is dynamically allocated and dynamically + * resized on the fly. A table that has this value set to zero is + * assumed to be statically allocated and will not be resized. + * 8. The table of chains must be properly reset to all null pointers. + */ + +hash_t *kl_hash_create(hashcount_t maxcount, hash_comp_t compfun, + hash_fun_t hashfun) +{ + hash_t *hash; + + if (hash_val_t_bit == 0) /* 1 */ + compute_bits(); + + hash = malloc(sizeof *hash); /* 2 */ + + if (hash) { /* 3 */ + hash->table = malloc(sizeof *hash->table * INIT_SIZE); /* 4 */ + if (hash->table) { /* 5 */ + hash->nchains = INIT_SIZE; /* 6 */ + hash->highmark = INIT_SIZE * 2; + hash->lowmark = INIT_SIZE / 2; + hash->nodecount = 0; + hash->maxcount = maxcount; + hash->compare = compfun ? compfun : hash_comp_default; + hash->function = hashfun ? hashfun : hash_fun_default; + hash->allocnode = kl_hnode_alloc; + hash->freenode = kl_hnode_free; + hash->context = NULL; + hash->mask = INIT_MASK; + hash->dynamic = 1; /* 7 */ + clear_table(hash); /* 8 */ + assert (kl_hash_verify(hash)); + return hash; + } + free(hash); + } + + return NULL; +} + +/* + * Select a different set of node allocator routines. + */ + +void kl_hash_set_allocator(hash_t *hash, hnode_alloc_t al, + hnode_free_t fr, void *context) +{ + assert (kl_hash_count(hash) == 0); + assert ((al == 0 && fr == 0) || (al != 0 && fr != 0)); + + hash->allocnode = al ? al : kl_hnode_alloc; + hash->freenode = fr ? fr : kl_hnode_free; + hash->context = context; +} + +/* + * Free every node in the hash using the hash->freenode() function pointer, and + * cause the hash to become empty. + */ + +void kl_hash_free_nodes(hash_t *hash) +{ + hscan_t hs; + hnode_t *node; + kl_hash_scan_begin(&hs, hash); + while ((node = kl_hash_scan_next(&hs))) { + kl_hash_scan_delete(hash, node); + hash->freenode(node, hash->context); + } + hash->nodecount = 0; + clear_table(hash); +} + +/* + * Obsolescent function for removing all nodes from a table, + * freeing them and then freeing the table all in one step. + */ + +void kl_hash_free(hash_t *hash) +{ +#ifdef KAZLIB_OBSOLESCENT_DEBUG + assert ("call to obsolescent function hash_free()" && 0); +#endif + kl_hash_free_nodes(hash); + kl_hash_destroy(hash); +} + +/* + * Free a dynamic hash table structure. + */ + +void kl_hash_destroy(hash_t *hash) +{ + assert (hash_val_t_bit != 0); + assert (kl_hash_isempty(hash)); + free(hash->table); + free(hash); +} + +/* + * Initialize a user supplied hash structure. The user also supplies a table of + * chains which is assigned to the hash structure. The table is static---it + * will not grow or shrink. + * 1. See note 1. in hash_create(). + * 2. The user supplied array of pointers hopefully contains nchains nodes. + * 3. See note 7. in hash_create(). + * 4. We must dynamically compute the mask from the given power of two table + * size. + * 5. The user supplied table can't be assumed to contain null pointers, + * so we reset it here. + */ + +hash_t *kl_hash_init(hash_t *hash, hashcount_t maxcount, + hash_comp_t compfun, hash_fun_t hashfun, hnode_t **table, + hashcount_t nchains) +{ + if (hash_val_t_bit == 0) /* 1 */ + compute_bits(); + + assert (is_power_of_two(nchains)); + + hash->table = table; /* 2 */ + hash->nchains = nchains; + hash->nodecount = 0; + hash->maxcount = maxcount; + hash->compare = compfun ? compfun : hash_comp_default; + hash->function = hashfun ? hashfun : hash_fun_default; + hash->dynamic = 0; /* 3 */ + hash->mask = compute_mask(nchains); /* 4 */ + clear_table(hash); /* 5 */ + + assert (kl_hash_verify(hash)); + + return hash; +} + +/* + * Reset the hash scanner so that the next element retrieved by + * hash_scan_next() shall be the first element on the first non-empty chain. + * Notes: + * 1. Locate the first non empty chain. + * 2. If an empty chain is found, remember which one it is and set the next + * pointer to refer to its first element. + * 3. Otherwise if a chain is not found, set the next pointer to NULL + * so that hash_scan_next() shall indicate failure. + */ + +void kl_hash_scan_begin(hscan_t *scan, hash_t *hash) +{ + hash_val_t nchains = hash->nchains; + hash_val_t chain; + + scan->table = hash; + + /* 1 */ + + for (chain = 0; chain < nchains && hash->table[chain] == 0; chain++) + ; + + if (chain < nchains) { /* 2 */ + scan->chain = chain; + scan->next = hash->table[chain]; + } else { /* 3 */ + scan->next = NULL; + } +} + +/* + * Retrieve the next node from the hash table, and update the pointer + * for the next invocation of hash_scan_next(). + * Notes: + * 1. Remember the next pointer in a temporary value so that it can be + * returned. + * 2. This assertion essentially checks whether the module has been properly + * initialized. The first point of interaction with the module should be + * either hash_create() or hash_init(), both of which set hash_val_t_bit to + * a non zero value. + * 3. If the next pointer we are returning is not NULL, then the user is + * allowed to call hash_scan_next() again. We prepare the new next pointer + * for that call right now. That way the user is allowed to delete the node + * we are about to return, since we will no longer be needing it to locate + * the next node. + * 4. If there is a next node in the chain (next->next), then that becomes the + * new next node, otherwise ... + * 5. We have exhausted the current chain, and must locate the next subsequent + * non-empty chain in the table. + * 6. If a non-empty chain is found, the first element of that chain becomes + * the new next node. Otherwise there is no new next node and we set the + * pointer to NULL so that the next time hash_scan_next() is called, a null + * pointer shall be immediately returned. + */ + + +hnode_t *kl_hash_scan_next(hscan_t *scan) +{ + hnode_t *next = scan->next; /* 1 */ + hash_t *hash = scan->table; + hash_val_t chain = scan->chain + 1; + hash_val_t nchains = hash->nchains; + + assert (hash_val_t_bit != 0); /* 2 */ + + if (next) { /* 3 */ + if (next->next) { /* 4 */ + scan->next = next->next; + } else { + while (chain < nchains && hash->table[chain] == 0) /* 5 */ + chain++; + if (chain < nchains) { /* 6 */ + scan->chain = chain; + scan->next = hash->table[chain]; + } else { + scan->next = NULL; + } + } + } + return next; +} + +/* + * Insert a node into the hash table. + * Notes: + * 1. It's illegal to insert more than the maximum number of nodes. The client + * should verify that the hash table is not full before attempting an + * insertion. + * 2. The same key may not be inserted into a table twice. + * 3. If the table is dynamic and the load factor is already at >= 2, + * grow the table. + * 4. We take the bottom N bits of the hash value to derive the chain index, + * where N is the base 2 logarithm of the size of the hash table. + */ + +void kl_hash_insert(hash_t *hash, hnode_t *node, const void *key) +{ + hash_val_t hkey, chain; + + assert (hash_val_t_bit != 0); + assert (node->next == NULL); + assert (hash->nodecount < hash->maxcount); /* 1 */ + assert (kl_hash_lookup(hash, key) == NULL); /* 2 */ + + if (hash->dynamic && hash->nodecount >= hash->highmark) /* 3 */ + grow_table(hash); + + hkey = hash->function(key); + chain = hkey & hash->mask; /* 4 */ + + node->key = key; + node->hkey = hkey; + node->next = hash->table[chain]; + hash->table[chain] = node; + hash->nodecount++; + + assert (kl_hash_verify(hash)); +} + +/* + * Find a node in the hash table and return a pointer to it. + * Notes: + * 1. We hash the key and keep the entire hash value. As an optimization, when + * we descend down the chain, we can compare hash values first and only if + * hash values match do we perform a full key comparison. + * 2. To locate the chain from among 2^N chains, we look at the lower N bits of + * the hash value by anding them with the current mask. + * 3. Looping through the chain, we compare the stored hash value inside each + * node against our computed hash. If they match, then we do a full + * comparison between the unhashed keys. If these match, we have located the + * entry. + */ + +hnode_t *kl_hash_lookup(hash_t *hash, const void *key) +{ + hash_val_t hkey, chain; + hnode_t *nptr; + + hkey = hash->function(key); /* 1 */ + chain = hkey & hash->mask; /* 2 */ + + for (nptr = hash->table[chain]; nptr; nptr = nptr->next) { /* 3 */ + if (nptr->hkey == hkey && hash->compare(nptr->key, key) == 0) + return nptr; + } + + return NULL; +} + +/* + * Delete the given node from the hash table. Since the chains + * are singly linked, we must locate the start of the node's chain + * and traverse. + * Notes: + * 1. The node must belong to this hash table, and its key must not have + * been tampered with. + * 2. If this deletion will take the node count below the low mark, we + * shrink the table now. + * 3. Determine which chain the node belongs to, and fetch the pointer + * to the first node in this chain. + * 4. If the node being deleted is the first node in the chain, then + * simply update the chain head pointer. + * 5. Otherwise advance to the node's predecessor, and splice out + * by updating the predecessor's next pointer. + * 6. Indicate that the node is no longer in a hash table. + */ + +hnode_t *kl_hash_delete(hash_t *hash, hnode_t *node) +{ + hash_val_t chain; + hnode_t *hptr; + + assert (kl_hash_lookup(hash, node->key) == node); /* 1 */ + assert (hash_val_t_bit != 0); + + if (hash->dynamic && hash->nodecount <= hash->lowmark + && hash->nodecount > INIT_SIZE) + shrink_table(hash); /* 2 */ + + chain = node->hkey & hash->mask; /* 3 */ + hptr = hash->table[chain]; + + if (hptr == node) { /* 4 */ + hash->table[chain] = node->next; + } else { + while (hptr->next != node) { /* 5 */ + assert (hptr != 0); + hptr = hptr->next; + } + assert (hptr->next == node); + hptr->next = node->next; + } + + hash->nodecount--; + assert (kl_hash_verify(hash)); + + node->next = NULL; /* 6 */ + return node; +} + +int kl_hash_alloc_insert(hash_t *hash, const void *key, void *data) +{ + hnode_t *node = hash->allocnode(hash->context); + + if (node) { + kl_hnode_init(node, data); + kl_hash_insert(hash, node, key); + return 1; + } + return 0; +} + +void kl_hash_delete_free(hash_t *hash, hnode_t *node) +{ + kl_hash_delete(hash, node); + hash->freenode(node, hash->context); +} + +/* + * Exactly like hash_delete, except does not trigger table shrinkage. This is to be + * used from within a hash table scan operation. See notes for hash_delete. + */ + +hnode_t *kl_hash_scan_delete(hash_t *hash, hnode_t *node) +{ + hash_val_t chain; + hnode_t *hptr; + + assert (kl_hash_lookup(hash, node->key) == node); + assert (hash_val_t_bit != 0); + + chain = node->hkey & hash->mask; + hptr = hash->table[chain]; + + if (hptr == node) { + hash->table[chain] = node->next; + } else { + while (hptr->next != node) + hptr = hptr->next; + hptr->next = node->next; + } + + hash->nodecount--; + assert (kl_hash_verify(hash)); + node->next = NULL; + + return node; +} + +/* + * Like hash_delete_free but based on hash_scan_delete. + */ + +void kl_hash_scan_delfree(hash_t *hash, hnode_t *node) +{ + kl_hash_scan_delete(hash, node); + hash->freenode(node, hash->context); +} + +/* + * Verify whether the given object is a valid hash table. This means + * Notes: + * 1. If the hash table is dynamic, verify whether the high and + * low expansion/shrinkage thresholds are powers of two. + * 2. Count all nodes in the table, and test each hash value + * to see whether it is correct for the node's chain. + */ + +int kl_hash_verify(hash_t *hash) +{ + hashcount_t count = 0; + hash_val_t chain; + hnode_t *hptr; + + if (hash->dynamic) { /* 1 */ + if (hash->lowmark >= hash->highmark) + return 0; + if (!is_power_of_two(hash->highmark)) + return 0; + if (!is_power_of_two(hash->lowmark)) + return 0; + } + + for (chain = 0; chain < hash->nchains; chain++) { /* 2 */ + for (hptr = hash->table[chain]; hptr != 0; hptr = hptr->next) { + if ((hptr->hkey & hash->mask) != chain) + return 0; + count++; + } + } + + if (count != hash->nodecount) + return 0; + + return 1; +} + +/* + * Test whether the hash table is full and return 1 if this is true, + * 0 if it is false. + */ + +#undef kl_hash_isfull +int kl_hash_isfull(hash_t *hash) +{ + return hash->nodecount == hash->maxcount; +} + +/* + * Test whether the hash table is empty and return 1 if this is true, + * 0 if it is false. + */ + +#undef kl_hash_isempty +int kl_hash_isempty(hash_t *hash) +{ + return hash->nodecount == 0; +} + +static hnode_t *kl_hnode_alloc(void *context) +{ + return malloc(sizeof *kl_hnode_alloc(NULL)); +} + +static void kl_hnode_free(hnode_t *node, void *context) +{ + free(node); +} + + +/* + * Create a hash table node dynamically and assign it the given data. + */ + +hnode_t *kl_hnode_create(void *data) +{ + hnode_t *node = malloc(sizeof *node); + if (node) { + node->data = data; + node->next = NULL; + } + return node; +} + +/* + * Initialize a client-supplied node + */ + +hnode_t *kl_hnode_init(hnode_t *hnode, void *data) +{ + hnode->data = data; + hnode->next = NULL; + return hnode; +} + +/* + * Destroy a dynamically allocated node. + */ + +void kl_hnode_destroy(hnode_t *hnode) +{ + free(hnode); +} + +#undef kl_hnode_put +void kl_hnode_put(hnode_t *node, void *data) +{ + node->data = data; +} + +#undef kl_hnode_get +void *kl_hnode_get(hnode_t *node) +{ + return node->data; +} + +#undef kl_hnode_getkey +const void *kl_hnode_getkey(hnode_t *node) +{ + return node->key; +} + +#undef kl_hash_count +hashcount_t kl_hash_count(hash_t *hash) +{ + return hash->nodecount; +} + +#undef kl_hash_size +hashcount_t kl_hash_size(hash_t *hash) +{ + return hash->nchains; +} + +static hash_val_t hash_fun_default(const void *key) +{ + static unsigned long randbox[] = { + 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U, + 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU, + 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU, + 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU, + }; + + const unsigned char *str = key; + hash_val_t acc = 0; + + while (*str) { + acc ^= randbox[(*str + acc) & 0xf]; + acc = (acc << 1) | (acc >> 31); + acc &= 0xffffffffU; + acc ^= randbox[((*str++ >> 4) + acc) & 0xf]; + acc = (acc << 2) | (acc >> 30); + acc &= 0xffffffffU; + } + return acc; +} + +static int hash_comp_default(const void *key1, const void *key2) +{ + return strcmp(key1, key2); +} diff --git a/c_src/couchdb-khash/hash.h b/c_src/couchdb-khash/hash.h new file mode 100644 index 0000000..0c75ed0 --- /dev/null +++ b/c_src/couchdb-khash/hash.h @@ -0,0 +1,240 @@ +/* + * Hash Table Data Type + * Copyright (C) 1997 Kaz Kylheku + * + * Free Software License: + * + * All rights are reserved by the author, with the following exceptions: + * Permission is granted to freely reproduce and distribute this software, + * possibly in exchange for a fee, provided that this copyright notice appears + * intact. Permission is also granted to adapt this software to produce + * derivative works, as long as the modified versions carry this copyright + * notice and additional notices stating that the work has been modified. + * This source code may be translated into executable form and incorporated + * into proprietary software; there is no requirement for such software to + * contain a copyright notice related to this source. + * + * $Id: hash.h,v 1.22.2.7 2000/11/13 01:36:45 kaz Exp $ + * $Name: kazlib_1_20 $ + */ + +#ifndef HASH_H +#define HASH_H + +#include +#ifdef KAZLIB_SIDEEFFECT_DEBUG +#include "sfx.h" +#endif + +/* + * Blurb for inclusion into C++ translation units + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned long hashcount_t; +#define HASHCOUNT_T_MAX ULONG_MAX + +typedef unsigned long hash_val_t; +#define HASH_VAL_T_MAX ULONG_MAX + +extern int hash_val_t_bit; + +#ifndef HASH_VAL_T_BIT +#define HASH_VAL_T_BIT ((int) hash_val_t_bit) +#endif + +/* + * Hash chain node structure. + * Notes: + * 1. This preprocessing directive is for debugging purposes. The effect is + * that if the preprocessor symbol KAZLIB_OPAQUE_DEBUG is defined prior to the + * inclusion of this header, then the structure shall be declared as having + * the single member int __OPAQUE__. This way, any attempts by the + * client code to violate the principles of information hiding (by accessing + * the structure directly) can be diagnosed at translation time. However, + * note the resulting compiled unit is not suitable for linking. + * 2. This is a pointer to the next node in the chain. In the last node of a + * chain, this pointer is null. + * 3. The key is a pointer to some user supplied data that contains a unique + * identifier for each hash node in a given table. The interpretation of + * the data is up to the user. When creating or initializing a hash table, + * the user must supply a pointer to a function for comparing two keys, + * and a pointer to a function for hashing a key into a numeric value. + * 4. The value is a user-supplied pointer to void which may refer to + * any data object. It is not interpreted in any way by the hashing + * module. + * 5. The hashed key is stored in each node so that we don't have to rehash + * each key when the table must grow or shrink. + */ + +typedef struct hnode_t { + #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) /* 1 */ + struct hnode_t *hash_next; /* 2 */ + const void *hash_key; /* 3 */ + void *hash_data; /* 4 */ + hash_val_t hash_hkey; /* 5 */ + #else + int hash_dummy; + #endif +} hnode_t; + +/* + * The comparison function pointer type. A comparison function takes two keys + * and produces a value of -1 if the left key is less than the right key, a + * value of 0 if the keys are equal, and a value of 1 if the left key is + * greater than the right key. + */ + +typedef int (*hash_comp_t)(const void *, const void *); + +/* + * The hashing function performs some computation on a key and produces an + * integral value of type hash_val_t based on that key. For best results, the + * function should have a good randomness properties in *all* significant bits + * over the set of keys that are being inserted into a given hash table. In + * particular, the most significant bits of hash_val_t are most significant to + * the hash module. Only as the hash table expands are less significant bits + * examined. Thus a function that has good distribution in its upper bits but + * not lower is preferrable to one that has poor distribution in the upper bits + * but not the lower ones. + */ + +typedef hash_val_t (*hash_fun_t)(const void *); + +/* + * allocator functions + */ + +typedef hnode_t *(*hnode_alloc_t)(void *); +typedef void (*hnode_free_t)(hnode_t *, void *); + +/* + * This is the hash table control structure. It keeps track of information + * about a hash table, as well as the hash table itself. + * Notes: + * 1. Pointer to the hash table proper. The table is an array of pointers to + * hash nodes (of type hnode_t). If the table is empty, every element of + * this table is a null pointer. A non-null entry points to the first + * element of a chain of nodes. + * 2. This member keeps track of the size of the hash table---that is, the + * number of chain pointers. + * 3. The count member maintains the number of elements that are presently + * in the hash table. + * 4. The maximum count is the greatest number of nodes that can populate this + * table. If the table contains this many nodes, no more can be inserted, + * and the hash_isfull() function returns true. + * 5. The high mark is a population threshold, measured as a number of nodes, + * which, if exceeded, will trigger a table expansion. Only dynamic hash + * tables are subject to this expansion. + * 6. The low mark is a minimum population threshold, measured as a number of + * nodes. If the table population drops below this value, a table shrinkage + * will occur. Only dynamic tables are subject to this reduction. No table + * will shrink beneath a certain absolute minimum number of nodes. + * 7. This is the a pointer to the hash table's comparison function. The + * function is set once at initialization or creation time. + * 8. Pointer to the table's hashing function, set once at creation or + * initialization time. + * 9. The current hash table mask. If the size of the hash table is 2^N, + * this value has its low N bits set to 1, and the others clear. It is used + * to select bits from the result of the hashing function to compute an + * index into the table. + * 10. A flag which indicates whether the table is to be dynamically resized. It + * is set to 1 in dynamically allocated tables, 0 in tables that are + * statically allocated. + */ + +typedef struct hash_t { + #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) + struct hnode_t **hash_table; /* 1 */ + hashcount_t hash_nchains; /* 2 */ + hashcount_t hash_nodecount; /* 3 */ + hashcount_t hash_maxcount; /* 4 */ + hashcount_t hash_highmark; /* 5 */ + hashcount_t hash_lowmark; /* 6 */ + hash_comp_t hash_compare; /* 7 */ + hash_fun_t hash_function; /* 8 */ + hnode_alloc_t hash_allocnode; + hnode_free_t hash_freenode; + void *hash_context; + hash_val_t hash_mask; /* 9 */ + int hash_dynamic; /* 10 */ + #else + int hash_dummy; + #endif +} hash_t; + +/* + * Hash scanner structure, used for traversals of the data structure. + * Notes: + * 1. Pointer to the hash table that is being traversed. + * 2. Reference to the current chain in the table being traversed (the chain + * that contains the next node that shall be retrieved). + * 3. Pointer to the node that will be retrieved by the subsequent call to + * hash_scan_next(). + */ + +typedef struct hscan_t { + #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) + hash_t *hash_table; /* 1 */ + hash_val_t hash_chain; /* 2 */ + hnode_t *hash_next; /* 3 */ + #else + int hash_dummy; + #endif +} hscan_t; + +extern hash_t *kl_hash_create(hashcount_t, hash_comp_t, hash_fun_t); +extern void kl_hash_set_allocator(hash_t *, hnode_alloc_t, hnode_free_t, void *); +extern void kl_hash_destroy(hash_t *); +extern void kl_hash_free_nodes(hash_t *); +extern void kl_hash_free(hash_t *); +extern hash_t *kl_hash_init(hash_t *, hashcount_t, hash_comp_t, + hash_fun_t, hnode_t **, hashcount_t); +extern void kl_hash_insert(hash_t *, hnode_t *, const void *); +extern hnode_t *kl_hash_lookup(hash_t *, const void *); +extern hnode_t *kl_hash_delete(hash_t *, hnode_t *); +extern int kl_hash_alloc_insert(hash_t *, const void *, void *); +extern void kl_hash_delete_free(hash_t *, hnode_t *); + +extern void kl_hnode_put(hnode_t *, void *); +extern void *kl_hnode_get(hnode_t *); +extern const void *kl_hnode_getkey(hnode_t *); +extern hashcount_t kl_hash_count(hash_t *); +extern hashcount_t kl_hash_size(hash_t *); + +extern int kl_hash_isfull(hash_t *); +extern int kl_hash_isempty(hash_t *); + +extern void kl_hash_scan_begin(hscan_t *, hash_t *); +extern hnode_t *kl_hash_scan_next(hscan_t *); +extern hnode_t *kl_hash_scan_delete(hash_t *, hnode_t *); +extern void kl_hash_scan_delfree(hash_t *, hnode_t *); + +extern int kl_hash_verify(hash_t *); + +extern hnode_t *kl_hnode_create(void *); +extern hnode_t *kl_hnode_init(hnode_t *, void *); +extern void kl_hnode_destroy(hnode_t *); + +#if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) +#ifdef KAZLIB_SIDEEFFECT_DEBUG +#define kl_hash_isfull(H) (SFX_CHECK(H)->hash_nodecount == (H)->hash_maxcount) +#else +#define kl_hash_isfull(H) ((H)->hash_nodecount == (H)->hash_maxcount) +#endif +#define kl_hash_isempty(H) ((H)->hash_nodecount == 0) +#define kl_hash_count(H) ((H)->hash_nodecount) +#define kl_hash_size(H) ((H)->hash_nchains) +#define kl_hnode_get(N) ((N)->hash_data) +#define kl_hnode_getkey(N) ((N)->hash_key) +#define kl_hnode_put(N, V) ((N)->hash_data = (V)) +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c_src/couchdb-khash/khash.c b/c_src/couchdb-khash/khash.c new file mode 100644 index 0000000..b15a18e --- /dev/null +++ b/c_src/couchdb-khash/khash.c @@ -0,0 +1,658 @@ +// This file is part of khash released under the MIT license. +// See the LICENSE file for more information. +// Copyright 2013 Cloudant, Inc + +#include +#include +#include + +#include "erl_nif.h" +#include "hash.h" + +#ifdef _WIN32 +#define INLINE __inline +#else +#define INLINE inline +#endif + +#define KHASH_VERSION 0 + + +typedef struct +{ + ERL_NIF_TERM atom_ok; + ERL_NIF_TERM atom_error; + ERL_NIF_TERM atom_value; + ERL_NIF_TERM atom_not_found; + ERL_NIF_TERM atom_end_of_table; + ERL_NIF_TERM atom_expired_iterator; + ErlNifResourceType* res_hash; + ErlNifResourceType* res_iter; +} khash_priv; + + +typedef struct +{ + unsigned int hval; + ErlNifEnv* env; + ERL_NIF_TERM key; + ERL_NIF_TERM val; +} khnode_t; + + +typedef struct +{ + int version; + unsigned int gen; + hash_t* h; + ErlNifPid p; +} khash_t; + + +typedef struct +{ + int version; + unsigned int gen; + khash_t* khash; + hscan_t scan; +} khash_iter_t; + + +static INLINE ERL_NIF_TERM +make_atom(ErlNifEnv* env, const char* name) +{ + ERL_NIF_TERM ret; + if(enif_make_existing_atom(env, name, &ret, ERL_NIF_LATIN1)) { + return ret; + } + return enif_make_atom(env, name); +} + + +static INLINE ERL_NIF_TERM +make_ok(ErlNifEnv* env, khash_priv* priv, ERL_NIF_TERM value) +{ + return enif_make_tuple2(env, priv->atom_ok, value); +} + + +static INLINE ERL_NIF_TERM +make_error(ErlNifEnv* env, khash_priv* priv, ERL_NIF_TERM reason) +{ + return enif_make_tuple2(env, priv->atom_error, reason); +} + + +static INLINE int +check_pid(ErlNifEnv* env, khash_t* khash) +{ + ErlNifPid pid; + enif_self(env, &pid); + + if(enif_compare(pid.pid, khash->p.pid) == 0) { + return 1; + } + + return 0; +} + + +hnode_t* +khnode_alloc(void* ctx) +{ + hnode_t* ret = (hnode_t*) enif_alloc(sizeof(hnode_t)); + khnode_t* node = (khnode_t*) enif_alloc(sizeof(khnode_t)); + + memset(ret, '\0', sizeof(hnode_t)); + memset(node, '\0', sizeof(khnode_t)); + + node->env = enif_alloc_env(); + ret->hash_key = node; + + return ret; +} + + +void +khnode_free(hnode_t* obj, void* ctx) +{ + khnode_t* node = (khnode_t*) kl_hnode_getkey(obj); + enif_free_env(node->env); + enif_free(node); + enif_free(obj); + return; +} + + +int +khash_cmp_fun(const void* l, const void* r) +{ + khnode_t* left = (khnode_t*) l; + khnode_t* right = (khnode_t*) r; + int cmp = enif_compare(left->key, right->key); + + if(cmp < 0) { + return -1; + } else if(cmp == 0) { + return 0; + } else { + return 1; + } +} + + +hash_val_t +khash_hash_fun(const void* obj) +{ + khnode_t* node = (khnode_t*) obj; + return (hash_val_t) node->hval; +} + + +static INLINE khash_t* +khash_create_int(ErlNifEnv* env, khash_priv* priv, ERL_NIF_TERM opts) +{ + khash_t* khash = NULL; + + assert(priv != NULL && "missing private data member"); + + khash = (khash_t*) enif_alloc_resource(priv->res_hash, sizeof(khash_t)); + memset(khash, '\0', sizeof(khash_t)); + khash->version = KHASH_VERSION; + khash->gen = 0; + + khash->h = kl_hash_create(HASHCOUNT_T_MAX, khash_cmp_fun, khash_hash_fun); + + if(khash->h == NULL ) { + enif_release_resource(khash); + return NULL; + } + + kl_hash_set_allocator(khash->h, khnode_alloc, khnode_free, NULL); + enif_self(env, &(khash->p)); + + return khash; +} + + +static ERL_NIF_TERM +khash_new(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + khash_priv* priv = enif_priv_data(env); + khash_t* khash; + ERL_NIF_TERM ret; + + if(argc != 1) { + return enif_make_badarg(env); + } + + khash = khash_create_int(env, priv, argv[0]); + if(khash == NULL) { + return enif_make_badarg(env); + } + + ret = enif_make_resource(env, khash); + enif_release_resource(khash); + + return make_ok(env, priv, ret); +} + + +static void +khash_free(ErlNifEnv* env, void* obj) +{ + khash_t* khash = (khash_t*) obj; + + if(khash->h != NULL) { + kl_hash_free_nodes(khash->h); + kl_hash_destroy(khash->h); + } + + return; +} + + +static ERL_NIF_TERM +khash_to_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + khash_priv* priv = (khash_priv*) enif_priv_data(env); + ERL_NIF_TERM ret = enif_make_list(env, 0); + khash_t* khash = NULL; + void* res = NULL; + hscan_t scan; + hnode_t* entry; + khnode_t* node; + ERL_NIF_TERM key; + ERL_NIF_TERM val; + ERL_NIF_TERM tuple; + + if(argc != 1) { + return enif_make_badarg(env); + } + + if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) { + return enif_make_badarg(env); + } + + khash = (khash_t*) res; + + if(!check_pid(env, khash)) { + return enif_make_badarg(env); + } + + kl_hash_scan_begin(&scan, khash->h); + + while((entry = kl_hash_scan_next(&scan)) != NULL) { + node = (khnode_t*) kl_hnode_getkey(entry); + key = enif_make_copy(env, node->key); + val = enif_make_copy(env, node->val); + tuple = enif_make_tuple2(env, key, val); + ret = enif_make_list_cell(env, tuple, ret); + } + + return ret; +} + + +static ERL_NIF_TERM +khash_clear(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + khash_priv* priv = enif_priv_data(env); + khash_t* khash = NULL; + void* res = NULL; + + if(argc != 1) { + return enif_make_badarg(env); + } + + if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) { + return enif_make_badarg(env); + } + + khash = (khash_t*) res; + + if(!check_pid(env, khash)) { + return enif_make_badarg(env); + } + + kl_hash_free_nodes(khash->h); + + khash->gen += 1; + + return priv->atom_ok; +} + + +static INLINE hnode_t* +khash_lookup_int(ErlNifEnv* env, uint32_t hv, ERL_NIF_TERM key, khash_t* khash) +{ + khnode_t node; + node.hval = hv; + node.env = env; + node.key = key; + return kl_hash_lookup(khash->h, &node); +} + + +static ERL_NIF_TERM +khash_lookup(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + khash_priv* priv = enif_priv_data(env); + khash_t* khash = NULL; + void* res = NULL; + uint32_t hval; + hnode_t* entry; + khnode_t* node; + ERL_NIF_TERM ret; + + if(argc != 3) { + return enif_make_badarg(env); + } + + if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) { + return enif_make_badarg(env); + } + + khash = (khash_t*) res; + + if(!check_pid(env, khash)) { + return enif_make_badarg(env); + } + + if(!enif_get_uint(env, argv[1], &hval)) { + return enif_make_badarg(env); + } + + entry = khash_lookup_int(env, hval, argv[2], khash); + if(entry == NULL) { + ret = priv->atom_not_found; + } else { + node = (khnode_t*) kl_hnode_getkey(entry); + ret = enif_make_copy(env, node->val); + ret = enif_make_tuple2(env, priv->atom_value, ret); + } + + return ret; +} + + +static ERL_NIF_TERM +khash_get(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + khash_priv* priv = enif_priv_data(env); + khash_t* khash = NULL; + void* res = NULL; + uint32_t hval; + hnode_t* entry; + khnode_t* node; + ERL_NIF_TERM ret; + + if(argc != 4) { + return enif_make_badarg(env); + } + + if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) { + return enif_make_badarg(env); + } + + khash = (khash_t*) res; + + if(!check_pid(env, khash)) { + return enif_make_badarg(env); + } + + if(!enif_get_uint(env, argv[1], &hval)) { + return enif_make_badarg(env); + } + + entry = khash_lookup_int(env, hval, argv[2], khash); + if(entry == NULL) { + ret = argv[3]; + } else { + node = (khnode_t*) kl_hnode_getkey(entry); + ret = enif_make_copy(env, node->val); + } + + return ret; +} + + +static ERL_NIF_TERM +khash_put(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + khash_priv* priv = enif_priv_data(env); + khash_t* khash = NULL; + void* res = NULL; + uint32_t hval; + hnode_t* entry; + khnode_t* node; + + if(argc != 4) { + return enif_make_badarg(env); + } + + if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) { + return enif_make_badarg(env); + } + + khash = (khash_t*) res; + + if(!check_pid(env, khash)) { + return enif_make_badarg(env); + } + + if(!enif_get_uint(env, argv[1], &hval)) { + return enif_make_badarg(env); + } + + entry = khash_lookup_int(env, hval, argv[2], khash); + if(entry == NULL) { + entry = khnode_alloc(NULL); + node = (khnode_t*) kl_hnode_getkey(entry); + node->hval = hval; + node->key = enif_make_copy(node->env, argv[2]); + node->val = enif_make_copy(node->env, argv[3]); + kl_hash_insert(khash->h, entry, node); + } else { + node = (khnode_t*) kl_hnode_getkey(entry); + enif_clear_env(node->env); + node->key = enif_make_copy(node->env, argv[2]); + node->val = enif_make_copy(node->env, argv[3]); + } + + khash->gen += 1; + + return priv->atom_ok; +} + + +static ERL_NIF_TERM +khash_del(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + khash_priv* priv = enif_priv_data(env); + khash_t* khash = NULL; + void* res = NULL; + uint32_t hval; + hnode_t* entry; + ERL_NIF_TERM ret; + + if(argc != 3) { + return enif_make_badarg(env); + } + + if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) { + return enif_make_badarg(env); + } + + khash = (khash_t*) res; + + if(!check_pid(env, khash)) { + return enif_make_badarg(env); + } + + if(!enif_get_uint(env, argv[1], &hval)) { + return enif_make_badarg(env); + } + + entry = khash_lookup_int(env, hval, argv[2], khash); + if(entry == NULL) { + ret = priv->atom_not_found; + } else { + kl_hash_delete_free(khash->h, entry); + ret = priv->atom_ok; + } + + khash->gen += 1; + + return ret; +} + + +static ERL_NIF_TERM +khash_size(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + khash_priv* priv = enif_priv_data(env); + khash_t* khash; + + if(argc != 1) { + return enif_make_badarg(env); + } + + if(!enif_get_resource(env, argv[0], priv->res_hash, (void*) &khash)) { + return enif_make_badarg(env); + } + + if(!check_pid(env, khash)) { + return enif_make_badarg(env); + } + + return enif_make_uint64(env, kl_hash_count(khash->h)); +} + + +static ERL_NIF_TERM +khash_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + khash_priv* priv = enif_priv_data(env); + khash_t* khash = NULL; + void* res = NULL; + khash_iter_t* iter; + ERL_NIF_TERM ret; + + if(argc != 1) { + return enif_make_badarg(env); + } + + if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) { + return enif_make_badarg(env); + } + + khash = (khash_t*) res; + + if(!check_pid(env, khash)) { + return enif_make_badarg(env); + } + + iter = (khash_iter_t*) enif_alloc_resource( + priv->res_iter, sizeof(khash_iter_t)); + memset(iter, '\0', sizeof(khash_iter_t)); + iter->version = KHASH_VERSION; + iter->gen = khash->gen; + iter->khash = khash; + kl_hash_scan_begin(&(iter->scan), iter->khash->h); + + // The iterator needs to guarantee that the khash + // remains alive for the life of the iterator. + enif_keep_resource(khash); + + ret = enif_make_resource(env, iter); + enif_release_resource(iter); + + return make_ok(env, priv, ret); +} + + +static void +khash_iter_free(ErlNifEnv* env, void* obj) +{ + khash_iter_t* iter = (khash_iter_t*) obj; + enif_release_resource(iter->khash); +} + + +static ERL_NIF_TERM +khash_iter_next(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + khash_priv* priv = enif_priv_data(env); + khash_iter_t* iter = NULL; + void* res = NULL; + hnode_t* entry; + khnode_t* node; + ERL_NIF_TERM key; + ERL_NIF_TERM val; + + if(argc != 1) { + return enif_make_badarg(env); + } + + if(!enif_get_resource(env, argv[0], priv->res_iter, &res)) { + return enif_make_badarg(env); + } + + iter = (khash_iter_t*) res; + + if(!check_pid(env, iter->khash)) { + return enif_make_badarg(env); + } + + if(iter->gen != iter->khash->gen) { + return make_error(env, priv, priv->atom_expired_iterator); + } + + entry = kl_hash_scan_next(&(iter->scan)); + if(entry == NULL) { + return priv->atom_end_of_table; + } + + node = (khnode_t*) kl_hnode_getkey(entry); + key = enif_make_copy(env, node->key); + val = enif_make_copy(env, node->val); + return enif_make_tuple2(env, key, val); +} + + +static int +load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info) +{ + int flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER; + ErlNifResourceType* res; + + khash_priv* new_priv = (khash_priv*) enif_alloc(sizeof(khash_priv)); + if(new_priv == NULL) { + return 1; + } + + res = enif_open_resource_type( + env, NULL, "khash", khash_free, flags, NULL); + if(res == NULL) { + return 1; + } + new_priv->res_hash = res; + + res = enif_open_resource_type( + env, NULL, "khash_iter", khash_iter_free, flags, NULL); + if(res == NULL) { + return 1; + } + new_priv->res_iter = res; + + new_priv->atom_ok = make_atom(env, "ok"); + new_priv->atom_error = make_atom(env, "error"); + new_priv->atom_value = make_atom(env, "value"); + new_priv->atom_not_found = make_atom(env, "not_found"); + new_priv->atom_end_of_table = make_atom(env, "end_of_table"); + new_priv->atom_expired_iterator = make_atom(env, "expired_iterator"); + + *priv = (void*) new_priv; + + return 0; +} + + +static int +reload(ErlNifEnv* env, void** priv, ERL_NIF_TERM info) +{ + return 0; +} + + +static int +upgrade(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM info) +{ + return load(env, priv, info); +} + + +static void +unload(ErlNifEnv* env, void* priv) +{ + enif_free(priv); + return; +} + + +static ErlNifFunc funcs[] = { + {"new", 1, khash_new}, + {"to_list", 1, khash_to_list}, + {"clear", 1, khash_clear}, + {"lookup_int", 3, khash_lookup}, + {"get_int", 4, khash_get}, + {"put_int", 4, khash_put}, + {"del_int", 3, khash_del}, + {"size", 1, khash_size}, + {"iter", 1, khash_iter}, + {"iter_next", 1, khash_iter_next} +}; + + +ERL_NIF_INIT(khash, funcs, &load, &reload, &upgrade, &unload); diff --git a/c_src/couchdb-khash/rebar.config b/c_src/couchdb-khash/rebar.config new file mode 100644 index 0000000..3b3f275 --- /dev/null +++ b/c_src/couchdb-khash/rebar.config @@ -0,0 +1,12 @@ +{port_specs, [ + {"../../priv/khash.so", ["*.c"]} +]}. + +{port_env, [ + % Development compilation + % {".*", "CFLAGS", "$CFLAGS -g -Wall -Werror -fPIC"} + + % Production compilation + {"(linux|solaris|darwin|freebsd)", "CFLAGS", "$CFLAGS -Wall -Werror -DNDEBUG -O3"}, + {"win32", "CFLAGS", "$CFLAGS /O2 /DNDEBUG /Wall"} +]}. diff --git a/c_src/couchdb-khash2/hash.c b/c_src/couchdb-khash2/hash.c new file mode 100644 index 0000000..e3da0da --- /dev/null +++ b/c_src/couchdb-khash2/hash.c @@ -0,0 +1,843 @@ +/* + * Hash Table Data Type + * Copyright (C) 1997 Kaz Kylheku + * + * Free Software License: + * + * All rights are reserved by the author, with the following exceptions: + * Permission is granted to freely reproduce and distribute this software, + * possibly in exchange for a fee, provided that this copyright notice appears + * intact. Permission is also granted to adapt this software to produce + * derivative works, as long as the modified versions carry this copyright + * notice and additional notices stating that the work has been modified. + * This source code may be translated into executable form and incorporated + * into proprietary software; there is no requirement for such software to + * contain a copyright notice related to this source. + * + * $Id: hash.c,v 1.36.2.11 2000/11/13 01:36:45 kaz Exp $ + * $Name: kazlib_1_20 $ + */ + +#include +#include +#include +#include +#define HASH_IMPLEMENTATION +#include "hash.h" + +#ifdef KAZLIB_RCSID +static const char rcsid[] = "$Id: hash.c,v 1.36.2.11 2000/11/13 01:36:45 kaz Exp $"; +#endif + +#define INIT_BITS 6 +#define INIT_SIZE (1UL << (INIT_BITS)) /* must be power of two */ +#define INIT_MASK ((INIT_SIZE) - 1) + +#define next hash_next +#define key hash_key +#define data hash_data +#define hkey hash_hkey + +#define table hash_table +#define nchains hash_nchains +#define nodecount hash_nodecount +#define maxcount hash_maxcount +#define highmark hash_highmark +#define lowmark hash_lowmark +#define compare hash_compare +#define function hash_function +#define allocnode hash_allocnode +#define freenode hash_freenode +#define context hash_context +#define mask hash_mask +#define dynamic hash_dynamic + +#define table hash_table +#define chain hash_chain + +static hnode_t *kl_hnode_alloc(void *context); +static void kl_hnode_free(hnode_t *node, void *context); +static hash_val_t hash_fun_default(const void *key); +static int hash_comp_default(const void *key1, const void *key2); + +int hash_val_t_bit; + +/* + * Compute the number of bits in the hash_val_t type. We know that hash_val_t + * is an unsigned integral type. Thus the highest value it can hold is a + * Mersenne number (power of two, less one). We initialize a hash_val_t + * object with this value and then shift bits out one by one while counting. + * Notes: + * 1. HASH_VAL_T_MAX is a Mersenne number---one that is one less than a power + * of two. This means that its binary representation consists of all one + * bits, and hence ``val'' is initialized to all one bits. + * 2. While bits remain in val, we increment the bit count and shift it to the + * right, replacing the topmost bit by zero. + */ + +static void compute_bits(void) +{ + hash_val_t val = HASH_VAL_T_MAX; /* 1 */ + int bits = 0; + + while (val) { /* 2 */ + bits++; + val >>= 1; + } + + hash_val_t_bit = bits; +} + +/* + * Verify whether the given argument is a power of two. + */ + +static int is_power_of_two(hash_val_t arg) +{ + if (arg == 0) + return 0; + while ((arg & 1) == 0) + arg >>= 1; + return (arg == 1); +} + +/* + * Compute a shift amount from a given table size + */ + +static hash_val_t compute_mask(hashcount_t size) +{ + assert (is_power_of_two(size)); + assert (size >= 2); + + return size - 1; +} + +/* + * Initialize the table of pointers to null. + */ + +static void clear_table(hash_t *hash) +{ + hash_val_t i; + + for (i = 0; i < hash->nchains; i++) + hash->table[i] = NULL; +} + +/* + * Double the size of a dynamic table. This works as follows. Each chain splits + * into two adjacent chains. The shift amount increases by one, exposing an + * additional bit of each hashed key. For each node in the original chain, the + * value of this newly exposed bit will decide which of the two new chains will + * receive the node: if the bit is 1, the chain with the higher index will have + * the node, otherwise the lower chain will receive the node. In this manner, + * the hash table will continue to function exactly as before without having to + * rehash any of the keys. + * Notes: + * 1. Overflow check. + * 2. The new number of chains is twice the old number of chains. + * 3. The new mask is one bit wider than the previous, revealing a + * new bit in all hashed keys. + * 4. Allocate a new table of chain pointers that is twice as large as the + * previous one. + * 5. If the reallocation was successful, we perform the rest of the growth + * algorithm, otherwise we do nothing. + * 6. The exposed_bit variable holds a mask with which each hashed key can be + * AND-ed to test the value of its newly exposed bit. + * 7. Now loop over each chain in the table and sort its nodes into two + * chains based on the value of each node's newly exposed hash bit. + * 8. The low chain replaces the current chain. The high chain goes + * into the corresponding sister chain in the upper half of the table. + * 9. We have finished dealing with the chains and nodes. We now update + * the various bookeeping fields of the hash structure. + */ + +static void grow_table(hash_t *hash) +{ + hnode_t **newtable; + + assert (2 * hash->nchains > hash->nchains); /* 1 */ + + newtable = realloc(hash->table, + sizeof *newtable * hash->nchains * 2); /* 4 */ + + if (newtable) { /* 5 */ + hash_val_t mask = (hash->mask << 1) | 1; /* 3 */ + hash_val_t exposed_bit = mask ^ hash->mask; /* 6 */ + hash_val_t chain; + + assert (mask != hash->mask); + + for (chain = 0; chain < hash->nchains; chain++) { /* 7 */ + hnode_t *low_chain = 0, *high_chain = 0, *hptr, *next; + + for (hptr = newtable[chain]; hptr != 0; hptr = next) { + next = hptr->next; + + if (hptr->hkey & exposed_bit) { + hptr->next = high_chain; + high_chain = hptr; + } else { + hptr->next = low_chain; + low_chain = hptr; + } + } + + newtable[chain] = low_chain; /* 8 */ + newtable[chain + hash->nchains] = high_chain; + } + + hash->table = newtable; /* 9 */ + hash->mask = mask; + hash->nchains *= 2; + hash->lowmark *= 2; + hash->highmark *= 2; + } + assert (kl_hash_verify(hash)); +} + +/* + * Cut a table size in half. This is done by folding together adjacent chains + * and populating the lower half of the table with these chains. The chains are + * simply spliced together. Once this is done, the whole table is reallocated + * to a smaller object. + * Notes: + * 1. It is illegal to have a hash table with one slot. This would mean that + * hash->shift is equal to hash_val_t_bit, an illegal shift value. + * Also, other things could go wrong, such as hash->lowmark becoming zero. + * 2. Looping over each pair of sister chains, the low_chain is set to + * point to the head node of the chain in the lower half of the table, + * and high_chain points to the head node of the sister in the upper half. + * 3. The intent here is to compute a pointer to the last node of the + * lower chain into the low_tail variable. If this chain is empty, + * low_tail ends up with a null value. + * 4. If the lower chain is not empty, we simply tack the upper chain onto it. + * If the upper chain is a null pointer, nothing happens. + * 5. Otherwise if the lower chain is empty but the upper one is not, + * If the low chain is empty, but the high chain is not, then the + * high chain is simply transferred to the lower half of the table. + * 6. Otherwise if both chains are empty, there is nothing to do. + * 7. All the chain pointers are in the lower half of the table now, so + * we reallocate it to a smaller object. This, of course, invalidates + * all pointer-to-pointers which reference into the table from the + * first node of each chain. + * 8. Though it's unlikely, the reallocation may fail. In this case we + * pretend that the table _was_ reallocated to a smaller object. + * 9. Finally, update the various table parameters to reflect the new size. + */ + +static void shrink_table(hash_t *hash) +{ + hash_val_t chain, nchains; + hnode_t **newtable, *low_tail, *low_chain, *high_chain; + + assert (hash->nchains >= 2); /* 1 */ + nchains = hash->nchains / 2; + + for (chain = 0; chain < nchains; chain++) { + low_chain = hash->table[chain]; /* 2 */ + high_chain = hash->table[chain + nchains]; + for (low_tail = low_chain; low_tail && low_tail->next; low_tail = low_tail->next) + ; /* 3 */ + if (low_chain != 0) /* 4 */ + low_tail->next = high_chain; + else if (high_chain != 0) /* 5 */ + hash->table[chain] = high_chain; + else + assert (hash->table[chain] == NULL); /* 6 */ + } + newtable = realloc(hash->table, + sizeof *newtable * nchains); /* 7 */ + if (newtable) /* 8 */ + hash->table = newtable; + hash->mask >>= 1; /* 9 */ + hash->nchains = nchains; + hash->lowmark /= 2; + hash->highmark /= 2; + assert (kl_hash_verify(hash)); +} + + +/* + * Create a dynamic hash table. Both the hash table structure and the table + * itself are dynamically allocated. Furthermore, the table is extendible in + * that it will automatically grow as its load factor increases beyond a + * certain threshold. + * Notes: + * 1. If the number of bits in the hash_val_t type has not been computed yet, + * we do so here, because this is likely to be the first function that the + * user calls. + * 2. Allocate a hash table control structure. + * 3. If a hash table control structure is successfully allocated, we + * proceed to initialize it. Otherwise we return a null pointer. + * 4. We try to allocate the table of hash chains. + * 5. If we were able to allocate the hash chain table, we can finish + * initializing the hash structure and the table. Otherwise, we must + * backtrack by freeing the hash structure. + * 6. INIT_SIZE should be a power of two. The high and low marks are always set + * to be twice the table size and half the table size respectively. When the + * number of nodes in the table grows beyond the high size (beyond load + * factor 2), it will double in size to cut the load factor down to about + * about 1. If the table shrinks down to or beneath load factor 0.5, + * it will shrink, bringing the load up to about 1. However, the table + * will never shrink beneath INIT_SIZE even if it's emptied. + * 7. This indicates that the table is dynamically allocated and dynamically + * resized on the fly. A table that has this value set to zero is + * assumed to be statically allocated and will not be resized. + * 8. The table of chains must be properly reset to all null pointers. + */ + +hash_t *kl_hash_create(hashcount_t maxcount, hash_comp_t compfun, + hash_fun_t hashfun) +{ + hash_t *hash; + + if (hash_val_t_bit == 0) /* 1 */ + compute_bits(); + + hash = malloc(sizeof *hash); /* 2 */ + + if (hash) { /* 3 */ + hash->table = malloc(sizeof *hash->table * INIT_SIZE); /* 4 */ + if (hash->table) { /* 5 */ + hash->nchains = INIT_SIZE; /* 6 */ + hash->highmark = INIT_SIZE * 2; + hash->lowmark = INIT_SIZE / 2; + hash->nodecount = 0; + hash->maxcount = maxcount; + hash->compare = compfun ? compfun : hash_comp_default; + hash->function = hashfun ? hashfun : hash_fun_default; + hash->allocnode = kl_hnode_alloc; + hash->freenode = kl_hnode_free; + hash->context = NULL; + hash->mask = INIT_MASK; + hash->dynamic = 1; /* 7 */ + clear_table(hash); /* 8 */ + assert (kl_hash_verify(hash)); + return hash; + } + free(hash); + } + + return NULL; +} + +/* + * Select a different set of node allocator routines. + */ + +void kl_hash_set_allocator(hash_t *hash, hnode_alloc_t al, + hnode_free_t fr, void *context) +{ + assert (kl_hash_count(hash) == 0); + assert ((al == 0 && fr == 0) || (al != 0 && fr != 0)); + + hash->allocnode = al ? al : kl_hnode_alloc; + hash->freenode = fr ? fr : kl_hnode_free; + hash->context = context; +} + +/* + * Free every node in the hash using the hash->freenode() function pointer, and + * cause the hash to become empty. + */ + +void kl_hash_free_nodes(hash_t *hash) +{ + hscan_t hs; + hnode_t *node; + kl_hash_scan_begin(&hs, hash); + while ((node = kl_hash_scan_next(&hs))) { + kl_hash_scan_delete(hash, node); + hash->freenode(node, hash->context); + } + hash->nodecount = 0; + clear_table(hash); +} + +/* + * Obsolescent function for removing all nodes from a table, + * freeing them and then freeing the table all in one step. + */ + +void kl_hash_free(hash_t *hash) +{ +#ifdef KAZLIB_OBSOLESCENT_DEBUG + assert ("call to obsolescent function hash_free()" && 0); +#endif + kl_hash_free_nodes(hash); + kl_hash_destroy(hash); +} + +/* + * Free a dynamic hash table structure. + */ + +void kl_hash_destroy(hash_t *hash) +{ + assert (hash_val_t_bit != 0); + assert (kl_hash_isempty(hash)); + free(hash->table); + free(hash); +} + +/* + * Initialize a user supplied hash structure. The user also supplies a table of + * chains which is assigned to the hash structure. The table is static---it + * will not grow or shrink. + * 1. See note 1. in hash_create(). + * 2. The user supplied array of pointers hopefully contains nchains nodes. + * 3. See note 7. in hash_create(). + * 4. We must dynamically compute the mask from the given power of two table + * size. + * 5. The user supplied table can't be assumed to contain null pointers, + * so we reset it here. + */ + +hash_t *kl_hash_init(hash_t *hash, hashcount_t maxcount, + hash_comp_t compfun, hash_fun_t hashfun, hnode_t **table, + hashcount_t nchains) +{ + if (hash_val_t_bit == 0) /* 1 */ + compute_bits(); + + assert (is_power_of_two(nchains)); + + hash->table = table; /* 2 */ + hash->nchains = nchains; + hash->nodecount = 0; + hash->maxcount = maxcount; + hash->compare = compfun ? compfun : hash_comp_default; + hash->function = hashfun ? hashfun : hash_fun_default; + hash->dynamic = 0; /* 3 */ + hash->mask = compute_mask(nchains); /* 4 */ + clear_table(hash); /* 5 */ + + assert (kl_hash_verify(hash)); + + return hash; +} + +/* + * Reset the hash scanner so that the next element retrieved by + * hash_scan_next() shall be the first element on the first non-empty chain. + * Notes: + * 1. Locate the first non empty chain. + * 2. If an empty chain is found, remember which one it is and set the next + * pointer to refer to its first element. + * 3. Otherwise if a chain is not found, set the next pointer to NULL + * so that hash_scan_next() shall indicate failure. + */ + +void kl_hash_scan_begin(hscan_t *scan, hash_t *hash) +{ + hash_val_t nchains = hash->nchains; + hash_val_t chain; + + scan->table = hash; + + /* 1 */ + + for (chain = 0; chain < nchains && hash->table[chain] == 0; chain++) + ; + + if (chain < nchains) { /* 2 */ + scan->chain = chain; + scan->next = hash->table[chain]; + } else { /* 3 */ + scan->next = NULL; + } +} + +/* + * Retrieve the next node from the hash table, and update the pointer + * for the next invocation of hash_scan_next(). + * Notes: + * 1. Remember the next pointer in a temporary value so that it can be + * returned. + * 2. This assertion essentially checks whether the module has been properly + * initialized. The first point of interaction with the module should be + * either hash_create() or hash_init(), both of which set hash_val_t_bit to + * a non zero value. + * 3. If the next pointer we are returning is not NULL, then the user is + * allowed to call hash_scan_next() again. We prepare the new next pointer + * for that call right now. That way the user is allowed to delete the node + * we are about to return, since we will no longer be needing it to locate + * the next node. + * 4. If there is a next node in the chain (next->next), then that becomes the + * new next node, otherwise ... + * 5. We have exhausted the current chain, and must locate the next subsequent + * non-empty chain in the table. + * 6. If a non-empty chain is found, the first element of that chain becomes + * the new next node. Otherwise there is no new next node and we set the + * pointer to NULL so that the next time hash_scan_next() is called, a null + * pointer shall be immediately returned. + */ + + +hnode_t *kl_hash_scan_next(hscan_t *scan) +{ + hnode_t *next = scan->next; /* 1 */ + hash_t *hash = scan->table; + hash_val_t chain = scan->chain + 1; + hash_val_t nchains = hash->nchains; + + assert (hash_val_t_bit != 0); /* 2 */ + + if (next) { /* 3 */ + if (next->next) { /* 4 */ + scan->next = next->next; + } else { + while (chain < nchains && hash->table[chain] == 0) /* 5 */ + chain++; + if (chain < nchains) { /* 6 */ + scan->chain = chain; + scan->next = hash->table[chain]; + } else { + scan->next = NULL; + } + } + } + return next; +} + +/* + * Insert a node into the hash table. + * Notes: + * 1. It's illegal to insert more than the maximum number of nodes. The client + * should verify that the hash table is not full before attempting an + * insertion. + * 2. The same key may not be inserted into a table twice. + * 3. If the table is dynamic and the load factor is already at >= 2, + * grow the table. + * 4. We take the bottom N bits of the hash value to derive the chain index, + * where N is the base 2 logarithm of the size of the hash table. + */ + +void kl_hash_insert(hash_t *hash, hnode_t *node, const void *key) +{ + hash_val_t hkey, chain; + + assert (hash_val_t_bit != 0); + assert (node->next == NULL); + assert (hash->nodecount < hash->maxcount); /* 1 */ + assert (kl_hash_lookup(hash, key) == NULL); /* 2 */ + + if (hash->dynamic && hash->nodecount >= hash->highmark) /* 3 */ + grow_table(hash); + + hkey = hash->function(key); + chain = hkey & hash->mask; /* 4 */ + + node->key = key; + node->hkey = hkey; + node->next = hash->table[chain]; + hash->table[chain] = node; + hash->nodecount++; + + assert (kl_hash_verify(hash)); +} + +/* + * Find a node in the hash table and return a pointer to it. + * Notes: + * 1. We hash the key and keep the entire hash value. As an optimization, when + * we descend down the chain, we can compare hash values first and only if + * hash values match do we perform a full key comparison. + * 2. To locate the chain from among 2^N chains, we look at the lower N bits of + * the hash value by anding them with the current mask. + * 3. Looping through the chain, we compare the stored hash value inside each + * node against our computed hash. If they match, then we do a full + * comparison between the unhashed keys. If these match, we have located the + * entry. + */ + +hnode_t *kl_hash_lookup(hash_t *hash, const void *key) +{ + hash_val_t hkey, chain; + hnode_t *nptr; + + hkey = hash->function(key); /* 1 */ + chain = hkey & hash->mask; /* 2 */ + + for (nptr = hash->table[chain]; nptr; nptr = nptr->next) { /* 3 */ + if (nptr->hkey == hkey && hash->compare(nptr->key, key) == 0) + return nptr; + } + + return NULL; +} + +/* + * Delete the given node from the hash table. Since the chains + * are singly linked, we must locate the start of the node's chain + * and traverse. + * Notes: + * 1. The node must belong to this hash table, and its key must not have + * been tampered with. + * 2. If this deletion will take the node count below the low mark, we + * shrink the table now. + * 3. Determine which chain the node belongs to, and fetch the pointer + * to the first node in this chain. + * 4. If the node being deleted is the first node in the chain, then + * simply update the chain head pointer. + * 5. Otherwise advance to the node's predecessor, and splice out + * by updating the predecessor's next pointer. + * 6. Indicate that the node is no longer in a hash table. + */ + +hnode_t *kl_hash_delete(hash_t *hash, hnode_t *node) +{ + hash_val_t chain; + hnode_t *hptr; + + assert (kl_hash_lookup(hash, node->key) == node); /* 1 */ + assert (hash_val_t_bit != 0); + + if (hash->dynamic && hash->nodecount <= hash->lowmark + && hash->nodecount > INIT_SIZE) + shrink_table(hash); /* 2 */ + + chain = node->hkey & hash->mask; /* 3 */ + hptr = hash->table[chain]; + + if (hptr == node) { /* 4 */ + hash->table[chain] = node->next; + } else { + while (hptr->next != node) { /* 5 */ + assert (hptr != 0); + hptr = hptr->next; + } + assert (hptr->next == node); + hptr->next = node->next; + } + + hash->nodecount--; + assert (kl_hash_verify(hash)); + + node->next = NULL; /* 6 */ + return node; +} + +int kl_hash_alloc_insert(hash_t *hash, const void *key, void *data) +{ + hnode_t *node = hash->allocnode(hash->context); + + if (node) { + kl_hnode_init(node, data); + kl_hash_insert(hash, node, key); + return 1; + } + return 0; +} + +void kl_hash_delete_free(hash_t *hash, hnode_t *node) +{ + kl_hash_delete(hash, node); + hash->freenode(node, hash->context); +} + +/* + * Exactly like hash_delete, except does not trigger table shrinkage. This is to be + * used from within a hash table scan operation. See notes for hash_delete. + */ + +hnode_t *kl_hash_scan_delete(hash_t *hash, hnode_t *node) +{ + hash_val_t chain; + hnode_t *hptr; + + assert (kl_hash_lookup(hash, node->key) == node); + assert (hash_val_t_bit != 0); + + chain = node->hkey & hash->mask; + hptr = hash->table[chain]; + + if (hptr == node) { + hash->table[chain] = node->next; + } else { + while (hptr->next != node) + hptr = hptr->next; + hptr->next = node->next; + } + + hash->nodecount--; + assert (kl_hash_verify(hash)); + node->next = NULL; + + return node; +} + +/* + * Like hash_delete_free but based on hash_scan_delete. + */ + +void kl_hash_scan_delfree(hash_t *hash, hnode_t *node) +{ + kl_hash_scan_delete(hash, node); + hash->freenode(node, hash->context); +} + +/* + * Verify whether the given object is a valid hash table. This means + * Notes: + * 1. If the hash table is dynamic, verify whether the high and + * low expansion/shrinkage thresholds are powers of two. + * 2. Count all nodes in the table, and test each hash value + * to see whether it is correct for the node's chain. + */ + +int kl_hash_verify(hash_t *hash) +{ + hashcount_t count = 0; + hash_val_t chain; + hnode_t *hptr; + + if (hash->dynamic) { /* 1 */ + if (hash->lowmark >= hash->highmark) + return 0; + if (!is_power_of_two(hash->highmark)) + return 0; + if (!is_power_of_two(hash->lowmark)) + return 0; + } + + for (chain = 0; chain < hash->nchains; chain++) { /* 2 */ + for (hptr = hash->table[chain]; hptr != 0; hptr = hptr->next) { + if ((hptr->hkey & hash->mask) != chain) + return 0; + count++; + } + } + + if (count != hash->nodecount) + return 0; + + return 1; +} + +/* + * Test whether the hash table is full and return 1 if this is true, + * 0 if it is false. + */ + +#undef kl_hash_isfull +int kl_hash_isfull(hash_t *hash) +{ + return hash->nodecount == hash->maxcount; +} + +/* + * Test whether the hash table is empty and return 1 if this is true, + * 0 if it is false. + */ + +#undef kl_hash_isempty +int kl_hash_isempty(hash_t *hash) +{ + return hash->nodecount == 0; +} + +static hnode_t *kl_hnode_alloc(void *context) +{ + return malloc(sizeof *kl_hnode_alloc(NULL)); +} + +static void kl_hnode_free(hnode_t *node, void *context) +{ + free(node); +} + + +/* + * Create a hash table node dynamically and assign it the given data. + */ + +hnode_t *kl_hnode_create(void *data) +{ + hnode_t *node = malloc(sizeof *node); + if (node) { + node->data = data; + node->next = NULL; + } + return node; +} + +/* + * Initialize a client-supplied node + */ + +hnode_t *kl_hnode_init(hnode_t *hnode, void *data) +{ + hnode->data = data; + hnode->next = NULL; + return hnode; +} + +/* + * Destroy a dynamically allocated node. + */ + +void kl_hnode_destroy(hnode_t *hnode) +{ + free(hnode); +} + +#undef kl_hnode_put +void kl_hnode_put(hnode_t *node, void *data) +{ + node->data = data; +} + +#undef kl_hnode_get +void *kl_hnode_get(hnode_t *node) +{ + return node->data; +} + +#undef kl_hnode_getkey +const void *kl_hnode_getkey(hnode_t *node) +{ + return node->key; +} + +#undef kl_hash_count +hashcount_t kl_hash_count(hash_t *hash) +{ + return hash->nodecount; +} + +#undef kl_hash_size +hashcount_t kl_hash_size(hash_t *hash) +{ + return hash->nchains; +} + +static hash_val_t hash_fun_default(const void *key) +{ + static unsigned long randbox[] = { + 0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U, + 0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU, + 0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU, + 0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU, + }; + + const unsigned char *str = key; + hash_val_t acc = 0; + + while (*str) { + acc ^= randbox[(*str + acc) & 0xf]; + acc = (acc << 1) | (acc >> 31); + acc &= 0xffffffffU; + acc ^= randbox[((*str++ >> 4) + acc) & 0xf]; + acc = (acc << 2) | (acc >> 30); + acc &= 0xffffffffU; + } + return acc; +} + +static int hash_comp_default(const void *key1, const void *key2) +{ + return strcmp(key1, key2); +} diff --git a/c_src/couchdb-khash2/hash.h b/c_src/couchdb-khash2/hash.h new file mode 100644 index 0000000..0c75ed0 --- /dev/null +++ b/c_src/couchdb-khash2/hash.h @@ -0,0 +1,240 @@ +/* + * Hash Table Data Type + * Copyright (C) 1997 Kaz Kylheku + * + * Free Software License: + * + * All rights are reserved by the author, with the following exceptions: + * Permission is granted to freely reproduce and distribute this software, + * possibly in exchange for a fee, provided that this copyright notice appears + * intact. Permission is also granted to adapt this software to produce + * derivative works, as long as the modified versions carry this copyright + * notice and additional notices stating that the work has been modified. + * This source code may be translated into executable form and incorporated + * into proprietary software; there is no requirement for such software to + * contain a copyright notice related to this source. + * + * $Id: hash.h,v 1.22.2.7 2000/11/13 01:36:45 kaz Exp $ + * $Name: kazlib_1_20 $ + */ + +#ifndef HASH_H +#define HASH_H + +#include +#ifdef KAZLIB_SIDEEFFECT_DEBUG +#include "sfx.h" +#endif + +/* + * Blurb for inclusion into C++ translation units + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned long hashcount_t; +#define HASHCOUNT_T_MAX ULONG_MAX + +typedef unsigned long hash_val_t; +#define HASH_VAL_T_MAX ULONG_MAX + +extern int hash_val_t_bit; + +#ifndef HASH_VAL_T_BIT +#define HASH_VAL_T_BIT ((int) hash_val_t_bit) +#endif + +/* + * Hash chain node structure. + * Notes: + * 1. This preprocessing directive is for debugging purposes. The effect is + * that if the preprocessor symbol KAZLIB_OPAQUE_DEBUG is defined prior to the + * inclusion of this header, then the structure shall be declared as having + * the single member int __OPAQUE__. This way, any attempts by the + * client code to violate the principles of information hiding (by accessing + * the structure directly) can be diagnosed at translation time. However, + * note the resulting compiled unit is not suitable for linking. + * 2. This is a pointer to the next node in the chain. In the last node of a + * chain, this pointer is null. + * 3. The key is a pointer to some user supplied data that contains a unique + * identifier for each hash node in a given table. The interpretation of + * the data is up to the user. When creating or initializing a hash table, + * the user must supply a pointer to a function for comparing two keys, + * and a pointer to a function for hashing a key into a numeric value. + * 4. The value is a user-supplied pointer to void which may refer to + * any data object. It is not interpreted in any way by the hashing + * module. + * 5. The hashed key is stored in each node so that we don't have to rehash + * each key when the table must grow or shrink. + */ + +typedef struct hnode_t { + #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) /* 1 */ + struct hnode_t *hash_next; /* 2 */ + const void *hash_key; /* 3 */ + void *hash_data; /* 4 */ + hash_val_t hash_hkey; /* 5 */ + #else + int hash_dummy; + #endif +} hnode_t; + +/* + * The comparison function pointer type. A comparison function takes two keys + * and produces a value of -1 if the left key is less than the right key, a + * value of 0 if the keys are equal, and a value of 1 if the left key is + * greater than the right key. + */ + +typedef int (*hash_comp_t)(const void *, const void *); + +/* + * The hashing function performs some computation on a key and produces an + * integral value of type hash_val_t based on that key. For best results, the + * function should have a good randomness properties in *all* significant bits + * over the set of keys that are being inserted into a given hash table. In + * particular, the most significant bits of hash_val_t are most significant to + * the hash module. Only as the hash table expands are less significant bits + * examined. Thus a function that has good distribution in its upper bits but + * not lower is preferrable to one that has poor distribution in the upper bits + * but not the lower ones. + */ + +typedef hash_val_t (*hash_fun_t)(const void *); + +/* + * allocator functions + */ + +typedef hnode_t *(*hnode_alloc_t)(void *); +typedef void (*hnode_free_t)(hnode_t *, void *); + +/* + * This is the hash table control structure. It keeps track of information + * about a hash table, as well as the hash table itself. + * Notes: + * 1. Pointer to the hash table proper. The table is an array of pointers to + * hash nodes (of type hnode_t). If the table is empty, every element of + * this table is a null pointer. A non-null entry points to the first + * element of a chain of nodes. + * 2. This member keeps track of the size of the hash table---that is, the + * number of chain pointers. + * 3. The count member maintains the number of elements that are presently + * in the hash table. + * 4. The maximum count is the greatest number of nodes that can populate this + * table. If the table contains this many nodes, no more can be inserted, + * and the hash_isfull() function returns true. + * 5. The high mark is a population threshold, measured as a number of nodes, + * which, if exceeded, will trigger a table expansion. Only dynamic hash + * tables are subject to this expansion. + * 6. The low mark is a minimum population threshold, measured as a number of + * nodes. If the table population drops below this value, a table shrinkage + * will occur. Only dynamic tables are subject to this reduction. No table + * will shrink beneath a certain absolute minimum number of nodes. + * 7. This is the a pointer to the hash table's comparison function. The + * function is set once at initialization or creation time. + * 8. Pointer to the table's hashing function, set once at creation or + * initialization time. + * 9. The current hash table mask. If the size of the hash table is 2^N, + * this value has its low N bits set to 1, and the others clear. It is used + * to select bits from the result of the hashing function to compute an + * index into the table. + * 10. A flag which indicates whether the table is to be dynamically resized. It + * is set to 1 in dynamically allocated tables, 0 in tables that are + * statically allocated. + */ + +typedef struct hash_t { + #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) + struct hnode_t **hash_table; /* 1 */ + hashcount_t hash_nchains; /* 2 */ + hashcount_t hash_nodecount; /* 3 */ + hashcount_t hash_maxcount; /* 4 */ + hashcount_t hash_highmark; /* 5 */ + hashcount_t hash_lowmark; /* 6 */ + hash_comp_t hash_compare; /* 7 */ + hash_fun_t hash_function; /* 8 */ + hnode_alloc_t hash_allocnode; + hnode_free_t hash_freenode; + void *hash_context; + hash_val_t hash_mask; /* 9 */ + int hash_dynamic; /* 10 */ + #else + int hash_dummy; + #endif +} hash_t; + +/* + * Hash scanner structure, used for traversals of the data structure. + * Notes: + * 1. Pointer to the hash table that is being traversed. + * 2. Reference to the current chain in the table being traversed (the chain + * that contains the next node that shall be retrieved). + * 3. Pointer to the node that will be retrieved by the subsequent call to + * hash_scan_next(). + */ + +typedef struct hscan_t { + #if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) + hash_t *hash_table; /* 1 */ + hash_val_t hash_chain; /* 2 */ + hnode_t *hash_next; /* 3 */ + #else + int hash_dummy; + #endif +} hscan_t; + +extern hash_t *kl_hash_create(hashcount_t, hash_comp_t, hash_fun_t); +extern void kl_hash_set_allocator(hash_t *, hnode_alloc_t, hnode_free_t, void *); +extern void kl_hash_destroy(hash_t *); +extern void kl_hash_free_nodes(hash_t *); +extern void kl_hash_free(hash_t *); +extern hash_t *kl_hash_init(hash_t *, hashcount_t, hash_comp_t, + hash_fun_t, hnode_t **, hashcount_t); +extern void kl_hash_insert(hash_t *, hnode_t *, const void *); +extern hnode_t *kl_hash_lookup(hash_t *, const void *); +extern hnode_t *kl_hash_delete(hash_t *, hnode_t *); +extern int kl_hash_alloc_insert(hash_t *, const void *, void *); +extern void kl_hash_delete_free(hash_t *, hnode_t *); + +extern void kl_hnode_put(hnode_t *, void *); +extern void *kl_hnode_get(hnode_t *); +extern const void *kl_hnode_getkey(hnode_t *); +extern hashcount_t kl_hash_count(hash_t *); +extern hashcount_t kl_hash_size(hash_t *); + +extern int kl_hash_isfull(hash_t *); +extern int kl_hash_isempty(hash_t *); + +extern void kl_hash_scan_begin(hscan_t *, hash_t *); +extern hnode_t *kl_hash_scan_next(hscan_t *); +extern hnode_t *kl_hash_scan_delete(hash_t *, hnode_t *); +extern void kl_hash_scan_delfree(hash_t *, hnode_t *); + +extern int kl_hash_verify(hash_t *); + +extern hnode_t *kl_hnode_create(void *); +extern hnode_t *kl_hnode_init(hnode_t *, void *); +extern void kl_hnode_destroy(hnode_t *); + +#if defined(HASH_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) +#ifdef KAZLIB_SIDEEFFECT_DEBUG +#define kl_hash_isfull(H) (SFX_CHECK(H)->hash_nodecount == (H)->hash_maxcount) +#else +#define kl_hash_isfull(H) ((H)->hash_nodecount == (H)->hash_maxcount) +#endif +#define kl_hash_isempty(H) ((H)->hash_nodecount == 0) +#define kl_hash_count(H) ((H)->hash_nodecount) +#define kl_hash_size(H) ((H)->hash_nchains) +#define kl_hnode_get(N) ((N)->hash_data) +#define kl_hnode_getkey(N) ((N)->hash_key) +#define kl_hnode_put(N, V) ((N)->hash_data = (V)) +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c_src/couchdb-khash2/khash.c b/c_src/couchdb-khash2/khash.c new file mode 100644 index 0000000..b15a18e --- /dev/null +++ b/c_src/couchdb-khash2/khash.c @@ -0,0 +1,658 @@ +// This file is part of khash released under the MIT license. +// See the LICENSE file for more information. +// Copyright 2013 Cloudant, Inc + +#include +#include +#include + +#include "erl_nif.h" +#include "hash.h" + +#ifdef _WIN32 +#define INLINE __inline +#else +#define INLINE inline +#endif + +#define KHASH_VERSION 0 + + +typedef struct +{ + ERL_NIF_TERM atom_ok; + ERL_NIF_TERM atom_error; + ERL_NIF_TERM atom_value; + ERL_NIF_TERM atom_not_found; + ERL_NIF_TERM atom_end_of_table; + ERL_NIF_TERM atom_expired_iterator; + ErlNifResourceType* res_hash; + ErlNifResourceType* res_iter; +} khash_priv; + + +typedef struct +{ + unsigned int hval; + ErlNifEnv* env; + ERL_NIF_TERM key; + ERL_NIF_TERM val; +} khnode_t; + + +typedef struct +{ + int version; + unsigned int gen; + hash_t* h; + ErlNifPid p; +} khash_t; + + +typedef struct +{ + int version; + unsigned int gen; + khash_t* khash; + hscan_t scan; +} khash_iter_t; + + +static INLINE ERL_NIF_TERM +make_atom(ErlNifEnv* env, const char* name) +{ + ERL_NIF_TERM ret; + if(enif_make_existing_atom(env, name, &ret, ERL_NIF_LATIN1)) { + return ret; + } + return enif_make_atom(env, name); +} + + +static INLINE ERL_NIF_TERM +make_ok(ErlNifEnv* env, khash_priv* priv, ERL_NIF_TERM value) +{ + return enif_make_tuple2(env, priv->atom_ok, value); +} + + +static INLINE ERL_NIF_TERM +make_error(ErlNifEnv* env, khash_priv* priv, ERL_NIF_TERM reason) +{ + return enif_make_tuple2(env, priv->atom_error, reason); +} + + +static INLINE int +check_pid(ErlNifEnv* env, khash_t* khash) +{ + ErlNifPid pid; + enif_self(env, &pid); + + if(enif_compare(pid.pid, khash->p.pid) == 0) { + return 1; + } + + return 0; +} + + +hnode_t* +khnode_alloc(void* ctx) +{ + hnode_t* ret = (hnode_t*) enif_alloc(sizeof(hnode_t)); + khnode_t* node = (khnode_t*) enif_alloc(sizeof(khnode_t)); + + memset(ret, '\0', sizeof(hnode_t)); + memset(node, '\0', sizeof(khnode_t)); + + node->env = enif_alloc_env(); + ret->hash_key = node; + + return ret; +} + + +void +khnode_free(hnode_t* obj, void* ctx) +{ + khnode_t* node = (khnode_t*) kl_hnode_getkey(obj); + enif_free_env(node->env); + enif_free(node); + enif_free(obj); + return; +} + + +int +khash_cmp_fun(const void* l, const void* r) +{ + khnode_t* left = (khnode_t*) l; + khnode_t* right = (khnode_t*) r; + int cmp = enif_compare(left->key, right->key); + + if(cmp < 0) { + return -1; + } else if(cmp == 0) { + return 0; + } else { + return 1; + } +} + + +hash_val_t +khash_hash_fun(const void* obj) +{ + khnode_t* node = (khnode_t*) obj; + return (hash_val_t) node->hval; +} + + +static INLINE khash_t* +khash_create_int(ErlNifEnv* env, khash_priv* priv, ERL_NIF_TERM opts) +{ + khash_t* khash = NULL; + + assert(priv != NULL && "missing private data member"); + + khash = (khash_t*) enif_alloc_resource(priv->res_hash, sizeof(khash_t)); + memset(khash, '\0', sizeof(khash_t)); + khash->version = KHASH_VERSION; + khash->gen = 0; + + khash->h = kl_hash_create(HASHCOUNT_T_MAX, khash_cmp_fun, khash_hash_fun); + + if(khash->h == NULL ) { + enif_release_resource(khash); + return NULL; + } + + kl_hash_set_allocator(khash->h, khnode_alloc, khnode_free, NULL); + enif_self(env, &(khash->p)); + + return khash; +} + + +static ERL_NIF_TERM +khash_new(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + khash_priv* priv = enif_priv_data(env); + khash_t* khash; + ERL_NIF_TERM ret; + + if(argc != 1) { + return enif_make_badarg(env); + } + + khash = khash_create_int(env, priv, argv[0]); + if(khash == NULL) { + return enif_make_badarg(env); + } + + ret = enif_make_resource(env, khash); + enif_release_resource(khash); + + return make_ok(env, priv, ret); +} + + +static void +khash_free(ErlNifEnv* env, void* obj) +{ + khash_t* khash = (khash_t*) obj; + + if(khash->h != NULL) { + kl_hash_free_nodes(khash->h); + kl_hash_destroy(khash->h); + } + + return; +} + + +static ERL_NIF_TERM +khash_to_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + khash_priv* priv = (khash_priv*) enif_priv_data(env); + ERL_NIF_TERM ret = enif_make_list(env, 0); + khash_t* khash = NULL; + void* res = NULL; + hscan_t scan; + hnode_t* entry; + khnode_t* node; + ERL_NIF_TERM key; + ERL_NIF_TERM val; + ERL_NIF_TERM tuple; + + if(argc != 1) { + return enif_make_badarg(env); + } + + if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) { + return enif_make_badarg(env); + } + + khash = (khash_t*) res; + + if(!check_pid(env, khash)) { + return enif_make_badarg(env); + } + + kl_hash_scan_begin(&scan, khash->h); + + while((entry = kl_hash_scan_next(&scan)) != NULL) { + node = (khnode_t*) kl_hnode_getkey(entry); + key = enif_make_copy(env, node->key); + val = enif_make_copy(env, node->val); + tuple = enif_make_tuple2(env, key, val); + ret = enif_make_list_cell(env, tuple, ret); + } + + return ret; +} + + +static ERL_NIF_TERM +khash_clear(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + khash_priv* priv = enif_priv_data(env); + khash_t* khash = NULL; + void* res = NULL; + + if(argc != 1) { + return enif_make_badarg(env); + } + + if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) { + return enif_make_badarg(env); + } + + khash = (khash_t*) res; + + if(!check_pid(env, khash)) { + return enif_make_badarg(env); + } + + kl_hash_free_nodes(khash->h); + + khash->gen += 1; + + return priv->atom_ok; +} + + +static INLINE hnode_t* +khash_lookup_int(ErlNifEnv* env, uint32_t hv, ERL_NIF_TERM key, khash_t* khash) +{ + khnode_t node; + node.hval = hv; + node.env = env; + node.key = key; + return kl_hash_lookup(khash->h, &node); +} + + +static ERL_NIF_TERM +khash_lookup(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + khash_priv* priv = enif_priv_data(env); + khash_t* khash = NULL; + void* res = NULL; + uint32_t hval; + hnode_t* entry; + khnode_t* node; + ERL_NIF_TERM ret; + + if(argc != 3) { + return enif_make_badarg(env); + } + + if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) { + return enif_make_badarg(env); + } + + khash = (khash_t*) res; + + if(!check_pid(env, khash)) { + return enif_make_badarg(env); + } + + if(!enif_get_uint(env, argv[1], &hval)) { + return enif_make_badarg(env); + } + + entry = khash_lookup_int(env, hval, argv[2], khash); + if(entry == NULL) { + ret = priv->atom_not_found; + } else { + node = (khnode_t*) kl_hnode_getkey(entry); + ret = enif_make_copy(env, node->val); + ret = enif_make_tuple2(env, priv->atom_value, ret); + } + + return ret; +} + + +static ERL_NIF_TERM +khash_get(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + khash_priv* priv = enif_priv_data(env); + khash_t* khash = NULL; + void* res = NULL; + uint32_t hval; + hnode_t* entry; + khnode_t* node; + ERL_NIF_TERM ret; + + if(argc != 4) { + return enif_make_badarg(env); + } + + if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) { + return enif_make_badarg(env); + } + + khash = (khash_t*) res; + + if(!check_pid(env, khash)) { + return enif_make_badarg(env); + } + + if(!enif_get_uint(env, argv[1], &hval)) { + return enif_make_badarg(env); + } + + entry = khash_lookup_int(env, hval, argv[2], khash); + if(entry == NULL) { + ret = argv[3]; + } else { + node = (khnode_t*) kl_hnode_getkey(entry); + ret = enif_make_copy(env, node->val); + } + + return ret; +} + + +static ERL_NIF_TERM +khash_put(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + khash_priv* priv = enif_priv_data(env); + khash_t* khash = NULL; + void* res = NULL; + uint32_t hval; + hnode_t* entry; + khnode_t* node; + + if(argc != 4) { + return enif_make_badarg(env); + } + + if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) { + return enif_make_badarg(env); + } + + khash = (khash_t*) res; + + if(!check_pid(env, khash)) { + return enif_make_badarg(env); + } + + if(!enif_get_uint(env, argv[1], &hval)) { + return enif_make_badarg(env); + } + + entry = khash_lookup_int(env, hval, argv[2], khash); + if(entry == NULL) { + entry = khnode_alloc(NULL); + node = (khnode_t*) kl_hnode_getkey(entry); + node->hval = hval; + node->key = enif_make_copy(node->env, argv[2]); + node->val = enif_make_copy(node->env, argv[3]); + kl_hash_insert(khash->h, entry, node); + } else { + node = (khnode_t*) kl_hnode_getkey(entry); + enif_clear_env(node->env); + node->key = enif_make_copy(node->env, argv[2]); + node->val = enif_make_copy(node->env, argv[3]); + } + + khash->gen += 1; + + return priv->atom_ok; +} + + +static ERL_NIF_TERM +khash_del(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + khash_priv* priv = enif_priv_data(env); + khash_t* khash = NULL; + void* res = NULL; + uint32_t hval; + hnode_t* entry; + ERL_NIF_TERM ret; + + if(argc != 3) { + return enif_make_badarg(env); + } + + if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) { + return enif_make_badarg(env); + } + + khash = (khash_t*) res; + + if(!check_pid(env, khash)) { + return enif_make_badarg(env); + } + + if(!enif_get_uint(env, argv[1], &hval)) { + return enif_make_badarg(env); + } + + entry = khash_lookup_int(env, hval, argv[2], khash); + if(entry == NULL) { + ret = priv->atom_not_found; + } else { + kl_hash_delete_free(khash->h, entry); + ret = priv->atom_ok; + } + + khash->gen += 1; + + return ret; +} + + +static ERL_NIF_TERM +khash_size(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + khash_priv* priv = enif_priv_data(env); + khash_t* khash; + + if(argc != 1) { + return enif_make_badarg(env); + } + + if(!enif_get_resource(env, argv[0], priv->res_hash, (void*) &khash)) { + return enif_make_badarg(env); + } + + if(!check_pid(env, khash)) { + return enif_make_badarg(env); + } + + return enif_make_uint64(env, kl_hash_count(khash->h)); +} + + +static ERL_NIF_TERM +khash_iter(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + khash_priv* priv = enif_priv_data(env); + khash_t* khash = NULL; + void* res = NULL; + khash_iter_t* iter; + ERL_NIF_TERM ret; + + if(argc != 1) { + return enif_make_badarg(env); + } + + if(!enif_get_resource(env, argv[0], priv->res_hash, &res)) { + return enif_make_badarg(env); + } + + khash = (khash_t*) res; + + if(!check_pid(env, khash)) { + return enif_make_badarg(env); + } + + iter = (khash_iter_t*) enif_alloc_resource( + priv->res_iter, sizeof(khash_iter_t)); + memset(iter, '\0', sizeof(khash_iter_t)); + iter->version = KHASH_VERSION; + iter->gen = khash->gen; + iter->khash = khash; + kl_hash_scan_begin(&(iter->scan), iter->khash->h); + + // The iterator needs to guarantee that the khash + // remains alive for the life of the iterator. + enif_keep_resource(khash); + + ret = enif_make_resource(env, iter); + enif_release_resource(iter); + + return make_ok(env, priv, ret); +} + + +static void +khash_iter_free(ErlNifEnv* env, void* obj) +{ + khash_iter_t* iter = (khash_iter_t*) obj; + enif_release_resource(iter->khash); +} + + +static ERL_NIF_TERM +khash_iter_next(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + khash_priv* priv = enif_priv_data(env); + khash_iter_t* iter = NULL; + void* res = NULL; + hnode_t* entry; + khnode_t* node; + ERL_NIF_TERM key; + ERL_NIF_TERM val; + + if(argc != 1) { + return enif_make_badarg(env); + } + + if(!enif_get_resource(env, argv[0], priv->res_iter, &res)) { + return enif_make_badarg(env); + } + + iter = (khash_iter_t*) res; + + if(!check_pid(env, iter->khash)) { + return enif_make_badarg(env); + } + + if(iter->gen != iter->khash->gen) { + return make_error(env, priv, priv->atom_expired_iterator); + } + + entry = kl_hash_scan_next(&(iter->scan)); + if(entry == NULL) { + return priv->atom_end_of_table; + } + + node = (khnode_t*) kl_hnode_getkey(entry); + key = enif_make_copy(env, node->key); + val = enif_make_copy(env, node->val); + return enif_make_tuple2(env, key, val); +} + + +static int +load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info) +{ + int flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER; + ErlNifResourceType* res; + + khash_priv* new_priv = (khash_priv*) enif_alloc(sizeof(khash_priv)); + if(new_priv == NULL) { + return 1; + } + + res = enif_open_resource_type( + env, NULL, "khash", khash_free, flags, NULL); + if(res == NULL) { + return 1; + } + new_priv->res_hash = res; + + res = enif_open_resource_type( + env, NULL, "khash_iter", khash_iter_free, flags, NULL); + if(res == NULL) { + return 1; + } + new_priv->res_iter = res; + + new_priv->atom_ok = make_atom(env, "ok"); + new_priv->atom_error = make_atom(env, "error"); + new_priv->atom_value = make_atom(env, "value"); + new_priv->atom_not_found = make_atom(env, "not_found"); + new_priv->atom_end_of_table = make_atom(env, "end_of_table"); + new_priv->atom_expired_iterator = make_atom(env, "expired_iterator"); + + *priv = (void*) new_priv; + + return 0; +} + + +static int +reload(ErlNifEnv* env, void** priv, ERL_NIF_TERM info) +{ + return 0; +} + + +static int +upgrade(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM info) +{ + return load(env, priv, info); +} + + +static void +unload(ErlNifEnv* env, void* priv) +{ + enif_free(priv); + return; +} + + +static ErlNifFunc funcs[] = { + {"new", 1, khash_new}, + {"to_list", 1, khash_to_list}, + {"clear", 1, khash_clear}, + {"lookup_int", 3, khash_lookup}, + {"get_int", 4, khash_get}, + {"put_int", 4, khash_put}, + {"del_int", 3, khash_del}, + {"size", 1, khash_size}, + {"iter", 1, khash_iter}, + {"iter_next", 1, khash_iter_next} +}; + + +ERL_NIF_INIT(khash, funcs, &load, &reload, &upgrade, &unload); diff --git a/c_src/couchdb-khash2/rebar.config b/c_src/couchdb-khash2/rebar.config new file mode 100644 index 0000000..2e88bd2 --- /dev/null +++ b/c_src/couchdb-khash2/rebar.config @@ -0,0 +1,12 @@ +{port_specs, [ + {"../../priv/khash2.so", ["*.c"]} +]}. + +{port_env, [ + % Development compilation + % {".*", "CFLAGS", "$CFLAGS -g -Wall -Werror -fPIC"} + + % Production compilation + {"(linux|solaris|darwin|freebsd)", "CFLAGS", "$CFLAGS -Wall -Werror -DNDEBUG -O3"}, + {"win32", "CFLAGS", "$CFLAGS /O2 /DNDEBUG /Wall"} +]}. diff --git a/c_src/nifArray/nifArray.bak b/c_src/nifArray/nifArray.bak new file mode 100644 index 0000000..66c2dcc --- /dev/null +++ b/c_src/nifArray/nifArray.bak @@ -0,0 +1,110 @@ +#include +#include "erl_nif.h" + +#ifdef _WIN32 +#define INLINE __inline +#else +#define INLINE inline +#endif + +static ErlNifResourceType* ResType = NULL; +ERL_NIF_TERM null; +ERL_NIF_TERM ok; + + +static int load(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info) +{ + ErlNifResourceFlags flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER; + ResType = enif_open_resource_type(env, NULL, "_nifArray_", NULL, flags, NULL); + if(NULL == ResType) + return -1; + null = enif_make_atom(env, "null"); + ok = enif_make_atom(env, "ok"); + return 0; +} + +static ERL_NIF_TERM new(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + uint32_t Size; + if(!enif_get_uint(env, argv[0], &Size)) { + return enif_make_badarg(env); + } + + ErlNifBinary **ArrayPtr = (ErlNifBinary **) enif_alloc_resource(ResType, sizeof(ErlNifBinary*)*Size); + int i; + for(i = 0; i < Size; i++){ + ArrayPtr[i] = NULL; + } + ERL_NIF_TERM Res = enif_make_resource(env, ArrayPtr); + enif_release_resource(ArrayPtr); + return Res; +} + +static ERL_NIF_TERM get(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + uint32_t Index; + if(!enif_get_uint(env, argv[1], &Index)) { + return enif_make_badarg(env); + } + + ErlNifBinary **ArrayPtr; + + if (!enif_get_resource(env, argv[0], ResType, (void**)&ArrayPtr)){ + return enif_make_badarg(env); + } + if(NULL != ArrayPtr[Index]){ + ERL_NIF_TERM Ret; + enif_binary_to_term(env, ArrayPtr[Index]->data, ArrayPtr[Index]->size, &Ret, 0); + return Ret; + } else{ + return null; + } +} + +static ERL_NIF_TERM put(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + uint32_t Index; + if(!enif_get_uint(env, argv[1], &Index)) { + return enif_make_badarg(env); + } + + ErlNifBinary *Value = (ErlNifBinary *)malloc(sizeof(ErlNifBinary)); + if(!enif_term_to_binary(env, argv[2], Value)) + return enif_make_badarg(env); + + ErlNifBinary **ArrayPtr; + + if (!enif_get_resource(env, argv[0], ResType, (void**)&ArrayPtr)){ + return enif_make_badarg(env); + } + if(NULL == ArrayPtr[Index]){ + ArrayPtr[Index] = Value; + return ok; + } else{ + enif_release_binary(ArrayPtr[Index]), + free(ArrayPtr[Index]); + ArrayPtr[Index] = Value; + return ok; + } +} + +static ERL_NIF_TERM test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifBinary Value; + if(!enif_term_to_binary(env, argv[0], &Value)) + return enif_make_badarg(env); + // enif_fprintf(stdout, "IMY************ %T", argv[0]); + ERL_NIF_TERM Ret; + enif_binary_to_term(env, Value.data, Value.size, &Ret, 0); + enif_release_binary(&Value); + return Ret; +} + +static ErlNifFunc nifFuns[] = { + {"new", 1, new}, + {"get", 2, get}, + {"put", 3, put}, + {"test", 1, test}, +}; + +ERL_NIF_INIT(nifArray, nifFuns, &load, NULL, NULL, NULL) diff --git a/c_src/nifArray/nifArray.c b/c_src/nifArray/nifArray.c new file mode 100644 index 0000000..5ba45d5 --- /dev/null +++ b/c_src/nifArray/nifArray.c @@ -0,0 +1,108 @@ +#include +#include "erl_nif.h" + +#ifdef _WIN32 +#define INLINE __inline +#else +#define INLINE inline +#endif + +static ErlNifResourceType* ResType = NULL; +ERL_NIF_TERM null; +ERL_NIF_TERM ok; + +static int load(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info) +{ + ErlNifResourceFlags flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER; + ResType = enif_open_resource_type(env, NULL, "_nifArray_", NULL, flags, NULL); + if(NULL == ResType) + return -1; + null = enif_make_atom(env, "null"); + ok = enif_make_atom(env, "ok"); + return 0; +} + +static ERL_NIF_TERM new(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + uint32_t Size; + if(!enif_get_uint(env, argv[0], &Size)) { + return enif_make_badarg(env); + } + + ErlNifBinary *ArrayPtr = (ErlNifBinary *) enif_alloc_resource(ResType, sizeof(ErlNifBinary)*Size); + int i; + for(i = 0; i < Size; i++){ + ErlNifBinary nullBin; + enif_term_to_binary(env, null, &nullBin); + ArrayPtr[i] = nullBin; + } + ERL_NIF_TERM Res = enif_make_resource(env, ArrayPtr); + enif_release_resource(ArrayPtr); + return Res; +} + +static ERL_NIF_TERM get(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + uint32_t Index; + if(!enif_get_uint(env, argv[1], &Index)) { + return enif_make_badarg(env); + } + + ErlNifBinary *ArrayPtr; + + if (!enif_get_resource(env, argv[0], ResType, (void **)&ArrayPtr)){ + return enif_make_badarg(env); + } + //enif_fprintf(stdout, "IMY************get %T \n", argv[1]); + ERL_NIF_TERM Ret; + enif_binary_to_term(env, ArrayPtr[Index].data, ArrayPtr[Index].size, &Ret, 0); + return Ret; +} + +static ERL_NIF_TERM put(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + //enif_fprintf(stdout, "IMY************put0001 %T \n", argv[1]); + uint32_t Index; + if(!enif_get_uint(env, argv[1], &Index)) { + return enif_make_badarg(env); + } + //enif_fprintf(stdout, "IMY************put0002 %T %T \n", argv[1], argv[2]); + ErlNifBinary Value; + if(!enif_term_to_binary(env, argv[2], &Value)) + return enif_make_badarg(env); + + ErlNifBinary *ArrayPtr; + //enif_fprintf(stdout, "IMY************put0003 %T \n", argv[1]); + if (!enif_get_resource(env, argv[0], ResType, (void **)&ArrayPtr)){ + return enif_make_badarg(env); + } + //enif_fprintf(stdout, "IMY************put111 %T \n", argv[1]); + ErlNifBinary OldValue = ArrayPtr[Index]; + // enif_fprintf(stdout, "IMY************put222 %T %d \n", argv[1], Index); + ArrayPtr[Index] = Value; + //enif_fprintf(stdout, "IMY************put333 %T \n", argv[1]); + enif_release_binary(&OldValue); + //enif_fprintf(stdout, "IMY************put444 %T \n", argv[1]); + return ok; +} + +static ERL_NIF_TERM test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifBinary Value; + if(!enif_term_to_binary(env, argv[0], &Value)) + return enif_make_badarg(env); + // enif_fprintf(stdout, "IMY************ %T", argv[0]); + ERL_NIF_TERM Ret; + enif_binary_to_term(env, Value.data, Value.size, &Ret, 0); + enif_release_binary(&Value); + return Ret; +} + +static ErlNifFunc nifFuns[] = { + {"new", 1, new}, + {"get", 2, get}, + {"put", 3, put}, + {"test", 1, test}, +}; + +ERL_NIF_INIT(nifArray, nifFuns, &load, NULL, NULL, NULL) diff --git a/c_src/nifArray/rebar.config b/c_src/nifArray/rebar.config new file mode 100644 index 0000000..5fa7c76 --- /dev/null +++ b/c_src/nifArray/rebar.config @@ -0,0 +1,7 @@ +{port_specs, [ + {"../../priv/nifArray.so", ["*.c"]} +]}. + + + + diff --git a/c_src/nifHashb/nifHashb.c b/c_src/nifHashb/nifHashb.c new file mode 100644 index 0000000..c1d9346 --- /dev/null +++ b/c_src/nifHashb/nifHashb.c @@ -0,0 +1,455 @@ +#include +#include +#include "erl_nif.h" + +typedef struct { + size_t keySize; + size_t bblSize_1; + size_t bblSize_2; + struct _bbl **arrayPtr; +} hashb; + +typedef struct _bbl { + struct _node *head; +} bbl; + +typedef struct _node { + struct _node *next; + ErlNifBinary Key; + ErlNifBinary Value; +} node; + +#define BBLSIZE_1 128 +#define BBLSIZE_2 128 + +static ErlNifResourceType *ResType = NULL; +static ERL_NIF_TERM undefined; +static ERL_NIF_TERM ok; + +///////////////////////////////hash 函数 ///////////////////////////// +static uint32_t fnv_hash(const unsigned char *data, size_t size, uint32_t salt) { + uint32_t i; + uint64_t hashv; + const unsigned char *_hf_key = (const unsigned char *) (data); + hashv = 2166136261; + for (i = 0; i < size; i++) { + hashv = hashv ^ _hf_key[i]; + hashv = hashv * 16777619; + } + //enif_fprintf(stdout, "IMY************get %T \n", argv[1]); + return hashv; +} + +static uint32_t murmurhash(const unsigned char *key, uint32_t len, uint32_t seed) { + //uint32_t c1 = 0xcc9e2d51; + //uint32_t c2 = 0x1b873593; + //uint32_t r1 = 15; + //uint32_t r2 = 13; + //uint32_t m = 5; + //uint32_t n = 0xe6546b64; + uint32_t h = 0; + uint32_t k = 0; + uint8_t *d = (uint8_t *) key; // 32 bit extract from `key' + const uint32_t *chunks = NULL; + const uint8_t *tail = NULL; // tail - last 8 bytes + int i = 0; + int l = len / 4; // chunk length + + h = seed; + + chunks = (const uint32_t *) (d + l * 4); // body + tail = (const uint8_t *) (d + l * 4); // last 8 byte chunk of `key' + + // for each 4 byte chunk of `key' + for (i = -l; i != 0; ++i) { + // next 4 byte chunk of `key' + k = chunks[i]; + + // encode next 4 byte chunk of `key' + k *= 0xcc9e2d51; + k = (k << 15) | (k >> (32 - 15)); + k *= 0x1b873593; + + // append to hash + h ^= k; + h = (h << 13) | (h >> (32 - 13)); + h = h * 5 + 0xe6546b64; + } + + k = 0; + + // remainder + switch (len & 3) { // `len % 4' + case 3: + k ^= (tail[2] << 16); + case 2: + k ^= (tail[1] << 8); + case 1: + k ^= tail[0]; + k *= 0xcc9e2d51; + k = (k << 15) | (k >> (32 - 15)); + k *= 0x1b873593; + h ^= k; + } + + h ^= len; + + h ^= (h >> 16); + h *= 0x85ebca6b; + h ^= (h >> 13); + h *= 0xc2b2ae35; + h ^= (h >> 16); + + return h; +} + +//////////////////////////////////hash 函数测试/////////////////////////// +static ERL_NIF_TERM hash1(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { + ErlNifBinary Value; + if (!enif_term_to_binary(env, argv[0], &Value)) + return enif_make_badarg(env); + + uint32_t Salt; + if (!enif_get_uint(env, argv[1], &Salt)) { + return enif_make_badarg(env); + } + + Salt = fnv_hash(Value.data, Value.size, Salt); + enif_release_binary(&Value); + return ok; + +}; + +static ERL_NIF_TERM hash2(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { + ErlNifUInt64 Salt; + if (!enif_get_uint64(env, argv[1], &Salt)) + return enif_make_badarg(env); + + ErlNifBinary Value; + if (!enif_term_to_binary(env, argv[0], &Value)) + return enif_make_badarg(env); + enif_release_binary(&Value); + Salt = enif_hash(ERL_NIF_INTERNAL_HASH, argv[1], 2423432432); + return ok; +}; + +static ERL_NIF_TERM hash3(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { + ErlNifUInt64 Salt; + if (!enif_get_uint64(env, argv[1], &Salt)) + return enif_make_badarg(env); + + ErlNifBinary Value; + if (!enif_term_to_binary(env, argv[0], &Value)) + return enif_make_badarg(env); + Salt = murmurhash(Value.data, Value.size, 0); + enif_release_binary(&Value); + return ok; +}; + +static uint8_t compareChar1(const unsigned char *src, const unsigned char *dst, size_t Size) { + + // int j = 0; + // unsigned char *tsrc, *tdst; + // tsrc = src; + // tdst = dst; + // for(j = 0; j <= Size; j++) + // { + // enif_fprintf(stdout, "IMY************get9999111 src %d dst %d \n", *tsrc, *tdst); + // tsrc++; + // tdst++; + // } + uint8_t i = 0; + while (i < Size-1 && (*src == *dst)) { + // enif_fprintf(stdout, "IMY************get99992222 %d %d \n", *src, *dst); + src++; + dst++; + i++; + } + //enif_fprintf(stdout, "IMY************get9999while end %d %d \n", *src, *dst); + if (*src != *dst) { + //enif_fprintf(stdout, "IMY************get99993333 %d %d \n", *src, *dst); + return 1; + } else { + //enif_fprintf(stdout, "IMY************get99995555 %d %d \n", *src, *dst); + return 0; + } +} + +static uint8_t compareChar2(const unsigned char *src, const unsigned char *dst, size_t Size) { + const uint32_t *intSrc = NULL; + const uint32_t *intDst = NULL; + const uint8_t *tailSrc = NULL; // tail - last 8 bytes + const uint8_t *tailDst = NULL; // tail - last 8 bytes + int l = Size / 4; // chunk length + + intSrc = (const uint32_t *) src; // body + intDst = (const uint32_t *) dst; // body + tailSrc = (const uint8_t *) (src + l * 4); // last 8 byte chunk of `key' + tailDst = (const uint8_t *) (dst + l * 4); // last 8 byte chunk of `key' + + // for each 4 byte chunk of `key' + int i = 0; + for (i = 0; i < l; i++) { + if (intSrc[i] != intDst[i]) + return 1; + } + // remainder + switch (Size & 3) { + case 3: + if (tailSrc[0] != tailDst[0] || tailSrc[1] != tailDst[1] || tailSrc[2] != tailDst[2]) { + return 1; + } + break; + case 2: + if (tailSrc[0] != tailDst[0] || tailSrc[1] != tailDst[1]) { + return 1; + } + break; + case 1: + if (tailSrc[0] != tailDst[0]) { + return 1; + } + break; + } + return 0; +} + +static ERL_NIF_TERM compareBin1(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { + ErlNifBinary src, dst; + + if (!(enif_inspect_binary(env, argv[0], &src) && enif_inspect_binary(env, argv[1], &dst))) + return enif_make_badarg(env); + + if (src.size != dst.size) { + return enif_make_int(env, 1); + } + int Ret = compareChar1(src.data, dst.data, src.size); + return enif_make_int(env, Ret); +} + +static ERL_NIF_TERM compareBin2(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { + ErlNifBinary src, dst; + if (!(enif_inspect_binary(env, argv[0], &src) && enif_inspect_binary(env, argv[1], &dst))) + return enif_make_badarg(env); + + if (src.size != dst.size) { + return enif_make_int(env, 1); + } + int Ret = compareChar2(src.data, dst.data, src.size); + return enif_make_int(env, Ret); +} + +///////////////////////////////////////////////////////////////////// + +static int load(ErlNifEnv *env, void **priv, ERL_NIF_TERM load_info) { + ErlNifResourceFlags flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER; + ResType = enif_open_resource_type(env, NULL, "_nifHashb_", NULL, flags, NULL); + if (NULL == ResType) + return -1; + undefined = enif_make_atom(env, "undefined"); + ok = enif_make_atom(env, "ok"); + return 0; +} + +static ERL_NIF_TERM new(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { + hashb *ResPtr = (hashb *) enif_alloc_resource(ResType, sizeof(hashb)); + ResPtr->keySize = 0; + ResPtr->bblSize_1 = BBLSIZE_1; + ResPtr->bblSize_2 = BBLSIZE_2; + ResPtr->arrayPtr = enif_alloc(sizeof(bbl *) * BBLSIZE_1); + int i, j; + bbl NullBbl; + NullBbl.head = NULL; + for (i = 0; i < BBLSIZE_1; i++) { + ResPtr->arrayPtr[i] = enif_alloc(sizeof(bbl) * BBLSIZE_2); + for (j = 0; j < BBLSIZE_2; j++) { + ResPtr->arrayPtr[i][j] = NullBbl; + } + } + ERL_NIF_TERM Res = enif_make_resource(env, ResPtr); + enif_release_resource(ResPtr); + return Res; +} + +static uint8_t compareChar(const unsigned char *src, const unsigned char *dst, size_t Size) { + const uint32_t *intSrc = NULL; + const uint32_t *intDst = NULL; + const uint8_t *tailSrc = NULL; // tail - last 8 bytes + const uint8_t *tailDst = NULL; // tail - last 8 bytes + int l = Size / 4; // chunk length + + intSrc = (const uint32_t *) src; // body + intDst = (const uint32_t *) dst; // body + tailSrc = (const uint8_t *) (src + l * 4); // last 8 byte chunk of `key' + tailDst = (const uint8_t *) (dst + l * 4); // last 8 byte chunk of `key' + + // for each 4 byte chunk of `key' + int i = 0; + for (i = 0; i < l; i++) { + if (intSrc[i] != intDst[i]) + return 1; + } + // remainder + switch (Size & 3) { + case 3: + if (tailSrc[0] != tailDst[0] || tailSrc[1] != tailDst[1] || tailSrc[2] != tailDst[2]) { + return 1; + } + break; + case 2: + if (tailSrc[0] != tailDst[0] || tailSrc[1] != tailDst[1]) { + return 1; + } + break; + case 1: + if (tailSrc[0] != tailDst[0]) { + return 1; + } + break; + } + return 0; +} + +static uint8_t compareBin(ErlNifBinary src, ErlNifBinary dst) { + if (src.size != dst.size) { + //enif_fprintf(stdout, "IMY************get8888\n"); + return 1; + } + //enif_fprintf(stdout, "IMY************get8888111 %d \n", src.size); + return compareChar(src.data, dst.data, src.size); +} + +static ERL_NIF_TERM get(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { + hashb *ResPtr; + + if (!enif_get_resource(env, argv[0], ResType, (void **) &ResPtr)) { + return enif_make_badarg(env); + } + //enif_fprintf(stdout, "IMY************get1111\n"); + + + uint32_t BblIndex_1, BblIndex_2; + if(!enif_get_uint(env, argv[1], &BblIndex_1)) { + return enif_make_badarg(env); + } + + //enif_fprintf(stdout, "IMY************get22221111\n"); + + if(!enif_get_uint(env, argv[2], &BblIndex_2)) { + return enif_make_badarg(env); + } + //enif_fprintf(stdout, "IMY************get2222\n"); + BblIndex_1 = BblIndex_1 % ResPtr->bblSize_1; + BblIndex_2 = BblIndex_2 % ResPtr->bblSize_2; + //enif_fprintf(stdout, "IMY************get3333\n"); + if (NULL == ResPtr->arrayPtr[BblIndex_1][BblIndex_2].head) { + //enif_fprintf(stdout, "IMY************get4444\n"); + return undefined; + } else { + //enif_fprintf(stdout, "IMY************get55555\n"); + ErlNifBinary getKey; + if (!enif_inspect_binary(env, argv[3], &getKey)) + return enif_make_badarg(env); + node *Head = ResPtr->arrayPtr[BblIndex_1][BblIndex_2].head; + while (Head) { + //enif_fprintf(stdout, "IMY************get7777\n"); + if (compareBin(getKey, Head->Key) == 0) { + ERL_NIF_TERM Ret; + enif_binary_to_term(env, Head->Value.data, Head->Value.size, &Ret, 0); + //enif_fprintf(stdout, "IMY************get5555\n"); + return Ret; + } + Head = Head->next; + } + //enif_fprintf(stdout, "IMY************get6666\n"); + return undefined; + } +} + +static ERL_NIF_TERM put(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { + hashb *ResPtr; + + if (!enif_get_resource(env, argv[0], ResType, (void **) &ResPtr)) { + return enif_make_badarg(env); + } + //enif_fprintf(stdout, "IMY************put111 \n"); + + uint32_t BblIndex_1, BblIndex_2; + if(!enif_get_uint(env, argv[1], &BblIndex_1)) { + return enif_make_badarg(env); + } + if(!enif_get_uint(env, argv[2], &BblIndex_2)) { + return enif_make_badarg(env); + } + + BblIndex_1 = BblIndex_1 % ResPtr->bblSize_1; + BblIndex_2 = BblIndex_2 % ResPtr->bblSize_2; + + ErlNifBinary Key, Value; + if (!enif_inspect_binary(env, argv[3], &Key)) + return enif_make_badarg(env); + + if (!enif_inspect_binary(env, argv[4], &Value)) + return enif_make_badarg(env); + + //enif_fprintf(stdout, "IMY************put222 %d %d \n", BblIndex_1, BblIndex_2); + + //enif_fprintf(stdout, "IMY************put3333 \n"); + if (NULL == ResPtr->arrayPtr[BblIndex_1][BblIndex_2].head) { + node *NewNode = enif_alloc(sizeof(node)); + enif_alloc_binary(Key.size, &NewNode->Key); + enif_alloc_binary(Value.size, &NewNode->Value); + memcpy(NewNode->Key.data, Key.data, Key.size); + memcpy(NewNode->Value.data, Value.data, Value.size); + NewNode->next = NULL; + ResPtr->arrayPtr[BblIndex_1][BblIndex_2].head = NewNode; + ResPtr->keySize = ResPtr->keySize + 1; + //enif_fprintf(stdout, "IMY************put4444 \n"); + return ok; + } else { + node *Head = ResPtr->arrayPtr[BblIndex_1][BblIndex_2].head; + while (Head) { + if (compareBin(Key, Head->Key) == 0) { + //ERL_NIF_TERM Ret; + ErlNifBinary OldValue = Head->Value; + enif_release_binary(&OldValue); + //enif_binary_to_term(env, OldValue.data, OldValue.size, &Ret, 0); + ErlNifBinary NewValue; + enif_alloc_binary(Value.size, &NewValue); + memcpy(NewValue.data, Value.data, Value.size); + Head->Value = NewValue; + //enif_fprintf(stdout, "IMY************put55555 \n"); + //return Ret; + return ok; + } + Head = Head->next; + } + node *NewNode = enif_alloc(sizeof(node)); + enif_alloc_binary(Key.size, &NewNode->Key); + enif_alloc_binary(Value.size, &NewNode->Value); + memcpy(NewNode->Key.data, Key.data, Key.size); + memcpy(NewNode->Value.data, Value.data, Value.size); + NewNode->next = ResPtr->arrayPtr[BblIndex_1][BblIndex_2].head; + ResPtr->arrayPtr[BblIndex_1][BblIndex_2].head = NewNode; + ResPtr->keySize = ResPtr->keySize + 1; + //enif_fprintf(stdout, "IMY************put6666\n"); + return ok; + } +} +///////////////////////////////////////////////////////////////////////// + +static ErlNifFunc nifFuns[] = { + {"new", 0, new}, + {"get", 4, get}, + {"put", 5, put}, + {"hash1", 2, hash1}, + {"hash2", 2, hash2}, + {"hash3", 2, hash3}, + {"compareBin1", 2, compareBin1}, + {"compareBin2", 2, compareBin2}, +}; + +ERL_NIF_INIT(nifHashb, nifFuns, +&load, NULL, NULL, NULL) + + diff --git a/c_src/nifHashb/nifHashb.cbak b/c_src/nifHashb/nifHashb.cbak new file mode 100644 index 0000000..70d3002 --- /dev/null +++ b/c_src/nifHashb/nifHashb.cbak @@ -0,0 +1,435 @@ +#include +#include +#include "erl_nif.h" + +typedef struct { + size_t keySize; + size_t bblSize_1; + size_t bblSize_2; + struct _bbl **arrayPtr; +} hashb; + +typedef struct _bbl { + struct _node *head; +} bbl; + +typedef struct _node { + struct _node *next; + ErlNifBinary Key; + ErlNifBinary Value; +} node; + +#define BBLSIZE_1 128 +#define BBLSIZE_2 128 + +static ErlNifResourceType *ResType = NULL; +static ERL_NIF_TERM undefined; +static ERL_NIF_TERM ok; + +///////////////////////////////hash 函数 ///////////////////////////// +static uint32_t fnv_hash(const unsigned char *data, size_t size, uint32_t salt) { + uint32_t i; + uint64_t hashv; + const unsigned char *_hf_key = (const unsigned char *) (data); + hashv = 2166136261; + for (i = 0; i < size; i++) { + hashv = hashv ^ _hf_key[i]; + hashv = hashv * 16777619; + } + //enif_fprintf(stdout, "IMY************get %T \n", argv[1]); + return hashv; +} + +static uint32_t murmurhash(const unsigned char *key, uint32_t len, uint32_t seed) { + //uint32_t c1 = 0xcc9e2d51; + //uint32_t c2 = 0x1b873593; + //uint32_t r1 = 15; + //uint32_t r2 = 13; + //uint32_t m = 5; + //uint32_t n = 0xe6546b64; + uint32_t h = 0; + uint32_t k = 0; + uint8_t *d = (uint8_t *) key; // 32 bit extract from `key' + const uint32_t *chunks = NULL; + const uint8_t *tail = NULL; // tail - last 8 bytes + int i = 0; + int l = len / 4; // chunk length + + h = seed; + + chunks = (const uint32_t *) (d + l * 4); // body + tail = (const uint8_t *) (d + l * 4); // last 8 byte chunk of `key' + + // for each 4 byte chunk of `key' + for (i = -l; i != 0; ++i) { + // next 4 byte chunk of `key' + k = chunks[i]; + + // encode next 4 byte chunk of `key' + k *= 0xcc9e2d51; + k = (k << 15) | (k >> (32 - 15)); + k *= 0x1b873593; + + // append to hash + h ^= k; + h = (h << 13) | (h >> (32 - 13)); + h = h * 5 + 0xe6546b64; + } + + k = 0; + + // remainder + switch (len & 3) { // `len % 4' + case 3: + k ^= (tail[2] << 16); + case 2: + k ^= (tail[1] << 8); + case 1: + k ^= tail[0]; + k *= 0xcc9e2d51; + k = (k << 15) | (k >> (32 - 15)); + k *= 0x1b873593; + h ^= k; + } + + h ^= len; + + h ^= (h >> 16); + h *= 0x85ebca6b; + h ^= (h >> 13); + h *= 0xc2b2ae35; + h ^= (h >> 16); + + return h; +} + +//////////////////////////////////hash 函数测试/////////////////////////// +static ERL_NIF_TERM hash1(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { + ErlNifBinary Value; + if (!enif_term_to_binary(env, argv[0], &Value)) + return enif_make_badarg(env); + + uint32_t Salt; + if (!enif_get_uint(env, argv[1], &Salt)) { + return enif_make_badarg(env); + } + + Salt = fnv_hash(Value.data, Value.size, Salt); + enif_release_binary(&Value); + return ok; + +}; + +static ERL_NIF_TERM hash2(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { + ErlNifUInt64 Salt; + if (!enif_get_uint64(env, argv[1], &Salt)) + return enif_make_badarg(env); + + ErlNifBinary Value; + if (!enif_term_to_binary(env, argv[0], &Value)) + return enif_make_badarg(env); + enif_release_binary(&Value); + Salt = enif_hash(ERL_NIF_INTERNAL_HASH, argv[1], 2423432432); + return ok; +}; + +static ERL_NIF_TERM hash3(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { + ErlNifUInt64 Salt; + if (!enif_get_uint64(env, argv[1], &Salt)) + return enif_make_badarg(env); + + ErlNifBinary Value; + if (!enif_term_to_binary(env, argv[0], &Value)) + return enif_make_badarg(env); + Salt = murmurhash(Value.data, Value.size, 0); + enif_release_binary(&Value); + return ok; +}; + +static uint8_t compareChar1(const unsigned char *src, const unsigned char *dst, size_t Size) { + + // int j = 0; + // unsigned char *tsrc, *tdst; + // tsrc = src; + // tdst = dst; + // for(j = 0; j <= Size; j++) + // { + // enif_fprintf(stdout, "IMY************get9999111 src %d dst %d \n", *tsrc, *tdst); + // tsrc++; + // tdst++; + // } + uint8_t i = 0; + while (i < Size-1 && (*src == *dst)) { + // enif_fprintf(stdout, "IMY************get99992222 %d %d \n", *src, *dst); + src++; + dst++; + i++; + } + //enif_fprintf(stdout, "IMY************get9999while end %d %d \n", *src, *dst); + if (*src != *dst) { + //enif_fprintf(stdout, "IMY************get99993333 %d %d \n", *src, *dst); + return 1; + } else { + //enif_fprintf(stdout, "IMY************get99995555 %d %d \n", *src, *dst); + return 0; + } +} + +static uint8_t compareChar2(const unsigned char *src, const unsigned char *dst, size_t Size) { + const uint32_t *intSrc = NULL; + const uint32_t *intDst = NULL; + const uint8_t *tailSrc = NULL; // tail - last 8 bytes + const uint8_t *tailDst = NULL; // tail - last 8 bytes + int l = Size / 4; // chunk length + + intSrc = (const uint32_t *) src; // body + intDst = (const uint32_t *) dst; // body + tailSrc = (const uint8_t *) (src + l * 4); // last 8 byte chunk of `key' + tailDst = (const uint8_t *) (dst + l * 4); // last 8 byte chunk of `key' + + // for each 4 byte chunk of `key' + int i = 0; + for (i = 0; i < l; i++) { + if (intSrc[i] != intDst[i]) + return 1; + } + // remainder + switch (Size & 3) { + case 3: + if (tailSrc[0] != tailDst[0] || tailSrc[1] != tailDst[1] || tailSrc[2] != tailDst[2]) { + return 1; + } + break; + case 2: + if (tailSrc[0] != tailDst[0] || tailSrc[1] != tailDst[1]) { + return 1; + } + break; + case 1: + if (tailSrc[0] != tailDst[0]) { + return 1; + } + break; + } + return 0; +} + +static ERL_NIF_TERM compareBin1(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { + ErlNifBinary src, dst; + + if (!(enif_inspect_binary(env, argv[0], &src) && enif_inspect_binary(env, argv[1], &dst))) + return enif_make_badarg(env); + + if (src.size != dst.size) { + return enif_make_int(env, 1); + } + int Ret = compareChar1(src.data, dst.data, src.size); + return enif_make_int(env, Ret); +} + +static ERL_NIF_TERM compareBin2(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { + ErlNifBinary src, dst; + if (!(enif_inspect_binary(env, argv[0], &src) && enif_inspect_binary(env, argv[1], &dst))) + return enif_make_badarg(env); + + if (src.size != dst.size) { + return enif_make_int(env, 1); + } + int Ret = compareChar2(src.data, dst.data, src.size); + return enif_make_int(env, Ret); +} + +///////////////////////////////////////////////////////////////////// + +static int load(ErlNifEnv *env, void **priv, ERL_NIF_TERM load_info) { + ErlNifResourceFlags flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER; + ResType = enif_open_resource_type(env, NULL, "_nifHashb_", NULL, flags, NULL); + if (NULL == ResType) + return -1; + undefined = enif_make_atom(env, "undefined"); + ok = enif_make_atom(env, "ok"); + return 0; +} + +static ERL_NIF_TERM new(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { + hashb *ResPtr = (hashb *) enif_alloc_resource(ResType, sizeof(hashb)); + ResPtr->keySize = 0; + ResPtr->bblSize_1 = BBLSIZE_1; + ResPtr->bblSize_2 = BBLSIZE_2; + ResPtr->arrayPtr = enif_alloc(sizeof(bbl *) * BBLSIZE_1); + int i, j; + bbl NullBbl; + NullBbl.head = NULL; + for (i = 0; i < BBLSIZE_1; i++) { + ResPtr->arrayPtr[i] = enif_alloc(sizeof(bbl) * BBLSIZE_2); + for (j = 0; j < BBLSIZE_2; j++) { + ResPtr->arrayPtr[i][j] = NullBbl; + } + } + ERL_NIF_TERM Res = enif_make_resource(env, ResPtr); + enif_release_resource(ResPtr); + return Res; +} + +static uint8_t compareChar(const unsigned char *src, const unsigned char *dst, size_t Size) { + const uint32_t *intSrc = NULL; + const uint32_t *intDst = NULL; + const uint8_t *tailSrc = NULL; // tail - last 8 bytes + const uint8_t *tailDst = NULL; // tail - last 8 bytes + int l = Size / 4; // chunk length + + intSrc = (const uint32_t *) src; // body + intDst = (const uint32_t *) dst; // body + tailSrc = (const uint8_t *) (src + l * 4); // last 8 byte chunk of `key' + tailDst = (const uint8_t *) (dst + l * 4); // last 8 byte chunk of `key' + + // for each 4 byte chunk of `key' + int i = 0; + for (i = 0; i < l; i++) { + if (intSrc[i] != intDst[i]) + return 1; + } + // remainder + switch (Size & 3) { + case 3: + if (tailSrc[0] != tailDst[0] || tailSrc[1] != tailDst[1] || tailSrc[2] != tailDst[2]) { + return 1; + } + break; + case 2: + if (tailSrc[0] != tailDst[0] || tailSrc[1] != tailDst[1]) { + return 1; + } + break; + case 1: + if (tailSrc[0] != tailDst[0]) { + return 1; + } + break; + } + return 0; +} + +static uint8_t compareBin(ErlNifBinary src, ErlNifBinary dst) { + if (src.size != dst.size) { + //enif_fprintf(stdout, "IMY************get8888\n"); + return 1; + } + //enif_fprintf(stdout, "IMY************get8888111 %d \n", src.size); + return compareChar(src.data, dst.data, src.size); +} + +static ERL_NIF_TERM get(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { + hashb *ResPtr; + + if (!enif_get_resource(env, argv[0], ResType, (void **) &ResPtr)) { + return enif_make_badarg(env); + } + //enif_fprintf(stdout, "IMY************get1111\n"); + + ErlNifBinary getKey; + if (!enif_term_to_binary(env, argv[1], &getKey)) + return enif_make_badarg(env); + + //enif_fprintf(stdout, "IMY************get2222\n"); + uint32_t BblIndex_1, BblIndex_2; + BblIndex_1 = murmurhash(getKey.data, getKey.size, 131) % ResPtr->bblSize_1; + BblIndex_2 = murmurhash(getKey.data, getKey.size, 16777619) % ResPtr->bblSize_2; + //enif_fprintf(stdout, "IMY************get3333\n"); + if (NULL == ResPtr->arrayPtr[BblIndex_1][BblIndex_2].head) { + //enif_fprintf(stdout, "IMY************get4444\n"); + enif_release_binary(&getKey); + return undefined; + } else { + // enif_fprintf(stdout, "IMY************get55555\n"); + node *Head = ResPtr->arrayPtr[BblIndex_1][BblIndex_2].head; + while (Head) { + // enif_fprintf(stdout, "IMY************get7777\n"); + if (compareBin(getKey, Head->Key) == 0) { + ERL_NIF_TERM Ret; + enif_binary_to_term(env, Head->Value.data, Head->Value.size, &Ret, 0); + //enif_fprintf(stdout, "IMY************get5555\n"); + enif_release_binary(&getKey); + return Ret; + } + Head = Head->next; + } + //enif_fprintf(stdout, "IMY************get6666\n"); + enif_release_binary(&getKey); + return undefined; + } +} + +static ERL_NIF_TERM put(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { + hashb *ResPtr; + + if (!enif_get_resource(env, argv[0], ResType, (void **) &ResPtr)) { + return enif_make_badarg(env); + } + //enif_fprintf(stdout, "IMY************put111 \n"); + + ErlNifBinary Key, Value; + if (!enif_term_to_binary(env, argv[1], &Key)) + return enif_make_badarg(env); + + if (!enif_term_to_binary(env, argv[2], &Value)) + return enif_make_badarg(env); + + uint32_t BblIndex_1, BblIndex_2; + BblIndex_1 = murmurhash(Key.data, Key.size, 131) % ResPtr->bblSize_1; + BblIndex_2 = murmurhash(Key.data, Key.size, 16777619) % ResPtr->bblSize_2; + //enif_fprintf(stdout, "IMY************put222 %d %d \n", BblIndex_1, BblIndex_2); + + //enif_fprintf(stdout, "IMY************put3333 \n"); + if (NULL == ResPtr->arrayPtr[BblIndex_1][BblIndex_2].head) { + node *NewNode = enif_alloc(sizeof(node)); + NewNode->Key = Key; + NewNode->Value = Value; + NewNode->next = NULL; + ResPtr->arrayPtr[BblIndex_1][BblIndex_2].head = NewNode; + ResPtr->keySize = ResPtr->keySize + 1; + //enif_fprintf(stdout, "IMY************put4444 \n"); + return ok; + } else { + node *Head = ResPtr->arrayPtr[BblIndex_1][BblIndex_2].head; + while (Head) { + if (compareBin(Key, Head->Key) == 0) { + //ERL_NIF_TERM Ret; + ErlNifBinary OldValue = Head->Value; + //enif_binary_to_term(env, OldValue.data, OldValue.size, &Ret, 0); + Head->Value = Value; + enif_release_binary(&OldValue); + enif_release_binary(&Key); + //enif_fprintf(stdout, "IMY************put55555 \n"); + //return Ret; + return ok; + } + Head = Head->next; + } + node *NewNode = enif_alloc(sizeof(node)); + NewNode->Key = Key; + NewNode->Value = Value; + NewNode->next = ResPtr->arrayPtr[BblIndex_1][BblIndex_2].head; + ResPtr->arrayPtr[BblIndex_1][BblIndex_2].head = NewNode; + ResPtr->keySize = ResPtr->keySize + 1; + //enif_fprintf(stdout, "IMY************put6666\n"); + return ok; + } +} +///////////////////////////////////////////////////////////////////////// + +static ErlNifFunc nifFuns[] = { + {"new", 0, new}, + {"get", 2, get}, + {"put", 3, put}, + {"hash1", 2, hash1}, + {"hash2", 2, hash2}, + {"hash3", 2, hash3}, + {"compareBin1", 2, compareBin1}, + {"compareBin2", 2, compareBin2}, +}; + +ERL_NIF_INIT(nifHashb, nifFuns, +&load, NULL, NULL, NULL) + + diff --git a/c_src/nifHashb/rebar.config b/c_src/nifHashb/rebar.config new file mode 100644 index 0000000..4918a3b --- /dev/null +++ b/c_src/nifHashb/rebar.config @@ -0,0 +1,7 @@ +{port_specs, [ + {"../../priv/nifHashb.so", ["*.c"]} +]}. + + + + diff --git a/priv/binary_tools.dll b/priv/binary_tools.dll index d736e04..ce0fce1 100644 Binary files a/priv/binary_tools.dll and b/priv/binary_tools.dll differ diff --git a/priv/bitmap_filter.dll b/priv/bitmap_filter.dll index f32be19..79ce94f 100644 Binary files a/priv/bitmap_filter.dll and b/priv/bitmap_filter.dll differ diff --git a/priv/bsn_ext.dll b/priv/bsn_ext.dll index 3d9d53a..c1179f5 100644 Binary files a/priv/bsn_ext.dll and b/priv/bsn_ext.dll differ diff --git a/priv/bsn_int.dll b/priv/bsn_int.dll index 06c091c..b1b7515 100644 Binary files a/priv/bsn_int.dll and b/priv/bsn_int.dll differ diff --git a/priv/btreelru_nif.dll b/priv/btreelru_nif.dll index 1ee183b..7049a76 100644 Binary files a/priv/btreelru_nif.dll and b/priv/btreelru_nif.dll differ diff --git a/priv/cbase64.dll b/priv/cbase64.dll index fc8bab6..c154930 100644 Binary files a/priv/cbase64.dll and b/priv/cbase64.dll differ diff --git a/priv/enlfq.dll b/priv/enlfq.dll index e1b70e1..fb5c217 100644 Binary files a/priv/enlfq.dll and b/priv/enlfq.dll differ diff --git a/priv/epqueue_nif.dll b/priv/epqueue_nif.dll index 62a1721..970657a 100644 Binary files a/priv/epqueue_nif.dll and b/priv/epqueue_nif.dll differ diff --git a/priv/etsq.dll b/priv/etsq.dll index 51103e2..5a34bbe 100644 Binary files a/priv/etsq.dll and b/priv/etsq.dll differ diff --git a/priv/hqueue.dll b/priv/hqueue.dll index 70fca44..ea3eed5 100644 Binary files a/priv/hqueue.dll and b/priv/hqueue.dll differ diff --git a/priv/khash.dll b/priv/khash.dll new file mode 100644 index 0000000..5a40b21 Binary files /dev/null and b/priv/khash.dll differ diff --git a/priv/khash.exp b/priv/khash.exp new file mode 100644 index 0000000..083af0e Binary files /dev/null and b/priv/khash.exp differ diff --git a/priv/khash.lib b/priv/khash.lib new file mode 100644 index 0000000..31eb33d Binary files /dev/null and b/priv/khash.lib differ diff --git a/priv/khash.so b/priv/khash.so new file mode 100644 index 0000000..aba2b0b Binary files /dev/null and b/priv/khash.so differ diff --git a/priv/khash2.dll b/priv/khash2.dll new file mode 100644 index 0000000..53955ea Binary files /dev/null and b/priv/khash2.dll differ diff --git a/priv/khash2.exp b/priv/khash2.exp new file mode 100644 index 0000000..344154a Binary files /dev/null and b/priv/khash2.exp differ diff --git a/priv/khash2.lib b/priv/khash2.lib new file mode 100644 index 0000000..d7b7c1b Binary files /dev/null and b/priv/khash2.lib differ diff --git a/priv/khash2.so b/priv/khash2.so new file mode 100644 index 0000000..2ef6d34 Binary files /dev/null and b/priv/khash2.so differ diff --git a/priv/native_array_nif.dll b/priv/native_array_nif.dll index f696ec6..b576cd4 100644 Binary files a/priv/native_array_nif.dll and b/priv/native_array_nif.dll differ diff --git a/priv/neural.dll b/priv/neural.dll index b0154e0..3b9054f 100644 Binary files a/priv/neural.dll and b/priv/neural.dll differ diff --git a/priv/nifArray.dll b/priv/nifArray.dll new file mode 100644 index 0000000..c11259a Binary files /dev/null and b/priv/nifArray.dll differ diff --git a/priv/nifArray.exp b/priv/nifArray.exp new file mode 100644 index 0000000..1f3abb4 Binary files /dev/null and b/priv/nifArray.exp differ diff --git a/priv/nifArray.lib b/priv/nifArray.lib new file mode 100644 index 0000000..429e791 Binary files /dev/null and b/priv/nifArray.lib differ diff --git a/priv/nifArray.so b/priv/nifArray.so new file mode 100644 index 0000000..e516d98 Binary files /dev/null and b/priv/nifArray.so differ diff --git a/priv/nifHashb.so b/priv/nifHashb.so new file mode 100644 index 0000000..da07150 Binary files /dev/null and b/priv/nifHashb.so differ diff --git a/priv/nif_skiplist.dll b/priv/nif_skiplist.dll index 61d1b06..f6fb207 100644 Binary files a/priv/nif_skiplist.dll and b/priv/nif_skiplist.dll differ diff --git a/src/dataType/utGenTerm.erl b/src/dataType/utGenTerm.erl index e0e4d33..98cbb6b 100644 --- a/src/dataType/utGenTerm.erl +++ b/src/dataType/utGenTerm.erl @@ -15,7 +15,7 @@ genString/1, genBinary/1, genBitstring/1, - genBignum/1, + genBigNum/1, genFunction/1 ]). @@ -23,10 +23,10 @@ any() -> any(16). any(MaxSize) when MaxSize =< 0 -> - Fun = choice(value_types()), + Fun = choice(valueTypes()), ?MODULE:Fun(MaxSize); any(MaxSize) -> - Fun = choice(all_types()), + Fun = choice(allTypes()), ?MODULE:Fun(MaxSize). genAtom(MaxSize) -> @@ -67,10 +67,13 @@ genShortString(_) -> Size = rand:uniform(255), [rand:uniform(127) || _ <- lists:seq(1, Size)]. -genString(_) -> +genString() -> Size = rand:uniform(4096), [rand:uniform(127) || _ <- lists:seq(1, Size)]. +genString(MaxSize) -> + [rand:uniform(255) || _ <- lists:seq(1, MaxSize)]. + genBinary(MaxSize) -> list_to_binary(genString(MaxSize)). @@ -78,30 +81,30 @@ genBitstring(MaxSize) -> B = genBinary(MaxSize), <<2:4/integer, B/binary>>. -genBignum(_) -> +genBigNum(_) -> 16#FFFFFFFFFFFFFFFF + rand:uniform(16#FFFFFFFF). genFunction(_) -> - choice(all_types()). + choice(allTypes()). choice(Options) -> lists:nth(rand:uniform(length(Options)), Options). -value_types() -> +valueTypes() -> [ - gen_atom, - gen_integer, - gen_float, - gen_reference, - gen_port, - gen_pid, - gen_short_string, - gen_string, - gen_binary, - gen_bitstring, - gen_bignum, - gen_function + genAtom, + genInteger, + genFloat, + genReference, + genPort, + genPid, + genShortString, + genString, + genBinary, + genBitstring, + genBigNum, + genFunction ]. -all_types() -> - value_types() ++ [gen_tuple, gen_list]. \ No newline at end of file +allTypes() -> + valueTypes() ++ [genTuple, genList]. \ No newline at end of file diff --git a/src/nifSrc/couchdb-khash/khash.erl b/src/nifSrc/couchdb-khash/khash.erl new file mode 100644 index 0000000..daaf5ea --- /dev/null +++ b/src/nifSrc/couchdb-khash/khash.erl @@ -0,0 +1,157 @@ +%% This file is part of khash released under the MIT license. +%% See the LICENSE file for more information. +%% Copyright 2013 Cloudant, Inc + +-module(khash). +-on_load(init/0). + + +-export([ + new/0, + new/1, + from_list/1, + from_list/2, + to_list/1, + clear/1, + lookup/2, + get/2, + get/3, + put/3, + del/2, + size/1, + iter/1, + iter_next/1, + fold/3 +]). + + +-define(NOT_LOADED, not_loaded(?LINE)). + + +-type kv() :: {any(), any()}. +-type khash() :: term(). +-type khash_iter() :: term(). +-type option() :: []. + + +-spec new() -> {ok, khash()}. +new() -> + new([]). + + +-spec new([option()]) -> {ok, khash()}. +new(_Options) -> + ?NOT_LOADED. + + +-spec from_list([kv()]) -> {ok, khash()}. +from_list(KVList) -> + from_list(KVList, []). + + +-spec from_list([kv()], [option()]) -> {ok, khash()}. +from_list(KVList, Options) -> + {ok, Hash} = ?MODULE:new(Options), + lists:foreach(fun({Key, Val}) -> + ?MODULE:put(Hash, Key, Val) + end, KVList), + {ok, Hash}. + + +-spec to_list(khash()) -> [kv()]. +to_list(_Hash) -> + ?NOT_LOADED. + + +-spec clear(khash()) -> ok. +clear(_Hash) -> + ?NOT_LOADED. + + +-spec lookup(khash(), any()) -> {value, any()} | not_found. +lookup(Hash, Key) -> + lookup_int(Hash, erlang:phash2(Key), Key). + + +-spec get(khash(), any()) -> any(). +get(Hash, Key) -> + get(Hash, Key, undefined). + + +-spec get(khash(), any(), any()) -> any(). +get(Hash, Key, Default) -> + get_int(Hash, erlang:phash2(Key), Key, Default). + + +-spec put(khash(), any(), any()) -> ok. +put(Hash, Key, Value) -> + put_int(Hash, erlang:phash2(Key), Key, Value). + + +-spec del(khash(), any()) -> ok. +del(Hash, Key) -> + del_int(Hash, erlang:phash2(Key), Key). + + +-spec size(khash()) -> non_neg_integer(). +size(_Hash) -> + ?NOT_LOADED. + + +-spec iter(khash()) -> {ok, khash_iter()}. +iter(_Hash) -> + ?NOT_LOADED. + + +-spec iter_next(khash_iter()) -> + kv() | end_of_table | {error, expired_iterator}. +iter_next(_Iter) -> + ?NOT_LOADED. + + +-spec fold(khash(), fun(), any()) -> any(). +fold(Hash, FoldFun, Acc) -> + {ok, Iter} = ?MODULE:iter(Hash), + fold_int(Iter, FoldFun, Acc). + + +fold_int(Iter, FoldFun, Acc) -> + case ?MODULE:iter_next(Iter) of + {Key, Value} -> + NewAcc = FoldFun(Key, Value, Acc), + fold_int(Iter, FoldFun, NewAcc); + end_of_table -> + Acc + end. + + +init() -> + PrivDir = case code:priv_dir(?MODULE) of + {error, _} -> + EbinDir = filename:dirname(code:which(?MODULE)), + AppPath = filename:dirname(EbinDir), + filename:join(AppPath, "priv"); + Path -> + Path + end, + erlang:load_nif(filename:join(PrivDir, "khash"), 0). + + +lookup_int(_Hash, _HashValue, _Key) -> + ?NOT_LOADED. + + +get_int(_Hash, _HashValue, _Key, _Default) -> + ?NOT_LOADED. + + +put_int(_Hash, _HashValue, _Key, _Value) -> + ?NOT_LOADED. + + +del_int(_Hash, _HashValue, _Key) -> + ?NOT_LOADED. + + +not_loaded(Line) -> + erlang:nif_error({not_loaded, [{module, ?MODULE}, {line, Line}]}). diff --git a/src/nifSrc/nifArray/nifArray.erl b/src/nifSrc/nifArray/nifArray.erl new file mode 100644 index 0000000..195a94d --- /dev/null +++ b/src/nifSrc/nifArray/nifArray.erl @@ -0,0 +1,49 @@ +-module(nifArray). + +-on_load(init/0). + +-export([ + new/1 + , get/2 + , put/3 + , test/1 + , test1/1 +]). + +-type nifArray() :: reference(). + +init() -> + SoName = + case code:priv_dir(?MODULE) of + {error, _} -> + case code:which(?MODULE) of + Filename when is_list(Filename) -> + filename:join([filename:dirname(Filename), "../priv", "nifArray"]); + _ -> + filename:join("../priv", "nifArray") + end; + Dir -> + filename:join(Dir, "nifArray") + end, + erlang:load_nif(SoName, 0). + +-spec new(Size :: integer()) -> nifArray(). +new(_Size) -> + erlang:nif_error({nif_not_loaded, module, ?MODULE, line, ?LINE}). + +-spec get(Ref :: nifArray(), Index :: integer()) -> term(). +get(_Ref, _Index) -> + erlang:nif_error({nif_not_loaded, module, ?MODULE, line, ?LINE}). + +-spec put(Ref :: nifArray(), Index :: integer(), Value :: term()) -> term(). +put(_Ref, _Index, _Value) -> + erlang:nif_error({nif_not_loaded, module, ?MODULE, line, ?LINE}). + +test(Value) -> + erlang:nif_error({nif_not_loaded, module, ?MODULE, line, ?LINE}). + +test1(Value) -> + Bin = term_to_binary(Value), + Term = binary_to_term(Bin). + + diff --git a/src/nifSrc/nifHashb/nifHashb.erl b/src/nifSrc/nifHashb/nifHashb.erl new file mode 100644 index 0000000..9cd33ca --- /dev/null +++ b/src/nifSrc/nifHashb/nifHashb.erl @@ -0,0 +1,77 @@ +-module(nifHashb). + +-on_load(init/0). + +-export([ + new/0 + , get/2 + , put/3 + , hash1/2 + , hash2/2 + , hash3/2 + , cb1/2 + , cb2/2 + , compareBin1/2 + , compareBin2/2 +]). + +init() -> + SoName = + case code:priv_dir(?MODULE) of + {error, _} -> + case code:which(?MODULE) of + Filename when is_list(Filename) -> + filename:join([filename:dirname(Filename), "../priv", "nifHashb"]); + _ -> + filename:join("../priv", "nifHashb") + end; + Dir -> + filename:join(Dir, "nifHashb") + end, + erlang:load_nif(SoName, 0). + +new() -> + erlang:nif_error({nif_not_loaded, module, ?MODULE, line, ?LINE}). + +get(Ref, Key) -> + KeyBin = erlang:term_to_binary(Key), + Hash1 = erlang:phash2(KeyBin), + Hash2 = erlang:phash2(KeyBin, 123211111), + get(Ref, Hash1, Hash2, KeyBin). + +get(Ref, Hash1, Hash2, KeyBin) -> + erlang:nif_error({nif_not_loaded, module, ?MODULE, line, ?LINE}). + +put(Ref, Key, Value) -> + KeyBin = erlang:term_to_binary(Key), + ValueBin = erlang:term_to_binary(Value), + Hash1 = erlang:phash2(KeyBin), + Hash2 = erlang:phash2(KeyBin, 123211111), + put(Ref, Hash1, Hash2, KeyBin, ValueBin). + +put(Ref, Hash1, Hash2, Key, Value) -> + erlang:nif_error({nif_not_loaded, module, ?MODULE, line, ?LINE}). + +hash1(Term, Range) -> + erlang:nif_error({nif_not_loaded, module, ?MODULE, line, ?LINE}). + +hash2(Term, Range) -> + erlang:nif_error({nif_not_loaded, module, ?MODULE, line, ?LINE}). + +hash3(Term, Range) -> + erlang:nif_error({nif_not_loaded, module, ?MODULE, line, ?LINE}). + +cb1(Term1, Term2) -> + compareBin1(term_to_binary(Term1), term_to_binary(Term2)). + +cb2(Term1, Term2) -> + compareBin2(term_to_binary(Term1), term_to_binary(Term2)). + +compareBin1(Bin1, Bin2) -> + erlang:nif_error({nif_not_loaded, module, ?MODULE, line, ?LINE}). + +compareBin2(Bin1, Bin2) -> + erlang:nif_error({nif_not_loaded, module, ?MODULE, line, ?LINE}). + + + diff --git a/src/nifSrc/nifHashb/nifHashbbak.erl b/src/nifSrc/nifHashb/nifHashbbak.erl new file mode 100644 index 0000000..a16622f --- /dev/null +++ b/src/nifSrc/nifHashb/nifHashbbak.erl @@ -0,0 +1,64 @@ +-module(nifHashbbak). + +-on_load(init/0). + +-export([ + new/0 + , get/2 + , put/3 + , hash1/2 + , hash2/2 + , hash3/2 + , cb1/2 + , cb2/2 + , compareBin1/2 + , compareBin2/2 +]). + +init() -> + SoName = + case code:priv_dir(?MODULE) of + {error, _} -> + case code:which(?MODULE) of + Filename when is_list(Filename) -> + filename:join([filename:dirname(Filename), "../priv", "nifHashb"]); + _ -> + filename:join("../priv", "nifHashb") + end; + Dir -> + filename:join(Dir, "nifHashb") + end, + erlang:load_nif(SoName, 0). + +new() -> + erlang:nif_error({nif_not_loaded, module, ?MODULE, line, ?LINE}). + +get(Ref, Key) -> + erlang:nif_error({nif_not_loaded, module, ?MODULE, line, ?LINE}). + +put(Ref, Key, Value) -> + erlang:nif_error({nif_not_loaded, module, ?MODULE, line, ?LINE}). + +hash1(Term, Range) -> + erlang:nif_error({nif_not_loaded, module, ?MODULE, line, ?LINE}). + +hash2(Term, Range) -> + erlang:nif_error({nif_not_loaded, module, ?MODULE, line, ?LINE}). + +hash3(Term, Range) -> + erlang:nif_error({nif_not_loaded, module, ?MODULE, line, ?LINE}). + +cb1(Term1, Term2) -> + compareBin1(term_to_binary(Term1), term_to_binary(Term2)). + +cb2(Term1, Term2) -> + compareBin2(term_to_binary(Term1), term_to_binary(Term2)). + +compareBin1(Bin1, Bin2) -> + erlang:nif_error({nif_not_loaded, module, ?MODULE, line, ?LINE}). + +compareBin2(Bin1, Bin2) -> + erlang:nif_error({nif_not_loaded, module, ?MODULE, line, ?LINE}). + + + diff --git a/src/testCase/DsTest/utKhashDs.erl b/src/testCase/DsTest/utKhashDs.erl new file mode 100644 index 0000000..288f93a --- /dev/null +++ b/src/testCase/DsTest/utKhashDs.erl @@ -0,0 +1,67 @@ +-module(utKhashDs). +-compile([nowarn_unused_function, nowarn_unused_vars, nowarn_export_all]). + +-export([start/2]). + +start(Num, Pid) -> + Ds = init(Num), + Time1 = erlang:system_time(nanosecond), + NewDsI = insert(Num, Ds), + Time2 = erlang:system_time(nanosecond), + NewDsR = read(Num, NewDsI, undefined), + Time3 = erlang:system_time(nanosecond), + NewDsU = update(Num, NewDsR), + Time4 = erlang:system_time(nanosecond), + NewDsF = for(Num, NewDsU), + Time5 = erlang:system_time(nanosecond), + delete(Num, NewDsF), + Time6 = erlang:system_time(nanosecond), + erlang:send(Pid, {over, self(), Time2 - Time1, Time3 - Time2, Time4 - Time3, Time5 - Time4, Time6 - Time5}), + exit(normal). + +init(Num) -> + {ok, Ds} = khash:new(), + Ds. + +insert(0, Ds) -> + Ds; +insert(Num, Ds) -> + Key = utTestDs:makeK(Num), + Value = utTestDs:makeV(Num), + khash:put(Ds, Key, Value), + insert(Num - 1, Ds). + +read(0, Ds, _V) -> + Ds; +read(Num, Ds, _V) -> + Key = utTestDs:makeK(Num), + Value = khash:get(Ds, Key, undefined), + read(Num - 1, Ds, Value). + +update(0, Ds) -> + Ds; +update(Num, Ds) -> + Key = utTestDs:makeK(Num), + Value = utTestDs:makeV2(Num), + khash:put(Ds, Key, Value), + update(Num - 1, Ds). + +for(Num, Ds) -> + Keylist = khash:to_list(Ds), + for1(Keylist, undefined), + Ds. + +for1([], V) -> + ok; +for1([H | T], _V) -> + V = erlang:get(H), + for1(T, V). + +delete(0, Ds) -> + ok; +delete(Num, Ds) -> + Key = utTestDs:makeK(Num), + khash:del(Ds, Key), + delete(Num - 1, Ds). + + diff --git a/src/testCase/DsTest/utNifArrayDs.erl b/src/testCase/DsTest/utNifArrayDs.erl new file mode 100644 index 0000000..cc76b92 --- /dev/null +++ b/src/testCase/DsTest/utNifArrayDs.erl @@ -0,0 +1,60 @@ +-module(utNifArrayDs). +-compile([nowarn_unused_function, nowarn_unused_vars, nowarn_export_all]). + +-export([start/2]). + +start(Num, Pid) -> + Ds = init(Num), + Time1 = erlang:system_time(nanosecond), + NewDsI = insert(Num - 1, Ds), + Time2 = erlang:system_time(nanosecond), + NewDsR = read(Num - 1, Ds), + Time3 = erlang:system_time(nanosecond), + NewDsU = update(Num - 1, Ds), + Time4 = erlang:system_time(nanosecond), + NewDsF = for(Num - 1, Ds), + Time5 = erlang:system_time(nanosecond), + delete(Num - 1, Ds), + Time6 = erlang:system_time(nanosecond), + erlang:send(Pid, {over, self(), Time2 - Time1, Time3 - Time2, Time4 - Time3, Time5 - Time4, not_support}), + exit(normal). + +init(Num) -> + nifArray:new(Num). + +insert(0, Ds) -> + % Key = utTestDs:makeK(0), + nifArray:put(Ds, 0, utTestDs:makeV(0)); +insert(Num, Ds) -> + % Key = utTestDs:makeK(Num), + nifArray:put(Ds, Num, utTestDs:makeV(Num)), + insert(Num - 1, Ds). + +read(0, Ds) -> + % Key = utTestDs:makeK(0), + Value = nifArray:get(Ds, 0), + Ds; +read(Num, Ds) -> + % Key = utTestDs:makeK(Num), + Value = nifArray:get(Ds, Num), + read(Num - 1, Ds). + +update(0, Ds) -> + % Key = utTestDs:makeK(0), + nifArray:put(Ds, 0, utTestDs:makeV2(0)); +update(Num, Ds) -> + % Key = utTestDs:makeK(Num), + nifArray:put(Ds, Num, utTestDs:makeV2(Num)), + update(Num - 1, Ds). + +for(0, Ds) -> + Value = nifArray:get(Ds, 0), + Ds; +for(Num, Ds) -> + Value = nifArray:get(Ds, Num), + for(Num - 1, Ds). + +delete(Num, Ds) -> + ok. + + diff --git a/src/testCase/DsTest/utNifHashbDs.erl b/src/testCase/DsTest/utNifHashbDs.erl new file mode 100644 index 0000000..5217f08 --- /dev/null +++ b/src/testCase/DsTest/utNifHashbDs.erl @@ -0,0 +1,55 @@ +-module(utNifHashbDs). +-compile([nowarn_unused_function, nowarn_unused_vars, nowarn_export_all]). + +-export([start/2]). + +start(Num, Pid) -> + Ds = init(Num), + Time1 = erlang:system_time(nanosecond), + NewDsI = insert(Num, Ds), + Time2 = erlang:system_time(nanosecond), + NewDsR = read(Num, Ds, undefined), + Time3 = erlang:system_time(nanosecond), + NewDsU = update(Num, Ds), + Time4 = erlang:system_time(nanosecond), + NewDsF = for(Num, Ds), + Time5 = erlang:system_time(nanosecond), + delete(Num, Ds), + Time6 = erlang:system_time(nanosecond), + erlang:send(Pid, {over, self(), Time2 - Time1, Time3 - Time2, Time4 - Time3, Time5 - Time4, Time6 - Time5}), + exit(normal). + +init(Num) -> + nifHashb:new(). + +insert(0, Ds) -> + Ds; +insert(Num, Ds) -> + Key = utTestDs:makeK(Num), + Value = utTestDs:makeV(Num), + nifHashb:put(Ds, Key, Value), + insert(Num - 1, Ds). + +read(0, Ds, _V) -> + Ds; +read(Num, Ds, _V) -> + Key = utTestDs:makeK(Num), + Value = nifHashb:get(Ds, Key), + read(Num - 1, Ds, Value). + +update(0, Ds) -> + Ds; +update(Num, Ds) -> + Key = utTestDs:makeK(Num), + Value = utTestDs:makeV2(Num), + nifHashb:put(Ds, Key, Value), + update(Num - 1, Ds). + +for(Num, Ds) -> + Ds. + + +delete(Num, Ds) -> + ok. + + diff --git a/src/testCase/DsTest/utTestDs.erl b/src/testCase/DsTest/utTestDs.erl index 379dbf7..87b7daf 100644 --- a/src/testCase/DsTest/utTestDs.erl +++ b/src/testCase/DsTest/utTestDs.erl @@ -9,10 +9,10 @@ }). %-define(V_NUM, [8, 16, 32, 64, 128, 256, 516, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 524288, 1048576]). --define(V_NUM, [8, 16, 32, 64, 128, 256, 516, 1024, 2048, 4096, 8192, 16384, 32768]). --define(DsList, [utPdDs, utArrayDs, utTupleDs, utListsDs, utMapsDs, utEtsSetDs, utEtsOrdDs, utDictDs, utGb_treesDs, utSetsDs, utGb_setsDs, utOrddictDs, utOrdsetsDs, utAtomicsDs, utPTermDs, utArrayDs1, utHashBblDs, utHashBblDs1]). -%-define(DsList, [utPdDs, utArrayDs, utTupleDs, utListsDs, utMapsDs, utEtsSetDs, utArrayDs1, utHashBblDs, utHashBblDs1]). -%-define(DsList, [utMapsDs, utArrayDs1, utHashBblDs]). +-define(V_NUM, [8, 16, 32, 64, 128, 256, 516, 1024, 2048, 4096, 8192, 16384]). +%-define(DsList, [utPdDs, utArrayDs, utTupleDs, utListsDs, utMapsDs, utEtsSetDs, utEtsOrdDs, utDictDs, utGb_treesDs, utSetsDs, utGb_setsDs, utOrddictDs, utOrdsetsDs, utAtomicsDs, utPTermDs, utArrayDs1, utHashBblDs, utHashBblDs1]). +%-define(DsList, [utPdDs, utArrayDs, utNifArrayDs, utTupleDs, utListsDs, utMapsDs, utEtsSetDs, utArrayDs1, utHashBblDs, utHashBblDs1, utKhashDs]). +-define(DsList, [utPdDs, utArrayDs, utNifArrayDs, utNifHashbDs, utKhashDs, utEtsSetDs, utTupleDs, utMapsDs]). -define(Cnt, 12). @@ -178,3 +178,6 @@ makeV2(N) -> {N, <<"test-testDs">>} end. +makeRandV() -> + utGenTerm:any(). + diff --git a/src/testCase/utTestPerformance.erl b/src/testCase/utTestPerformance.erl index 1f56e5e..e0e7871 100644 --- a/src/testCase/utTestPerformance.erl +++ b/src/testCase/utTestPerformance.erl @@ -236,4 +236,47 @@ hash1(Term) -> hash2(Term) -> erlang:phash2(Term, 256). +hashn1(Term) -> + nifHashb:hash1(Term, 256). + +hashn2(Term) -> + nifHashb:hash2(Term, 256). + +hashn3(Term) -> + nifHashb:hash3(Term, 256). + +ht1(0, _Fun, Term) -> + ok; +ht1(N, Fun, Term) -> + ?MODULE:Fun(Term), + ht1(N - 1, Fun, Term). + +hash3(Term) -> + erlang:phash(Term, 256). + +hash4(Term) -> + erlang:phash2(Term, 256). + +ttT(0, Fun) -> + ?MODULE:Fun(0); +ttT(N, Fun) -> + ?MODULE:Fun(N), + ttT(N - 1, Fun). + +nifT(N) -> + [nifArray:test(Term) || Term <- ?List]. + +nifT1(N) -> + [nifArray:test1(Term) || Term <- ?List]. + +cb(N, Len, Fun) -> + Bin = utGenTerm:genBinary(Len), + cddo(N, Bin, Fun). + +cddo(0, Bin, Fun) -> + nifHashb:Fun(Bin, Bin); +cddo(N, Bin, Fun) -> + nifHashb:Fun(Bin, Bin), + cddo(N - 1, Bin, Fun). + diff --git a/src/testCase/utTestTime.erl b/src/testCase/utTestTime.erl index d7a4d46..bc5c484 100644 --- a/src/testCase/utTestTime.erl +++ b/src/testCase/utTestTime.erl @@ -267,7 +267,7 @@ tk2() -> os:system_time(microsecond). tk3() -> - erlang:system_time(nanosecond). + erlang:system_time(nFanosecond). tk4() -> make_ref().