From 6340783e80bd7955bb22929e6cbd648d0ba53b2d Mon Sep 17 00:00:00 2001 From: SisMaker <1713699517@qq.com> Date: Fri, 6 Mar 2020 23:10:25 +0800 Subject: [PATCH] =?UTF-8?q?nif=E9=83=A8=E5=88=86=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- c_src/{mqtree => .mqtree}/mqtree.c | 0 c_src/{mqtree => .mqtree}/rebar.config | 0 c_src/{mqtree => .mqtree}/uthash.h | 0 c_src/couchdb-khash/hash.c | 843 +++++++++++++++++++++++++ c_src/couchdb-khash/hash.h | 240 +++++++ c_src/couchdb-khash/khash.c | 658 +++++++++++++++++++ c_src/couchdb-khash/rebar.config | 12 + c_src/couchdb-khash2/hash.c | 843 +++++++++++++++++++++++++ c_src/couchdb-khash2/hash.h | 240 +++++++ c_src/couchdb-khash2/khash.c | 658 +++++++++++++++++++ c_src/couchdb-khash2/rebar.config | 12 + c_src/nifArray/nifArray.bak | 110 ++++ c_src/nifArray/nifArray.c | 108 ++++ c_src/nifArray/rebar.config | 7 + c_src/nifHashb/nifHashb.c | 455 +++++++++++++ c_src/nifHashb/nifHashb.cbak | 435 +++++++++++++ c_src/nifHashb/rebar.config | 7 + priv/binary_tools.dll | Bin 114176 -> 114176 bytes priv/bitmap_filter.dll | Bin 91136 -> 91136 bytes priv/bsn_ext.dll | Bin 95744 -> 95744 bytes priv/bsn_int.dll | Bin 93184 -> 93184 bytes priv/btreelru_nif.dll | Bin 164352 -> 164352 bytes priv/cbase64.dll | Bin 93696 -> 93696 bytes priv/enlfq.dll | Bin 152064 -> 152064 bytes priv/epqueue_nif.dll | Bin 101376 -> 101376 bytes priv/etsq.dll | Bin 111616 -> 111616 bytes priv/hqueue.dll | Bin 96256 -> 96256 bytes priv/khash.dll | Bin 0 -> 96768 bytes priv/khash.exp | Bin 0 -> 686 bytes priv/khash.lib | Bin 0 -> 1678 bytes priv/khash.so | Bin 0 -> 72088 bytes priv/khash2.dll | Bin 0 -> 96768 bytes priv/khash2.exp | Bin 0 -> 688 bytes priv/khash2.lib | Bin 0 -> 1690 bytes priv/khash2.so | Bin 0 -> 72096 bytes priv/native_array_nif.dll | Bin 91648 -> 91648 bytes priv/neural.dll | Bin 165376 -> 165376 bytes priv/nifArray.dll | Bin 0 -> 91648 bytes priv/nifArray.exp | Bin 0 -> 686 bytes priv/nifArray.lib | Bin 0 -> 1714 bytes priv/nifArray.so | Bin 0 -> 18032 bytes priv/nifHashb.so | Bin 0 -> 25768 bytes priv/nif_skiplist.dll | Bin 126976 -> 126976 bytes src/dataType/utGenTerm.erl | 45 +- src/nifSrc/couchdb-khash/khash.erl | 157 +++++ src/nifSrc/nifArray/nifArray.erl | 49 ++ src/nifSrc/nifHashb/nifHashb.erl | 77 +++ src/nifSrc/nifHashb/nifHashbbak.erl | 64 ++ src/testCase/DsTest/utKhashDs.erl | 67 ++ src/testCase/DsTest/utNifArrayDs.erl | 60 ++ src/testCase/DsTest/utNifHashbDs.erl | 55 ++ src/testCase/DsTest/utTestDs.erl | 11 +- src/testCase/utTestPerformance.erl | 43 ++ src/testCase/utTestTime.erl | 2 +- 54 files changed, 5232 insertions(+), 26 deletions(-) rename c_src/{mqtree => .mqtree}/mqtree.c (100%) rename c_src/{mqtree => .mqtree}/rebar.config (100%) rename c_src/{mqtree => .mqtree}/uthash.h (100%) create mode 100644 c_src/couchdb-khash/hash.c create mode 100644 c_src/couchdb-khash/hash.h create mode 100644 c_src/couchdb-khash/khash.c create mode 100644 c_src/couchdb-khash/rebar.config create mode 100644 c_src/couchdb-khash2/hash.c create mode 100644 c_src/couchdb-khash2/hash.h create mode 100644 c_src/couchdb-khash2/khash.c create mode 100644 c_src/couchdb-khash2/rebar.config create mode 100644 c_src/nifArray/nifArray.bak create mode 100644 c_src/nifArray/nifArray.c create mode 100644 c_src/nifArray/rebar.config create mode 100644 c_src/nifHashb/nifHashb.c create mode 100644 c_src/nifHashb/nifHashb.cbak create mode 100644 c_src/nifHashb/rebar.config create mode 100644 priv/khash.dll create mode 100644 priv/khash.exp create mode 100644 priv/khash.lib create mode 100644 priv/khash.so create mode 100644 priv/khash2.dll create mode 100644 priv/khash2.exp create mode 100644 priv/khash2.lib create mode 100644 priv/khash2.so create mode 100644 priv/nifArray.dll create mode 100644 priv/nifArray.exp create mode 100644 priv/nifArray.lib create mode 100644 priv/nifArray.so create mode 100644 priv/nifHashb.so create mode 100644 src/nifSrc/couchdb-khash/khash.erl create mode 100644 src/nifSrc/nifArray/nifArray.erl create mode 100644 src/nifSrc/nifHashb/nifHashb.erl create mode 100644 src/nifSrc/nifHashb/nifHashbbak.erl create mode 100644 src/testCase/DsTest/utKhashDs.erl create mode 100644 src/testCase/DsTest/utNifArrayDs.erl create mode 100644 src/testCase/DsTest/utNifHashbDs.erl 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 d736e0447fb61859c5942cec1b1110bd38b5ecb3..ce0fce177bacd866f47cbdbd0b050c7110184e8c 100644 GIT binary patch delta 32 lcmZqp!`ASJZG!+K^YM^`W?{zd!i6(n0Rsga%3PAt> delta 32 lcmZoT!P;86aC4gkA+3abDB delta 32 lcmZp8!`kqMb%OvSb4)^5voPa!VMfMA6A)+nbW_Gm2LQ5I3KIYT diff --git a/priv/bsn_int.dll b/priv/bsn_int.dll index 06c091c15972c898e27c3ee1fcbf7fdbf71c1655..b1b7515915b56ded85f878077a11f956e1daef06 100644 GIT binary patch delta 32 lcmZp;!P;4uD%RsgoW3U>ei delta 32 lcmZp;!P;|yDS(ve1n2~Y2Fe8(oK1g7@x&hP8Kmfp53Pu0` delta 36 pcmZo@;c95%+91HloShKXEX>$0%*eQ1n2||PA0)6{-GJ$4AON)x37`M~ diff --git a/priv/cbase64.dll b/priv/cbase64.dll index fc8bab6b3f4a1441359fc00fed4f276bd677c985..c15493090ec61318de8b09f8f416040d216066a2 100644 GIT binary patch delta 32 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 mu6JmAA1Dr6)x?4VqRD>%0=AT1XF literal 0 HcmV?d00001 diff --git a/priv/khash.so b/priv/khash.so new file mode 100644 index 0000000000000000000000000000000000000000..aba2b0be302fed3ac98ad84f0260adeeda8a3c06 GIT binary patch literal 72088 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 literal 0 HcmV?d00001 diff --git a/priv/khash2.dll b/priv/khash2.dll new file mode 100644 index 0000000000000000000000000000000000000000..53955ea02195c1a8966804318e8dd49da6691d1e GIT binary patch literal 96768 zcmd?Sdw5jU_3%HF$&evrID;?{L}Zkp(I`fPk~mOjU;<}gqCr5ZH)v7BdPA52yr7Ab zD8q4D+G;P~-fZo~UcS|~76GkIh>&o%G4m$qjdFA)_8RL}3T_L)fnVxRZS zp6BH3v(MgZuf6u#Yp=c5+GpynS?O>&9F9UR%W^nYbCONiFue@dMd}-s$dQ@NQaNPJ%AIH-(=G>4k)9E-f z(669>sbd{ETkPB?&*tt^=?1!Qrwbg8J}Rr{?%2UwAu4|bjyXKomGZYQKHE8S<~bcY zb)Mv2u4?E0J??ZY@82t<#p$?_Mi*S=bez^J`^4l*-pe?3{~d5RO2PNb{zk`dS{mo& z<(=Q5@; zFwn~3;lA(-(fEDyDTlCa*Wgb0sTbWN!#mh?d)O}@v@Kt*9y+Cc%p{Q{r zQgiWFA7n}x2aQZ(aCnWyU)b1SwS;$O5?v|xI9{%)2Nm=F(GH63(VJVerk|6sMp(m~ zFS#jK%xF62t+HES`^|% zd;3KFGAWaqSiUwSiL`r~Tb`$b{lrQhuT+l{tE0w-_K7j!G$GXGsWFrddGzGF&cs38 zXv-Gs#%A+x=Q|u#ExK`CS*32=R#vSW@v@L^+*hXS$rl6R8@g#E^zmIS^hn+vqkl(r zW3Sfq5YUZqNibZhJ$w)8)0sxt<4 zK!mtYPkVF&GPEQtJZk)DGZ{C;i zSsQ{ME&Nb=q8tCvMmQqI6`q*UmY(F~wM}T<7$!xBg@dh&22wj;_dJ*|*t)QjipFF; zZ0Mzm3`KexIvXV&(O^enKoml!3bN-(}M1w7fgQ~RC zx}qd%bVO6-x^Z>Ly1F`ZGmMzJv&4A0mJSR*ZvH_*W0SQ`Po38hyxJqk2-#pMh#0rI zX?8a=Df-O}&xC}P!cKLDsT=PqjyeqOjqsqFaEbO{R?^cL!M==tVN9l^Szh6A#Ec_q z5{Z~MCc^jevU+2YE^~HP6*J|=vN@4-*(KrR7H7EX6F4Rjqe%I2_(qLFt1dmps;z3N zt=cGTb;)t>p<>k788du!#-5Se;_jHyk(JroyfLapcIzo$)K~|3K{1L3qQ+Kh zdO2J?vLlwd-IJ;<6<$sc+G?|7Nr9fKgUw+7^9rnqC2(71`1j0&OBV z{w1>@VodQsJ3TcZX6zMqI3;GBGBr7Fog?81TP?LPnC>KpdeQnWPQ&LZF-W5wlL&(Q7At#cQ(F6OiNX!5^QLR=U ztMh2Bk<84WQGKMhTef7r+nrR;N*=1E!d@ke*PyoLe|fPJ_Uud^bH-2AjcvJEATm{n zQ{5Uhafz%Vdga~C}H_m2$c3n z*;2p)&Llr+ITM%F8Ivkw=~&rP-56~72Ps@OGt_5ElS}pVjIw~`PpE9cJtmKbfJ_m1 zcC+R0;vut40lnIz1F77c_nCB6Qh$&XMX&Fc_e){J%}6qz?hgBKV}s^8at#Cdw?ED4h%@Jd?OAW`q<`W<`Z4mKuQVHc_rCo0TKV9K}M@^F(w6 zUk_JBt}9yruJwwj289G%x%4d+DJzc}lb~&cRYoR}dF-ef@Wd&)F^PfgT~#;g8GHEr z%2LZe3|wMV?HO#Edt;GcbX*-5UDEv?hLE*bc(*VPZEqIZF!T+= zDKc=g8M?MFFiLrT{NpquP0L)@jb{|gSpG)!`l>v87bu`7a?2x-hF&5G7`_idD)T!U z?ZqUQpNvUfIbsthTD8%ff|`d^VaVvgEKjN*yhxi_t-5uJmV$^niHg3m|@x17S@njG}I;5&Z(Kj-&rc=3{~_W^eNU zxB1wbCtUo*`8Yx$*UgEK3(zmj$8)I;h3)y6Rz-SGkY1!d>3qBu@(3ULWj-n~)VsEj z3}w^&)yI@F`h4MietkiH3IkU5%)dop}Vtxp*}Oy zo?xpKux{aqMkOjv1W9Y?9%Gtp2Q2?N3c&G&dg0egzOzrv=>c2skZ}SKOeqQh>*>W|#^6IrHBo52prbgz7c6AH;;{hoBSNOC6 zqApAh7}E<=R~PE3*^qj=$BZ5lTC;FgD)_bz@TfiZN(X2leWDKVqI3jmhW`8Vq3Geu ztsnVy4t;3-?KU0e3mw!<6FS5PS^i)S@GN^ZV%bDaREt6|U#%uCg_zI=ZTmJBEn7&) zI%{0d9KBY(?X zU$s#@xF)2AZ1rtY+up-`$_8B+MFtZc(0e1=7b;x3)!`sFASO|zN?HD0LNGBZtUIB~ zU3QhXj<1rMT&V{8NBL?7)xvA|haj5_p%s^JMo;`J6)pd6kb-~+CSGLOoBMx4z5?+y zuktmEdR1&t760c873b$GF6mWqvRdbvSkmO>s^*+8)Vw5LbA0cbzFuvfrfQz`g_?!= zny%h8OMBHk*dpD0@75E$`5O7En;UtR@8;w{ubLgI=AZ4Fr`z%&HMv}L^RM)p8@BXF z`+t}RxRe^u`b!GRq+0ECNaOYf8QIe<{UkhlXS8OXMD5IH%4XdfwfGy(-2Mb1 zS&o9k0v3gbw~=pjG`HMcyc!+QVv3cwRk3%~9&1tQ9{a@gW4)f@w>2kU`>EN!B4kf5R;^uMC*A%L}^!V6d7gg5fP;g z@Mr^cu$>PO7FRH7w}q8hqiDJ}J$m5l##brXWiPrHOI49+x6uI7T86`}3v#o_>&92n zXJ>fz95Iijz31`Hyt$vG-Htly3W+Ezbi@o*gPAN|F8>B6k2>SU_CQG)^Iuq@vu^WQ zZrUoFoKa(k)X!*imu;o?SgU$0WpsPwfUFZalOk5cnDG%h$Wc9m_sX)*Yuh zWbHo3T+cg-2YA1U7j*nNdH?QE0s1IFWwRH$$Xc=4yc7#{sL4uPW;T!#3udy~0cWy< zd|06wNBgn&NKp}uSVO$?02a1(TJGV1QZ&jIFfG&|z}3~gEs1NytY(e@_DZ3%=+{a# zhE{YL?tjdbY59QHt{j4V%m%9?<$jSDW*b;4$^??*-y)+RG1h!ly1tMX<}CnDj@m@N zgKjJW*Q#Bi728b))^TXhuzkt5o#tLjW4ic-jl@&DS-EnHZoH_cW(8uYP-&{36;sbE zT=rg;A;Q3;)%ZhzexRtTCDTa{(i5!Y5$BRx-EemZMIm!BlDbkxC(F7v{W)6N!@i_a zWc4%TLN()6NM=6H{Ox)5!``pHO?CT~?2W4dI)-XhyQErzcij)y$=1_w*V#?S`{!R+ z{@2zr=2qODv|Nc$cbn(+YBPQgH3#O9?wg+FWDF{^7@utXEdTcbVPlc)Q?*atin=R1 znC`7@4?nBPp4WJoBnh`ULUpO#*sa!1nTd)CWGZLAg@HRa?tz)Q@t%kZ(D@|F+KYvy@5kpw27CB+^WVGlPO8O-`ovsVm&(F(qEqq?* z(_0S&^eWO-wLZP7RaYhz@hR#fd8q1XO4lN-ddhv5pg=Em{qPPRuaQSkzP?Q{G(v${ zQ#YMVUGs-;uc{^a!lF!8}fwrqLYnLV77OXgg=Zjrf4WYAFaTEH_s2Mt#s z3omNP7Mafm#2=*^l=cuZ4%Jp){hBD;X@HeV*uyPw0sRGu>>U~AjwwN>u6X-Jlh z&p`u+7HM1V{tM6MV#I-YH_|^xfkEbiGu1<1==CS^v+u5G1D3t|*{~YMFQux(*qgXH z+2vgF4fACNfsri7CSL}YUl+hTp+3k7*58olR`nbo4VA0rX?MDRB?L%zSRZQ-xG$st z{B|ifd6YYk6S-}0d-?BF6bFShkb-{mPpwag;nvl4*Ek&WYTWBzvaG~XeXjQi355J# z<>uPdSR;Pq#O&4m5Rmg~!lj8Bv5Ixtv*AH{dRnRO+Cj39o-S#KrAs#HsX=AY;Ldod zF{LDi-L}}6;BmFaf-MVC1=f{k_cMM7LgAD_$$QEi%Oq6{U-sc%pxCsu&8E?cz2@H` zZPhO5(ywV>qDX((e`&oMJ^GQS;|1h6R_u)zHtkB3n4=j~zJmDkOHnRIIF0ECA`W zDKB^|7nqU=RBg2UcPh}$9rBWmOue3JmSzMIl^I_B18Ftcf@Pt3x!~y+v<$ljYsMT_ zl6q!9i2Dq>jUF`;m@GIt`a1AX4)k!(Zc+%sWxMi`Oxr6|HEdL{{Tsl5DJ-B~irbnx? z$3e|_>dv<7&N`uP{g>2T2LnsnK4mu-Aik=Ws*Tz&B1S7N!icds+}L?X{HEK&jUP?E z>5fFi*kyE9Z49R;I&1rNXuk+I9=zp_o1&@0vf6%|fKzC6L3vMVMp;RsUQc?To#=E} zqdG6MwDB!{oQ`<;_&I$F9Pu;8FOb`*?x(x{!`dBKQl_&Fvh^uld!lvGporn#gAN$R z+Z;)`x01{jk@q5r)uOdT()zJr=Y3}huCyL8WNj?Wx2>&xyYHgn+J3Fn{WewF-6KIn zMlX``21sga|2;@ce%sFIN5*r=aYt%+q5W>yqPr<^+M-2(ckaN|70~lW;X`eKcy+QP(C+>Nj|?#{en$#P{BXtRZER~!CdfutY;HqjavJ3-WS3W zko%k0?SH@es;KA+ECL--PBl@Py0Nv zdtS92i?{JeqeNsxT^p@8yYXw&o-Ll2@_=Ho&W$G`1|}4c6Cs~ zsoy7;N&P+;ISTNcNp=ZfZKx%=_Et^tkhfg-^t7LAx=rw*o?S)S&%=2(4~KSj0OjK$ zdcCKyEBo6uRiVi8`u=U2uW&%j#XhCg2M1~Kfm(Too_w=Y@>QlbpkJ(_wT|Iz%@k=( zBCWJ@!kw|8cUs8lSUe=dYr7RlT}P766ZB~1m1RaNZ&g3WX30bIMk}eF|9HHLUZ2H`~X~_ zt#uV`IiknsYv(w#UWSzV5W}aP(^tU|JP+uNnH+r=K)5;bqF!EvZ2H!d8IsIO$qZ1v zlAM6#_%vV1fW#jKVvf9df}Yx&p6HMniWUwKa&{_mdcvBoI1Fbn35Af%@Z5eNA2)L~ zK~XbvUW1TsWa@Gf;YiGQ*ZdFbVx4ibr_LA~HD*`WReW-&ws2n9SfXpquft8EQdY_~ zxjI;-C08)-jn8$LWkik@#)6&NgSLhLm`5IxKcPbMzGFUz*7O}aS>lWjp=^mPhLus* zi&31~JDOXTF1tV8xXdwk(d`Ek<$7}bg-=mDemWXjIVscin9?2kuN8r<v`GXgqQru8BR^sBT|ok*128`TRT9MTGj^EWsrUFD!QR) z5ldsUiW=8qj=z=oqD7?N79UUe`tWtgdt1M={7*AbqDBy;!ot&G&gv_a#Rb1F@qS*Z zRyzJZ$ke?D`MUj3WOk7_eh>06%N+poTbSIw#!YVbrbO}x>MAzpQd{khx?kD*ovGyK zvc%kV^YR(!A<6gj#u~*L)020X7AES9aNzua`@2JvM+%m}zk~-uL>&)u|JK$2J}@|bHsYfUJJMM%&)7Bb@poc z@$Z;P`Ngv5J?o{mYFBiPGxrzMp4ud(A1VWG0ew@dgtZpH>gX_RN{&gl^5~-0F z83S|cJI&-p?VH=BP&Z_H0kd%g^$abbS9YQwA_WNUvpd7tykVUN2K;8sGc zV=Jj)`Jel>G&!DJ~vH)@yioQ(-ACH&9Zzrhhc}hSu~bFET5n znDPAK`5(#lHfn4SuXaEl%YPc^iR~C8$XLt&$6w0k_3FgAk@UnON8xm~(EX}Vi^%7W=);ewzGsW;9)3opWdC!W*rRR}$8Vf)9xOj!Sl5_e zNT8;_(qRd*T10~G#JQOd$UYm4h`X_aKrt0g6+!^#TGFpRp(c+#PZgM%R8zhi0@Mr6aDf9$iO;aF4X0S(yf0?oJj#4N{|3qqFBn^Rit4iCL=g_{t8z6=vA03g%7jyuU^ds{` z?v8dW&nLK>jzU*)$PA=t z%m_(vsn&#%2oZS1o$$ukiHFmn|71Ru+SuC?9;~#PfAgdpx3bVL@+6lQdbDN{izqsk zdivWdNoLuSM=_JOV#U~$__(n;%_$G#USIs4#_Dg9jNjT={Vlm)*H}&YmUvxbHHM&; z_*WXM*|xOAFKDd(uG|BS)z1`gAJAC+SMJ8EiO^bLIkK0m1&$+o!P;iIm9G`an>}M~ zliW^S+bFkzYafzZ$=amc+-o1?mYVKcBkL8hua^JskCSn)FY6;5LVBt)e@CXCy48~z zDDtx9q@$YSofw>HhpAO4B3L$3BW7KCQ zeSIA3VhINLgqNp(@noLC0tzZF+?9!eh?WbbV`*_CbEdTA&3!}r>ZTT0H1VTS4g@gTC z9-&E2*Hi{_36ZEqex&^yo|0A3@=u}=G#DYGt2&o&`5gjO77l?^^;2m4^WP{IQaP-_upAy*D+a z^HzK2>j;e;bR44sJ##u)(t*Ng@+^m7FRU06tx6ZI3jK=n5Hi$CwYsi*-=acPs5>Ht zbtBlZXgvNBgm3gP#>S3C7wD~Ty6Uv2SpQma&_;u;i%-L}t0-emKnkg4z+8Y3KtELB z2R1k5WYvRehszn~fjAfy)l7zZ3Q%5T{KZdb$N^_}sImkSR2a}G0&<_yenQuUy%hQEN zKtO>vwsk`{KSrE|lb<^igN@n{Bc2-mHBd^qr}MvRWA+xL1rgHA(0MkSlTW92oy z@*v(*PcNZo7}F4jJ3(j1A!xgR^A~N5|{w zuv3=^0F*-5P3II<^%zIuaMWsvxmv<2e0V4K(D$`a#$oO`C68qMHe#N`EBY{xX6wnV zR9sKR(TT$(#*L3(tTFZ;prH4!)fXMblZB+6kZWBd_Flr7evY9fq0k+PEbPe`a z%2?y}iilv3(qT>zkm(1|n2>Zx1!;M7u9+U4OzEf`z6yG8U_xMW_>D|@TaWE|cd7l( zIN@E|JAfCaOl}rqucSL9ZP@wO+3C;5FseYz#CXo^W_IND)syHPGcbrKokN_yXd(T~ z?~!xd@P9#P%r6PbFvoGIQiPVR+ieF(%y2I{o9Qi1t0Q_vdHfl#TA*NZGpJc4T*Oef()NrG?;Tj5rl8V5NpT z$j^ha`P@664w)tL+VIwh>qs;!Q(K$2D`7cX(y(XjjRNsH?eC< z3|l_b{2hbw916dK73zN0DvbMT(Ul{76|ECj6netTol!Z*9q*$~9KRz z&FqUdK9^mn`Wi1@Bk;@y9uK*_W!SFc=c?!q%O7H=1a?I#cyH@D_S(6Yr9Tu->jh?p z9@$YJy9w*g%MTHW_JQRMVZzD8=x`S{`dx81uH>vQIlhtg%aQPEKKHD1RRm6-?B%9Y zgkXvY)mL<5xoq(CRDV*-Rq6_#$j)!n2*ETtQD=U~P8*F+!WvH&;9T!0NU4VnlS`ST zDg=(@NkSnZP~OZBj`m+K`$ff0h#UXL;a0}O!Qomt3}2kMo&_jsb;PfPl9%iD%f#E! z=NfW<(Y4m+_FuZNc$B3%9OIAhZ%K>DyC49bWpC<c92 z(D*GyzwxeOYFqw#cDR5UVkUu!Fthx>hLn)*IW|@)m(41)m7kkfAi54L83#C9E7Br+ zvVQXpXbT3TWYXVZYu>0W-sv@+SNxL5Q8}du=nyOhqpwI zA%8cSh*m2*LRNwzbF8Fsc^^Bl_0TRd+p=Wkihfs^p6p|<0-XLW~!a9W_5bX1of1jSw1hdTsFhVJdmn=N6qMU2x2DEgUW_MWh1)4O!Pd=GUxX^%rUR+ zd6;ir-t(ZF;hu+jb4<^}QuFNYhg5@fc}UN53p6l`c!mUl90?=4*1=Fdw;rfLVLh>0WZo$BuOCLG=GLn=Zr5QxsH1 z7t}%=V9T<%8{S{?UZEdUSj6Y|LjNUZqTzi|O4!>qL4P3Sz^4<7G>5B<-DZKRDCp0k zBqcs#xkqpc`kT!X8+~Qd7W0V21t_|kAE3bHu_cyKy;6d+@*Q zbj0AP&ux`hU}oGZ*{`HaI`CP!=vns~Z4BRXPR<|sU5fPfM`n@zmiK>o&2qY5bwK$e zyJ3F42j;Vs?S^R|FzB{K5(Xcr`KsqWoZN)(n6pOE-}}lcopJYbGI#Q9B5OJpKh9;( zl91198pozhURp z5$mI(gdZY9B5fC_=vt(h4>3)1Y>YUU{IKl9{wiOwlEa7uzifl#mj5pDMY>#)4;zk& z8<7I}kYU;U)9M`Y4~e`4U@l3w&Z+6Ka-;+Ee_ z#j1^RJaM3a0J@Hyl_c!sYqk<4$zzr>DnGwVDUJR0eFn?6bsrz!Ul~@0f4naQb(vSonm~`o>qf(B7wwb2P`W&a7+w{*ib|w( zXu_!`Fz7NxLcJ`3*e>K;xc|@)CtteROjr{YVpaY-Xr7&NboVA|%jbYlP#_%NT2Um+ z{!!vs=jg^(Wmz+kG`W2Z>h+Cf0r3ebD+mlOdL5EQ?O&Noxvj*f&1y;%sgU~at;CQ* z2Nq`Csr;OrM|(&x?pQ0?fGB4++(Z`s0DPIQ{}IaVj9?fG7RM{BmZ)n-^2qrM52nNK zCyxwR(q}b097kJYYXk&V%bq6L)jzCFYH14_k=8<`BXqBYxU%{Q`-%`?`dt7yvW57p z%2L(lL6Pw3;X}w1`WEWxSpnq#62PKEE184flr4qY*R@IU9Grg-&chM(5wP#E@9Xux zE&o2C-rIV3Od^u@ivH4HX32yD`r(gpoh_Ug)t=g8E9TAnFqnyMzRT8u%SEE^vSsaQ zy5W76;X^l97Bki*Xf2rUL_NP7th*KK?sYb}X_2xkAh?2?wC_u_v3({BlR_`MzI=wO zvT9#gEF(wdqc-UAXI5WRF1pYZpPAqc`SRK2935n0%`W8%Wco8ntA{3L>NVGtCMNZi zjoM|E%iO$=2hL}*Vs6$C?-{o1@UG-rCEBB{&AagUESHGBD@tY8YrK|uK~2KR!~~UQ ze;Q71v=~AT6mJl56fm}i)6}d!e_2IsBoW}F1f^fpY#{diE5E;$K#_l-zKbWO!g@}5Giti%bpPmkH4}r z*5`JJgEKBAEzE;g1k~(9irO==mpPuJ%m0P$p{jkga(w+HbpPz5FVg)?=svGzXkx~R zgx_How)G&_$$ENn!tcxxemQg(VpmJ2C4ohEgA78|uCTF94TD*#I&LxmL-8{`s0Lsq zqRKqRKch4QKQgbcDAj|R#S?nQ;No0AFxh^C zp%meBRm~Xlb7OJK@nbQ|9*g>%MfX0=wWBNBV?n=|0pe%Gd65sORSt|fSkPsX?n(?c zN6{ZwcFD-g#no1|r(G6h*EXV%YaudP4#G6Y*_`LZj`YnA=1 zA8!+!udX{_TaVL#$d;hSACS1KB4oSx3lL7?|2JIOKgW!l2q0Q^AR~TDNyLopk{6XD zjx4*&>Z29yBFv4>XzS;$C@}+Ry-07(xaxxc(Y49jbwZ9r$c*B9QDV>*v-`LLF}t^7 zV(`QW>B9gYY)$NsrRwT!TMzP94nl7Er|`YfSQk%859+Kl4(q9zd&F0fnmG&j^wZcJ zt=P`KYKQue!3R-m{lw~V`OAf%w zH1kB#OFc1T3Go-(v}Rnu4!ve9c396DbIW*>cz?0=DSTA=91zzahmjrrGM>J2dX>bzF4n1_A ztK~IMi3O}Pm_Z-u&pADGJOGXyusd@_!Fd;qJAX;BuD#I0$)mP~EIr%IKdG6>AZ{{3qY%t24y^E#{tW=7%(xxeDmv2J?a{i<0LrDT(HQJ?&iR3ax0@ zD_&t1KCa8R_?A6+2>ZvfrA>l1MVV+% zBVcwkx1h?Ft5fm1R=XXtixF+iGHAsdmtE43-hCH8r*R}R839!X^Lpwk#*_(9KpEgH zkS^9sYAGFT;9AbLl3xD6d>=o6^kr60@8;XRt3R%H7vuZQ_wIxOy?b}UE7iLhf96Z36g+~`D~Q1#sf@xk6yqQw)f}W$9SvJ5pm^(k@SoQ;l4S0$gZh)@8*WqYb*5Qy1c-_yv5$q5+|cnFCS3^|^4DXknx8QA&V8 zxV3qRe`sO4tpV&@Tr3*EO4`U8d1dM(E4hG`h5UKsiCW0xzwM<^9lwaPek%*VfT5#& z_DvmFU4Z(Bm{=*KHQcU#c`?xdE3-kx7#3dbCM}!F1(v@UoWY(?2`so{$-x+?RrWeq?0k?6!@w6)I(4F~KNLA!xm`6|^dP&~k{zN;I3I*#O)8UXaAWN${nen|2?-uz=fE45od} z$%-+cF>6n*+Jo%S9pcOFO?q)lXy(PD$n@T8s=Z>!*ehE^j2Pz1cL+Qi^)j1ONAlR` ziBsC$8%XnnA87d!q>n#&?6bt6cK1`=*|(65hkHo!*nbkJfbeL$``fv<#;lTVW&?ck z=W6v)#-EV-5{=aF8SzhGH!^>h_RlP?YS*7YxMN}C3=Fb}tBI&*V(!1gEyDBkK)rTh!lZ z-4H2R5U^Uq!H&frh^KBv0q3e-GbR>FjNSsIMkRKZZODjnJ3J-byZ}mwrX#{_a|NS! zV_68(5N%V7C~V#Mt`LhLB*X$%umzZ-Dy4WiiGUeGeLk)V{7LZ>YBevQ8^>2HqM~h0 zNIxbx94c_e`!&Q$OMNS1?mM&oLj?u#{_-4H@ravGZ#nqHz{cf|lrpstf9nd^FzCq- zoQd}A#HwA2OzA03q6L3O_--J1WayIOhS<p6fMw>CJTjDU|dw2lMeGofW;c}Itw~W zY9$}qwcUH+V5m?Xk68@|t|_O-X5rgXM)omU(__MyMs4LS|HaO*nVe@sRNLqhyCMCY`|vz1^lCoQxx)aE&r|q7B;uk4P-gF-zaFH3K`S zG$a9C5#hRS^o@YQAGWqV%GFoa@4toNQlWSIjo6?DXD1(r_(f;zr z>!wmYLp?#ry}x=#^r-YZ!m`DNrpD!;v|In00zKlMLGHBkiTV7oa}qDYq_} zDer~c%o4;ry5Pm6`D*o_ooiU3E%PG&Z*W(TX@6WKXB+INHr5^Z5!?bINj%mTQCg6- z!tLEA2y{E7TDX8RBGp7N7zfRaY~}VDM}(cH!%MOB`7eNF#31N?efm~sO|{l^Irv9h zpR%4;60GQ6m~FI7B?iyuh4w55wx(j476>?c97<*3nCfT|-FoV$B8XBql+H`taGD5o z?b$XZ&qa!}skj^KN9HZ6iGYNw`Mz%s){6|HEL4}5<`(r|Z(y}zmtBy$KMz-HCgO#v zv9hzOuPZw%eqQxgo$<4^Rbj;IfZ&cLgDTdg?{TIFl{du3mKM4`(<@$1d?Eo6m>2Q? zPC9kX=hScTcy!l#y<%(PQ{(c&#>_8a<@o3%htvbfj5mHta4QGt2v0pz=!YFDODKc8 zQW7k~?h%qGBGI2jZk5Uw7}?Yvo=iW+t!8+_$3e~JPiYB`%N#zjU)%WJ3BN{Ahi`$? zQ8ncAcuq}^tI(c}3^_6{RiLM@ zED&;jm>DXZ$S@bMw_(xp05}C>Mq6|n_v#tBn zggVxw91)qtw(MaJ^BwFvWNh8LF-=?kX8U0q4?RA=#;%KBBz7l|ZhR>SpC0MEq$Ird zH}HN~dt%)}ce3l;#06EmvK5V8KX?W93N&{8_6>()@vz3OzsUWa#x9vt@z-q?BjHNE zMeOdVGigeEh3UpptRN9%C&DVMeXp%*U#`7jJ?#~*k>>a&%EFp(^60tQ3iv{`buJ0J zF%qh^QWB~;jfAK)@jlwxgGI0{kukWVYMb!)Mo^Qq+BPoSLS)MMb;L$U4#j=vcERh* z?EN9je>VmA@s0dS`AZpSqjO%QrZRq7E@GF5F_RL9^z@iXdU8VnkC!Cgk&pdX>lGi% z9UIw(0)6M_cqPz(BIIB&mKOmmJEm~fT$2%=ic877U9p7nE00z%sG(p`nJ*JiF|RK@ zMMY|%PalpFH0_ESV7c9V?={@W>cRGGv(Nt9ZG!X{Vt0vz?=ko0d9~Zdhs;OwFjbTU z&W>Gi)c+ob%p%RKK)C0sz_OOEJ@pS}jI8Zlpd{Z22HU|{A{PzN zFOAU{{CU(5ZjrJr@r#J-J$RJeMu_g&QNk5FSK$2I0j;7{ zJElJTs3K|heBf-PV0JZ$77|c)jZ}iL6%dwq<)c#{eqI$8A*nd-OOCVl#NDAFG$D%D zoCcs++8rkix$fd-%Bg3YYnadNtt_#2d<+x!E;_n;6o=Gg2PB&^(NO@kJ}KJ;;H)MA zoHg9c)o2_7hm(KGkvc{yGvp$(Il6uvE{{_of9&)T8-^cah#Akpz!}_qy!JhzptKJ{ zOXoXs-+NPMYriI73hge{*fjV3J7rH6ngWcd+ zzj~rmPqM$Rw|)P7vo!4;r5ef5+(1dC{e!rz!7S`Bx7m*D_?0oEsqArbBo@GUr0fSI zjaSqOSNuY<44^eWVz1jgRFW3$Slnik-#}V9`w_{O-!0f)g!n@~kLQii0aV~i4WP+( z)ObZFqk#;ynZ@s%>)9OHtIyASI_mAcx;-YwQTlRMqYzAvuiZ>ag&)(e>7>m9>5Dza z#m1|a|9(L7&DXJKN(~W~YDmPyhbv+H)3VJ^yVr=BCCHZ2Jpu(yAGT3HyfgXcnIG$D zsT8#1;cZ_#Un;o(-~Owj%p3UNHlle*kP73n`6lWnM{?nDOS@+XY`);Nr#rI7o^PDb zn@2(U4HI{WhI_t^GOAMINK{O{IOO;|{qeqZERjO1w7#6*J-AUVv8TJ$^e1HavqHNIbC5DCg6-3FG>JovfL!w8_-Nsmz zsfJtSI7aIcK7n`|hls~9W|95nY)*)B=0F%0g#FbGnQzh7hRFWvMq?W*>|l(9@nR01 zuECCMWlPpzr?#^0H5l71%l|%vl#?3-kuZ?X#r^P>S^oXx=5)va#v064`^%GE1&JbQ zAe6l@+2u-{CuvW1l*-~u;QPywyfB}89h+A*Z87)vX%Cf3wzWb0lF83qTJ!m;)bR^9 zY^DoqLJphH))dhP^E8xzs(mOtBhFBk-V*aIM4`DAh<|a~Md~Qh{l!Zq1C;EN*s}2- z$?no92@0DQn}%>gvMgvbXyY*ll6K) z5%Dei^xzwbfAWtoqTv2GL(z6bPu_dPAsV?V?~&uBhv!P$>?JtCBEQ@9x_p3=R&D*I zTa5MKuT-cmY5G*Gol3P+fc}^V6x^RX&)$Q(w{Kr!pltequL-NGao`7cQ0U^+1pOkl zhRN8fr6hlr5gI?WM;Qx-5BnHtwORPAYWTAv8Fp{x?LT?j9rCE~JkAb|5&X_v{e1^} z>VM0wk3IHRx%xdq2)^fRk1W9wiRIu>kB{>_9@(68rl(Z2ZO-&~%x?+5*lR8}g7XDp z$~)S=U0~ltaHx62+7-xe`+g3m zCq5zB)h|9i*`>uj>2Q{Lv}9Q_!}t510xLGcS`%TaB>LK45J_hGXicmgL`ON}@hVHr zHuLkBMPo8ykVGjPUFCtI40sb6q1@CrU?+haz2g><-TG!B=c}`QYi1RSg3$sSwI&WS zCe%O0bfeGbjQ6jh<`F!GL%Q~&L#hMp#M9%)A`IAxr^lZq3D|1<`I6vA6vmf`oJjt% ztbyhowH&nVXCE=y@e0E$&#=SAV?Sbywy7~5tMA^H#i~A5=Pah(Mf7s5Nz7g0=Y4(Y z=&^oF#_Qm%k`C(rF6p9o_qa1oM0 zUnqS)P#AU93Ms=g@(=&FPH;- z?zwEw`KOD%RWAWYvt;!T*j&VmO7RDv+DkcXYn6K1*450P!YJ9_66oRwUzw#1f|i7| z$j@LbkCl0H$2vmEo66BOYRi*11?WlL0p0$X0V`!K=2&%#LTtOVDJKW&N|oPQ4sq1J zW4=Ty4a?8#$qwKb`C82ahSlAfoIB_lZux8Qh}j%PaBs+Ck+@Bxurbmr))) z?qv)=ItQCukM@bB#xOJ1qt0PqXhCV&fgPxWQIoS{T?I?}wD+O2a$Fi627{TXb#h0w z`)N0&yPa#jRGHi!QRyq@jsGCU(x2iaGiX$}qlxH8S(gRdl=IHT$#`>!F`NVnd^EL>mrfR@& zUQTP`^CgZ@^|^@wFoo7s&eOcqIhg==tgd2jtTgBI=p8IfHBU+tiF3sk(nniWG%w{+0hFQY+fEG7;pajA43lW9IG8x2 ztlIn!bV4>|FSjQ1zY8cr{?MNn?FjAz=f^)g|y0xgrh$0b=MS<^{lkHqBFIa@@lr zM%-q*<6N4{j2AXYo!@0Klr16|=bX4xlW%@(RNwo^&o}oIlooZahWHu#yk)cd@2tNG zkJ-Si)=zS-&^@;6i**dUPT{`=hn};DqCm-%_M6*Sw{mL36FWhhDd950rSOZ`E0pY3 zvZs4-A%MP%g=G~111s6o9mOBO{Q3O{f)vDH6ZfHH1^4jTg_e0#bSuKI6oG(S?NXgX6+|9o{y^phvg4+=R zE`hKu@w@f3_XTMLwdZ!-NFK-`_BHvy&vm+C*obppI$7gL3@|6*D1NOTbDlr*&-szEpL=uY zE$<0Y;ZXQdai|nwX%`m7c44zrlw%vpkx$m)G{!Lb8WdXMx!#RtSxv@nm$=Z~& zEgqHmGf74vtQ$l1;A`?Bp!Vz~da6c%UMR*z;ABPOJk5+x-pf(Se?a(t@6sGHiN$fV zvO#9s^Q*A1f$Rf&|Jk;GLR;!ZY#Hux?{E_*a8sF&96q+Bf{-&{k~mdx@|(?sD&+*} zH3Z{ibSNSr#s8{nekcB>o;+HhJvc_Fl=^X*E>KhaxP!VsQ&z3+&zDtlkENb3lb=0` zrSGx$5YXo_SC=R{ga`HdKCJiz+M^ZgqlfVZ&B6$C%%eRl#IFl}w)m#0=`o|D22PRB zqrt;V0(?JRKBZ%9CuH67|9rYEmAZDt{-}%h*5bE0uqE;ASRzg*$Xiq2-xh~y$-w1> z1D6-$n{+Bs^)4~cz+du9Sug|gfg(9;AxT!gZ&nt7vvJ!NPAWXVMO@gKtA$&3X8CwD zXKLP{@hNybS^fFFQ-#C*V~Dn9RgMKejGrT43GDek0KZ$0GODs=jVHG z#m^+TpUQ&Cm)HMC)HpnECC;VAfvD>Z%r}clZG}c*6tRI~+c~ym81EtK5@eGoJnDT} z{=M-*w1Th19$sv}-KO65QEwl3OAmg$uv(7M8Lz}omlO9D+rA7TJ@~m8ShQMn{3Ds0 zgyO-^FcemNpa(x(Y@=oq>$lUCiUew(EgZ)CXz=}9DA%*bYL7aDM#{*O&Dzh7zP$C*$^oV z@HMg)$+)j`ML~9iwu%VpcU}BYTk=4WwkkF#p#7c?YP2pV6<$GDcH$q|5loC$6&6Qg zWAaT-beOK)Z#B&*3?xP{%W7A6YBAXF74gUyIfdkJj0J_{i?ir^OEzO3s}_^Paes>b z-Fs9-z-#D)LvELEb-j%Z;b^qt7}_jYYgg~%gFO5o(487+_g}KeXnN=qqQZ3728$1F zFWT$O&XHR|_N#JpWv`T5VRowA+}ViS`eet;ttfkm+&tL}Ch7l$|SSkEA`>t0mo6(tQ{k5~7Sw$Eoa@tC`J0vn`S8*%$)`h>Gv>v{^5o7&<<=)VL2gCa zpxiv!F>>phJzs9c*|X%m5>C_1E#h(^t)JwJ=0e!mnF=}7KT`If1+4*1 zaNWc85Z8COPGyRn!Bx(60oNs5`YP8PuA8~;;7V}a$Cc#59+F{b)fP|b zVs!Vl+&5+hnJdttbE*$NV@TLic8+La|CSJpgR+cc69||yo@H@Dq+nN@ZYA&b$geG0 zgh(7+Qq1ahDB>AfnD~H&Qm-H)V0ZFOCqJ1(fs+OzVSp8%KPIx-ZK0XHA~ojd>Dy=_ zb^RPYm6*?=m<5Q?`(Fd|s`WabRf!ny)~e7bIkA+kbC@rzLVf+)a{7jd#*SI*F_xAb z{)y;PwWtHei^le0y9oWouE+m?gD4|wof*Q|ZXbRv!kE8}!V(nYLK&NcRlZZap(8uu z)AhzKaTqPRP)~<7BnW4rR(B%X`9u62=rm_s3}zKzBAzWsc4xb5%ND$71I=xUS`z5N zhuIzf_y^^197&vpl$(qlMcl-j&2>MuED%+(K42??*#4Ld>hS;I=A!t3Chz+qWpjT}*cU}l82J|(c zuK|72H+3JYQ{O)M;&{|E9wEj3CRni_I&^8a_;Svn*p^z|G2r{tp-^j@o zF_n?1B>3fJPFYF(c{#nYtfxTDzmTa*9 z3uG~cDV495*%D!zX5L^z2w3%FZkab>98_k0hXo#nU>)7O9o)3!;xFUt`!34SOlE2G zKHJyc&D#f`5rTagZ!i3B*m8zWuoW&hF9TE9`$8#SEs?KhV6=}I+aUk}Ur?82>n8Hb zd&;;RMsLCCu0^+LumhG zttJaIJ{i^Q3CMVw44x{*5A~#!7Ys;0Zs%jyWG9T|oUhDI0M8vQk)O0#U`G@zBBJ1J z+4zdh!AxQXtlG$jc^_?MUzZg7Sl&j+_wQclmhTVzjiJMMp^D_cgZ9UNfdO@}EN3H1 zH_YjS-FB?}cPaTUsi@g;^|J?uziwH7e;bU`g(JeNXOj3KL24E2b{-*2QOxXorrm`$ z|LCi}k(jeRL;gSZ-UP0ys{0?l2q-FeaX`r-uZoHyifM`>=WrGDqL7pmrY49WC{u7z zGzSnX@J7;X*+6A#qco#gf+Lz4W)@}x4o_Xh3{erB_)d3RCSpa1)Q{_p4W zzDF$zM=VgN4ncU6IkVcvC{y8w zB12S}VEjL7?u7Q*+TL$cN7-TKk zg(V3o&2jSKx-U>YVVSf|?O?PZ8KU>!JEEy@Y8&et(2^BdezUl~Z>1`UHj2*(j=~o0 zSMWf>$Bg^U+^VLpwdfAkhy^acVQ9;8EzEYW>0)tRi2!+JAqB3P6tZb=P5F*CNY%O- zF+#ty-V}%L;keF3G_ZQ+o#EXq?x-9t=*YzhK1L(yPldms|IJ??;g57mqAkjDlUdSW ziaYt}q3n#HUYGtr^;wK{H7r7BP(jJagQJei?_dYbxH6(+t`U zAgTd9D@Zy-)K+nJ1=?#~;~nVk>vjsi)^@hwHTR(e_XK$ip%M%>#nJvm14;m8jcpH5 zmqR4A`p`<00HbMWrEg77>sF3xHz?=i*&Pl|UF*MWW&i`NgT;G{F?gOuWTcIALvKcE zA#625y4%h`CU{hZUBqLO3Hn%rG5HbOg(^eHwuMS?w{;Lw#ejhCnyaX4&ek!uT;zS- z1Y<#eB+*PW2*m?*yB7oTyz_3{fzq{&EksQ+L=YY1H3*$jW)#L}WTh8n1*Uc^^~E+Q zcNRefMBr~KL_Z5_a1>dD$pnnfoovJCQVnDbHc)45vff4>FfV%*QegpQqH@ZvkdEDv zeTd`xBbbSvgbGopVlA$XSTTxqd{}Q&+)s1>FG^Gp*@Vg!W;qY7zI7hvD+OVHL^@kB zU%DP)IU7-s@;U6`W#Q$wT2HpDJ6)9Lq?>FBt@XuBc6XE4YY-M~l}3&W!nRtkzDq*H zxDhs@32WDgf>qn`dx4HkyWhQF#=GG`xAl4Vk_OVU*D`ODkVHY)U+X& za!mILmHXTEkFJI*+V1_xmeKNEq~(c&lzzD7#*{*r%b|hO0~K?7db142UpKj~yaW}8 z{z)i)byFy6sQ+r$m}1Owj7bTKAp z=&F9OKKTUZz@+yPvGm3Re|c_G6EDYjPo3hT=4wrZUWxL-9qYA?^?tb~@uskaYGJbtTU2uK5i@6U(Qm5pczL_7UmoMqAZ#_OAOvI|s z5A&??wf=PuON`HOv{AyCE~ZtT@RY>k{~pg2FWa@k86GsZ`9Y*_U5z*NL?QnI+Y-4I z66!`Hbf`}#_2MwaEW^pi=#8+@z!zA2T~TmUJZd1;r(wsFFGKA^U@Q`LQxh^#*=PVj zj=EvL;5b@B;xoy31CVN!Wz}{J^pSszFYGy{VBV%N*vO|}`x0WslqIBHRwzYdEFAA4 zqDnwIAz}DHLIi3jBE(T+O6@+|~4K0g#SC2kK+gIZaQhGKNCLUf^l zGd7UP1ZSi1k_Ztu)#*LM(840O*Wn=Y0uwsG$ljzed(Vet@v=_{$s~6lbphIJk=`i` zgBI`6?^cgTWx5_|*%N6wVa)rN8jfuR_Z9Y}Hl&Mm{>8Z7CBWEJj%xg)Xn5kN7-a6G z5-#f3%3mQ;=bz5cM~AweI^3;L8)}ulX)p3DN`mGnYfnx@ZLkYUA->btVF~wecoC;a+@56>pzFu81MJwHMGm}%$wxa zN_-nHp#Yoy_O`;V2qGp3MPZ>pCA<)-2_X~{OT~2TrjUyBDaZvP!h6>!{@;iupG0#N z8s2{s%^!XKn`rEFW~v|JVb|HWtq~1S?)VMz)QfV3$djc zpExFBDtyAHp*2F_3-3qe0+ISU%vWheOhX7kChBKJ0Qxiqjhir-IOEW!BcXl=@Lh5U6JEWYF?~nf`vwXM zzk>x6@VT}-=SPZ`_UI^x8{3C4n|{KFL_;+(+aSeFLPSkOEnI>+Ex$NA7qyYvqbJQ- zy=nI2_?oH#XO>0a%(A$XbSQzh^`{a!FRTNVa}}}Q6!W6Ypk5uJ<;h$k(iT%%yuW$} z`WQ?@A_{%V!Y!fKbmQwot1&a02=!9lRYdhI&S0@QW8V3aEnaJyOsYm7CX9#(VZ^h* zIh0>p(-9PN_a4?MoSis(u)ytd>Vzq7G%a&tw6z$2TUCr5j&mt_9GEw|P~_oul~9j+6dzac)BWeT6=wF{7xAz)N{Sw=!p0 z=6!EOzoGxUA_4mht;ezI)PEjXn}99C_n^BdnD@PCD?LP4V*Lbc*{Oivi3W@B#b*0f zH~=iOea)v$UT2{7!dcUnU&ThNFbm!$z(LTYYx~~X=~@$wd^$_C+xZt|3BLG&MBq*$ zXbfC6HAMf_QOfl_^culk*o&35ym=IKK4Z-n&E4L;3Ae4y-Ig`N?HM5xOq`%=M?Ew3 zp-DN6s^TnctQwviNwUJQ;WdPFJ+F#J=1PppbhI0O$14j3&IJ?DC)375tkuMt)x-MGdz#Q|#-l?T8sw3Kc9}T-D@)v>*kUDwv2e0C+zzI&l zH18t&JGy!Kmh%^B_db=-#r z4$E>gimgXAy+W}Ekr4VXU5$SIq*nD+kv+=7uUCcVm0|Xd&yUb>h6A+SDT6Sw(9_Q% zd%+?s0;6)#aPXG&7*pPDr=^o}%V9Udfj&CO+ER}3BdXlm&eZfxg0X!WCKRPjsCfzf zDrjO+s>8f3p*^}0sMT(WsRmAp(LXvLBNw)Zgcm3@wZO~$J;E)w6G998YR<#W`WT*u z-0&#WgR)~!ZrQP8SIYmjLRx;rYb1lwFh%iRq~hh#)4<24ny~z9AYIY>LNBIYM^9Vs z%_3D;I1aw_c&`G@$kl2@eK1+37~t}TbQvaH@b-YmM(NT^x&*-GN$JvFx&*>yopfn@ zMZ|;Is>cfH@`rSRmdztwx}18!n5b%WJ~Lg35_0i^n|a zxL!KuOUEebm?a&>z!Ky!K|02eqiAVTN0?xj1AWk_@SqUGSP^uJ8(i|Fi?4KnUf*N6 zbm=NxqT#Y#y0}P}7`VJ8UFu%eQsXs(riz3fz0d_JAc()t1VxFsdYk%P#?;-UpLCtH z?C3(;TT3VgXbuYT^&#jdFFNSDPztdZL%)u871|adov{;G(t!10U{WiM)MBbOs z{Wf_|jokXOxEDG<+M)PXjw4HgmDje;SKUIxLy z-!2j-NPu>ZES%G-(6}2%0fiQZ;~b#fq54UOfC?uYu$w_Y>9AjUR{Jmvf~~`8`{_Q~ z2fOq*?SpOcEYX?LdE(eLS!XS%LH5zVU_a@rTeSY0+Ih`1*js2e4UIas`QjzO?*3vd z);D<-`#~xd*4uAk1jkkz@QlvgZr zK@(W<=WE^KM(LQL?1524Y-gbkF~E99y0_7Pjul{yD^zsUcL;k1t>t(hA-2-e=I3L` z8yl{-Y!>syn)@-XVZtj1xv|zYVw>{UPz5zM&?EfXS`JGIigg$h!bi)Nix0M8gqcFy zsqwzxk1vv>;$SU?*LKkA&}x&Osi@XM#uy)H{w_4mXer3Z-By!5BfVrsD3quK^Y@C8 z)*T~l8*6Dh3(^FaR7cVz;(KOr!fd-j&M(AKy7KdoAsFXZeE}iJJw$l5 z;k$^!UY29DI}t-}@D)s+pcuvaAyOZP?0jb|T#PTph203XT!Tw3&Wbkms7D*dX2vXc z)4?)S>!$NF&;Sy`#!PFSl@hvMkEV=0L)q8~S#NGl+lmT002}qs;XH&r=W$rR&bYqZ zr+OR|{>GA4HFp!L9al}rxyF-F!?;@gqck2GOI#2^*$!&xeifDz$IlqoSNWZQTY10A znn7XfC%70bosH|~w8jZocgD5SKTfl(kg>n$*KViaW2^LeTX3WQtR_6@QYf|q>L(n< z#w^Ms(kUC4qkr@<@GY2CVE2<+UftSqtbHXNc;E*eS$W2Zh@fvWdr}^ZgHI+Hv2N3U zwwGvYV?pQhw@~`RwK~W>xCvQYs=#cl>!`~dMFFs!G~^&|P#+!YAuTU;7?pyiuk+66 zLc(cP(DnLHk(e;h*DLndbi%DQ;?3qGC+(2HaxjUEOsE9es5;`f7x{HTGy+_xJkbgI zHLxV-1%FLjk@SzAM<0@)_pQRO8e3eBk4!M>S7Z0e&0cHr1jm}7pYA_F+z;~)0@bg3 zREDX%qW}F$f_~PY3Ho9C^^Xk`uBW^f!B3rjZvVezn0uG4qcBzUI(IvD4wsRULT9NYCPVhFohmI&xs@s*BSM* z-LV1Q9t4?B~5tLya9z4i+CXb+3z!%g_9 zAl?g-NB(~X{u=T4J4=5aU#z8gXJ113&LeS87km@K>#-+9VqNyYXa7Ue$L|Qa55E)m zA=Na991kRVFE-;k?8ij_uTl|Uu4|c~(Ur&|vhfg-_w2t*BY7hTZFByLBUc`lp;EN4h zzDGEI6m0fU{C)+G3RR9D`1V5K2|3APcr_wQM13Jaf68kfMUU!)GG(5?R;wHSkH8md z&9(EFZ#Ok16lL8FuO2_3@`qYn^~kvO0_uN_N#7gK5f^c*B=;IZT8|KSuzkOkDaiG0 zu<;$7AlD+gIcjR}`Z;c>DwQP^Ipk4K`;$#0%J!emtQ@)TbmMB=-R!jqe)r$_{mTBv zQ~vYmzHylUEKvPAV=JIzfBgv;)a|pVz3}5jGp+qM=l1W6l7VyYCQ$vlCneWom5AX~ zuSbcV&b;EZ|Md5VD6NY9Cr|bNR)*>SIE7jJp-$HB{pV}EPLTi0H6Gu9q7qMh8t}^8 z!~4oJQM=*bg|hFzGP`{LP0wC;Q7kt-{ZGU1zB8BK?$y9I{u^)ufmI9F?*R7SoDFbn zN5#G~r*|Ad1Rl56$iO2-m_zT{>+qfeuEYClqEGBQa|lOzh!~>x>~FL^=OQHR2{;Q6=xZ-`K{kL^D-`^^5D z+27%JZ2$4u7xtfu{@4CfbAN}Bd)3Pn_QI_i{yb(RGqN_t1 z{U}sD)+seEw13tF}MZim7p=49psl$NQ zVATd8LWeXk)|Q9w;p4p?_m% zZusr*?aU$j=eow=>`8{`Y;u6D3q#JpN+L8URd3h|KU)jlgba)hUqnmyUe`T_#w z8Fbd|w!k%JX;(gW&~8`WzZXgWZ`zeTFr9*ubO0x)*b=mMeo0J8@lfsdWjSq(YsBup zt0;$LRr4Tbd83gV@u`ozCa0{nA?r#jAc8E9pvEjieNGPUoy^^=Um?^ECji*n ztshbpL3_<#A-cEdo@Vfgcs!^I@8G+Em%Ev}Kx4UFFN9;AiU{-0Uay%YUQh~I*NEL=`klYG&1`2K zDOfja#)Z@$biz4faBi2Wm)bLL!Mjsq0IwBZTLZ6sW@i9ToIv1-a#`zAG(jgbuS1|RJ zdkg-;xkZ-CLS#E~@c3{JAa-e3T@be=az2@CL#Ta`pveRRl~xF(h_#vZH2O}sW-WvC zLh@FvX_eK{61F?9`YiZa+w&@%vF9Mv5;mV~FeZu!-4;T_R-A^D<4E{1b;W9@jby-_J&L zvj9P+gFw@nx6f1PH|d4NcntW)9X%*dLKMh0`cE`p>XY*>CgO2f^}d&lmSdQx8|nR! z!A9CPloVPJQl+1TDPG5@N*whK8d7mEqzo-lcQ8a85>Vrh)dZ<6=$j`(Xn{u2*l~}Z zjFuXG$v1NBLaEvti`geT0tLyoegWy~eGlk;<=(eMQY19H9Oh$r7v1z9^z^Z~_#ppo zd#Qiae}FUi_v$~uDtZSRSyLaITY`S5>x!q)Eq0;1Y}_qvS7*}uSK!a!aBK> zDXwy>J3BhT(fS7(6O_AH{EMXW!reXV_qf=ATm2q40%4wcCU=+mJ)Snus(z2b2AnL; zJ9IIKsb4ux@(tD*FoPKY8UhOKQh65ID57w4t{Et940x%GaI2bb5<@3L*ojX1z>VP+ zk=)+WEH2=iAMJ?a;@Enp zh&baoOfn;!%Ofn`LCqP`B~1T$G;C z?R~v6uiEL~=QgF04`MBVZ^dI1sd1dixjZ*8Dh!jajyu|;0Vj+xd1tj2b{%MRxj*!c zv>36o^DxfLu%b(Ac1$FmW0=L8dc3@|4fx8qv0mo_eXqH*(Gubo85iYpy(+@_m?^Fz zqTjLbxV@7tUCQwGYAWbkFox+9>%an14E#x!1blCa8Ej2 zJUYzOTn7`jxaJWlaJ@soel+lXiFoitm z;)KVcv%6b77Hpx2BU}ucBf2AxFwGGGKU|L->Zlgv@jM)DQqJ~x+(N_SV#@C>TEB`D z#dJeP11uGyoL?h(YX;J$-1{Q;AJNhc<@Dupjwft!-9)6o)g9gsy4FE$E6a8fcd~5t ze5YlX%618En8^j03c5G}HbDhoy${nl*@|esCz^W*o?VmI+np()FB&K>n7~D$l4G9K z}vE zgYf|tve1rhI1b-J@85)W^vnvzEv%cN9lfDQ$SXI5Hyz6QCOoeiZ@-H~%$v0EJ;27R z^M|r7f!(GCrqgm1h<_G$e44Amq(6iifK0tb5$)w`s&3B0cfD!Dyye3B$Pfs~Fz@_@ zx#+?joDJ40xD*K{Z0eeS>3Pf?&2s?aKxT0g1wCb&bVE&+FoWJE4@T~$tLl22J|G6;D!0-2F7Kk zRO8fQ+~;L`yJSrfuGt2I>oa%JIV13rZRkUQW|IW^0%ZAEPbAS0fmjMmC^&ucA==Ga zMvev1zSh@PA&UpY45(IgfEP0BI}bI}UT*rP9@Qm?>f6V)(vt+K^lsKQ8Gj=zaU8D< zv#D7aVI7f-wapl4mLiGdX0AD_((yK*JcsT9aQ^+NSRzgMT8<2R?$hSkdvqg}iSypM zS;UY-w8m+C%rTf)Hp}>`n=uf?CW!lBT(gVF5*Z}}jBe&Ex?0J2zC}!t0n+`R7CCm3 zRm1F_8`uj~#7qZ~$y4oxe+ywT#q=E0I;MA+c1o7{=*x5@Q-$edrZbr)GF`#6i0Kxl zWlVo!dV%RJrtU1q?o5X;4PG(|>rtP@0F+CGs&}hE7{T{ERe;XlZP67Db$zW}csrmX*4YJmZQ_T{u52VZJ$b zL9$3UK07@rBYvTXA=WH{N{=o6VtUu?N*>i*P9+y4hS^G7WsISP5?4hFeih@k%(pUz z`CeQNEwEdSjNhI4-i)>IK8$I`NmoD%en<-(#aPQlAg=5d;ny-|wr(9` zQZ3O{#8``eBV*Ybb-I@s(`=ir;ud&w3;)|%@ZW2JOIqMk#@h4P*MeWcSj*3m7W~Q< z`1=^Nvc72HT^MWSb!&m$ zTVT%?;k&fJ-Ysy?7TC}N`?SEmE$|S=n!Ey9;E^r-2e!cDTHp}ITKkAf;Yl``(hxQa2R7vgGY!T0`CJ|B%A$XE*>&A1S*s3bVC9VG{Vywx( zm~jV=uY|FdpGwAB`D+>XXaC-R$@qO3hcMRoiH!Z2zmc&Pe<@=F^Q#&6W$acfHTrJ~o z*}t{ypIWx>mie;1CCo5AD0Z89-A2#n=E~izfAK& zV9jCvd~;$Fd0>b6^Aln-&5n3!d>qDQ%?0r$YheET_}B$WGG@fPEGfY}e>f+k;d^X2 z-JCIMB+}1_&opNw#qk7V{`{=e*yN-|squ1#pmC`n25oXf_n5H6o8xuCxK|XNZrnJW zmxKFYIP}49&O88IY3cD9+C0J^{;2-=p;G$$<45=SQJeHv2J41_QvF3#x^ajD-gOG- z5K!MiI^Al}hwxj8-v9(!iJQUT`{C!0-!T08*o=jO7ehizU1aySl%6-F0{1L> zK+K7ek4uUR0b2TqSs0%Xo0V)f#HFQbK@D-qX_;9W@f1Z2)rHuM{~Vsbv;UuBA$cWbn6n^>g-My|$+2CeAtTER+5;LGP~s};@h z-$zXKU#r*hfy8T27n7Efnq;6ll$k`eBE~Kx*-{)ID&?;wf6lySK4Ma0Q)3s!FZ?H= zw)FS6LZ$TVa{V{S*`?pipCVm~+;)V$HV-D%Y#HOaDE>B6z$~55l z#b>6YNmIZa9>>a<>gwb;8>>s#C85Vo(PiTAGTf(v#_7^@8M=7fCBS6??lJ^FSvMSR z@+XR~`={`ky5)#5PM3&qGW0#T3y^{FcZrBQ6@Qa;BXrs*rfU(e^iFg9iOHG9WIRh- z1h`@Mi5clNolEJ*)lNyb%h#D zo9mEMFJNsgNci~{xDeQl7<`dw-4T%kd_gK_8pax78jqHi4B@lWWCYK;~CK$f3iYiE7M!- zU!x;uN&ZHrLmrXX#8itnisSu$rsR9F|EN_GKf(0p42iY)z0)Mt;?bza*YL&``Hp66 z^T)WVMgFP2z?>)Q4AmQwBT=dY-uR(@=sfVF^#RGY4SpnZdL~54jVS3Kh(~ov<59d6 zmME=PC|)wpkQ>dnX^u=ka;G^Nx!=p!$an(dC{POX0J~3PIt!HIpN$`-P0yI(Nx{#6 zUpjt0@yo!E@|uGm><45Ui#gF`bjvwXg zD1OBM5kKP3h5%^}N&Ch~K7sY}IhvS8GfiZ=7(Il)ZnBQbN!JR|!%WcKCn5TkH1Ma0 z^4s~-byR-G%Wz$$qxFw5jEkApGL34G{)(Au{b)5~L!;!!FfC?U%QWf+hi6*LRO^S` zZc4s^X(H2q>R%T^4rcV8X7tl3pjt1h_0j(np8EG>hj7$8-{T;le+r)rImaOn$%to# z$V(DjXbiFU_QU^gi7yS~P$uH0+){}hQwXr7@K5;ToFGtE0J^}gCazIZ(O6{M*%Mm*RxzNJWlh;yjIgbAO z_?|7KAnOptAXB!-@b~FcdfI5=kGz|8S%`;b3ECWDAxg9CZ}Rl_@ln_~#6glYW5(c* z*hnt)oKql~ETj^*@PE zN>!Ga@=Vff-U{r`glgJS_@G&r<9vs5sExT=+C=|eUgVk%$)q6+&7AxpIg-)e^#Zac zYtKhZvw6AXI78_fGvX&ECC3}nlQNC>O$*mK!OauD|3qmFBR-9TL?b3d#->jbYiKcZ zvboc8JN`aQNvEepOb8E!S230u)6=8Ul9S?=W46YVjOp zs=Xuo$*W8onBHQlYm$5yruZmV4&~jQX&0sjroK!AnT9frVmg;;4AX^76PYG6O=oIm zn$2`2(;TMjnC3GrV!Dy(W~T2k)#Cqz@h+zOm{u~aW@=^Hz|=+OB+Bo`)RU=!X&_UQ z5nU$6QB0$m#xPA}n$9$vX%5pOro~K4m{u^YW?IYC%}M6Zlc_gT15;n70Zap#hA=fT zjba+jG=^y+(`=?iOpBRn@s}_zV_M0ynrQ=5w^lO02BssKj$>+K8pAZ7X))7jS*g0n zv{c=6SnXy(7dJgIOE)niNjELltOIXh>~d_!Nt2YG2mED{BmAbyJ9344VpfJ=;2z;c zIC4ikSs9Eb$ENBg#xKxC!cu8W&j4Mnn~d})XC>>5S&MYj;?s3gp&-03ZK*CaK8}Lx z5C*!ijE~lq`Nksk6#S)`p?1aNsrc(=K)(O~MG`Rl+dp5O_V3?$|L$VK zEaAWXTZa{8%YV)N=VC3Y{d3a&&;B{auvTaP*9vRU|H(fsyy5@zl^`c~ZQi;^AA3B% zV7=vu!Y4NrJ@xc6&p!A33mad2Y17NEy!u-4>uKmx4r$&yYIdK!S<35Kl=ES zPe1#-bjQvwc76HP?y~Ydd-v@>aIoUg;jfQ;bM)KFW8WSB{=^SIR-OFm)X%^C`djts zGiT5J>-Rsb=P%S;{PWUZwU_IzT)kF*y`k~O&09EmR_D~p*~PVW8@IOY+Piyn(0g|5 z)VYgS*KXc-b??!$*WJAhefswE=|5ng@1VhcL;Qyh3mAUSh>`b>8XY)hY!Gy|YDnw? z80-=jB_=IilAMy7mi}-?ra5crvh3w6=FFY<(EOPH%ztF%s?}@$PnZAybo&3>@;8p3 z5E?ddk|})hl!(atrbgZWz_jT&-!6LggHrzgng9O^`8#nBP}^0`14)NPR|8{O?})3L z#-WUh z8Bb)qjqxPLC5*!vmolErxQy`>#ubb;eQ+h?sm!lp9L2br@l3{6#+tskmhpqkZ(wYj z3Ew53{~G4IF?Qk(z>{$+#s(>5T7YoWr;`<08fe#>I^LFfL)-mvI^6 zevB&_`!KF%+@EnR;{l9yJ!JU@GWKLVh_Qk3V8#KA{TPQZ9>O?^u|MNP#={tAGros$ zKI4&$H!{AL@ixYz7?(00&A5VbAmb{=V;EZ*k7eAzIEb-ZPg#D2u{UFtu`lBg#(|8D zj7^LuFpg#%$~c{I7~>qq6B!pVp2WDAaX8}=#*-PBF`mM>l5qs%YR305u4O!xv96aa zZxmxs#t$$yFrLXcfblHGA&ehn9K|@EaSY=njMEt>GtOb0%eaWK6Hh>j8M`pn>~d`w zmoUFA<1)tW7*{fOXI#zLlW{HM&Wv?;OL=r*?8&$*V*}&vi~|_=Vr*hOgmE!5o348#f-Z%E@2$RxQwwAPw*-kyD+Y1+=g*2 z+j(jT_r=?!*(uXvS?BCo*owIGeFM<9x>58E<49 z#JGg96Hh417`J6y$+#WkYR26e*D?-b?AC|#YmnoyH{-U9eHpi79LP9`u}KT>Bg02) z;Tb1t;TdOZ;Th*^;eBQJjaqod+qCeEOEv#Pr2h)dKjSLRKVz%rKS26#(EKxY>&wrZ zu{UFhtBY?_3au@^ZwdV?UE2Ny6KfjdSO-a3V!FuA zM;BT9BqePzVX3PmJ3d_rLYu0C{#N>zT5r5>#Qw;>N*C>)po{iN(3Q&ZrZP@vjQ1r4 zpRDF|k;a%VvYXSDi5Mh(m?>$UaYzDdkjXku7iqldNAG-5tonTO%fyfHu)r5w3HadLiS#=p-+Qe zUtUyC{B`4X+B%5pi@z?+rjMq2;;);a)7B+aU;Lr<*VaK)ZwQBRe9~Gx$+jWx3F z0ouBX>JQ-u*gw@He{i|lLw;GnCTPUvMfHmKntq(>7vbs5r+VcNYlId)6*Zr*whp6u z<_}H2#;5v5<<-)sdgl+M`KS6vSWBPkA>~K&PxX=HrMxa#y9wlm(wpJ%yhvW-#r`T10o7|tPm?e0U7_cvrBC(T zAGy=komAfmYx1LdPw8ppqx$a;-`cvC+5wf1&1dkvLf{ASHv-7a^^e+zW4)nv;*U^T zxyYYCYLQm&vV~mic!daW$JD+Y{Zl*hM=jOLC3mw-v^_U!cT^57-zlP;k+yQu4iLwB zA@`$3+T=*>l=gFJ^_fp3q-l?`J#62m``f&j+okg zb1d73RxauX9Qi5Y`P<{C{=t!-*&;sbFX*{w@x_U9PC|{67xg2K&r$X#_84WqLN#u- z2q60xic!|EhfyAq%y@n>siOVbpKFSEPWRd5Ec>Hy-Tj=t>`x}x+KaT0+4D!^5v3N* z{$+nM-u7H%|6mU<`v*HeRg~Ycf1u|T;a%%aCVhPK9McCSn%-1wqK4rcl9P&Z+&7Q8z*CboMwR~y)qwH_TJLEG3 za|wIPmFb7t>W@r6($)`X>D&85O5a}JWcm|q{hdtTo?qGnNVV69zmEM|CPrS$hxTM; zJ|b=Tl=2U^)kB$1l#MUbiR7nC{WjIJzfXsH0Ii0~bf(+tk4(qjo@6?qw(>j1+x)qv zh@PN%{->fP$xFsN*#VOrX4=X_gBZs!e-q z6IL-_Yd7A^cVYil=4;^3?igU*>CeMQ`T6!Tx<2KgBqZvDP1( z7=O(CXvXg{*0G*mn&a>k{Mzh_**cr)WF#_uz>GX9Nm1LJ2HyWK10QO($! z@fVDJ86RaF$oMp46XR2iqZ$9oIFa#5#@UPyF!tp3*pG2O^Wzztn6KGIH!?q&`M%5_ zz<3+;6SeUCeA+QCWqt(XL=HccaRu`;8AoyW_Kd5T|14uGQn9piB+dc(7 z^Q)Nu9Ahiv`x$3*e4dOOn6It3V>rM5%y%0tpPwA9@qQHZhcn-s`Pqysm_L@WFZ0(j z&f)YsGS=+OD&s)*U%=SJcpBq;j{hFU(ac}Y*ueb$j1!r^h_N^GeHdpmUuw_rF>J<9 zGCzv_`!U|g{1nF980RsrX8*yAOPT)|<4WcSF|J_#I>y<|4`5uy{8t!T8B47l_Tezj zV!m6Td|opc=XaC16Ju}YKhM~g@pQ(4j9*|}#Nj(LHZi}D@iuQ6|0u@M%wNrTBZu$8 zIFb1?85c9(i*YvdXEDxaypi!n#!oQb#`rbHrHr>}e2%{>;|k_SGcI9%H^x=Wm)ZoK zuF6}MZyfWj%zu<|1LLiX-Ns1&OBs7JewlF@r*{`)U*^xz!gG3k83!_dA!8HcuNX%& z-pe?VaS7vW#-)t&8Gp@qBjbaND>#4M8E<3$T*jJS|19HD=I>!#$@ojgR>oTx>&8m? zyr}u-^m;J%X8uczeHqVVT*UGj!8nllD;PI0KZLP~`Rf^%GCz=UH1l&9moa|~<3#4? zGOptM_GFyR{D&ClGfrh(&Hj5a-pKsEWL>-FS*W*#roY5FMxK}5iewGhqlg+XX})Plb}U+^uL&` zDH?wXTTL{a3@yK-|72+395LxS9B~@7503cZ7JSl@I`T=&>4-Hgx0e2Lhw_rPQ)&a_ z9Q1(FMgC0&UyFh4QU1ui7QeK=Xx2Ki+mYUZ-q)0u)JH8q4D|M-JsxSNA+3PCq@5j3vK}`>w!qz5f2U^5h2Ti{d z3+=GHr2b}sEx!_L)=JHPoGt&7Z!eF;Nw)k+yx8`fB~EP-f4Z$+O1@@&)6%2&N*wcN zujkVL3R^vt`WUMHnt#&2(A%DlI1bOOIc~{s9-iLubo4LZtsxhgp8a`CyvX+aBu=!| z8;Mi!?x|yZskY}Q`I^;Hi$C2#o>U(BZje3wbX&cZ;Ylm*7=Km^{!)kdNY5VcfXVJb zvB*o>p^|L!ly*bS>P&WLveRi+R%wSOd297k>R0XcN5fh_Br*Nf(j)zAvaLQ!eu{XP ziF`;qh&_MOPGfJE(r#&gZ%Nvp9QhPKwOe^fy|!l6COa{eU$dSQruG_ZtH06?s9Ce6 zeVzIROcecny+Rql*IM&z>J?XV!K#&^Z@W-xt{A zC9$^KL5J~9T7tA5L&I8&*Ra+irJY*aB_a1o$hWd(`J|oCo*!w)v9}+I?d6e}{%YaL z{;BP@(Co@|a)8E{_5&>j(&y8dq{X1wM;!TNFLC6PM711g_5?d7J375JucfE8A380F z_A*G0v~T?TcqrX7A@{%YFWnu-PifE7Qm67#ebsVHnC!iDdXSbM$^-e3Be%5E+T|@z zjBvzc_t$o*&^`_-y}ka(eE{0-72;EU(RRPc{VV>cBU=9<_X(ui#&21BepbYg(KB+j(;4-%Vg^u7HZHfaws&nXZoB#gGf?w6YoE`b(HN?R`ZfkA+rF*pHZ1+DyTqj!a zD{c6t7wI}=!_xnL8y3+uY$y{He{Q$Y?i^`JbN{+aE%4jI&{ihvL-YBXPCDPpbwKU@;_H>I_&4H60ZD zEqe9KITTO&_uH2cjd^Z|nP~CSXCDzXa%3*iD7Q`Ph?YHk=W(JrMGsks)-L|yNutGh z?Vci9{pqJq6OEcW|5>8CX>Fb-TDkZA7l_twe&9u-hQ@0z5l!Fw!plU9TL->Mv}FGe zuLqGcO@ z7PRulo7)6_XYt!aE8h-yhbZzPsP3hm?-DL5T>TzV&lAS?i55TX@&VEGg$D#J`QY*G zgv)$=#PhEmuujmT@6HMuFl54q#19$yhM+Mux{nBZJ}_U<>clSvjSA@TF}YW7%N8`? z_z6Lax8L&#xmPM11U2N=2pX_#lBgdseclu_B7Vu3z}Z}o1i7PMtwo< zmFEft4JbG#Xvl@JyU0D=?I}S$&;2fF@v3oOl6%gHrv%kq{9VxE@}RHCy<}#QpvCW= z6SS->aJTUP{1bu(_*V;Byyvbm5&zT)g2sHAENG7V^MdMvcMIw{)hcN1_>Se|zu0@M zpgENb1T9)@5!CaKj|B~w__M_ME_=v-^?O4Et^8!VptY~96f`Gwv!Er0BZ3xvc12J; z$GsHJ@Z@+&E0YAxsV@>V#;H`$>e^og4cPCxPn2Wz5J4+@PLsHJxuk821=U3#5H#S4 zi-Lx1=_LB6fGvT7Mm_P6pheN^1Py8bj-ctAzm?eMnxK{IyC0zRqN-FuE8XJ-&GB6? zso!=%tG&LL{6885O@F=DK_QRv;{^>lk|1btutm^-7q$!PdGffRQO*s58oKtZpm0U_ zq#)6M$nv(<_fAiy++We#y12_zqwz~ zl0U41)_&RkFr|~TdZ?f=gYFl!_^V7o)0O81t=(2CsNv2}f(9tJB~9x6HH8mZV-z$! zCr;4X!pDpqEop~nL3Q6|3!1+2ML~wZ7%OOq>tsQzvu6uhdp1$f^oiMm<`h3JsAt{t zf(8U_5w!M^PX&$oWS^ih=Z;Ih`&mKD{OSbtoZxhf(hqsSQ_|VJ1r3NEDyVL1u%MN} z5rP)`%n>x*d9k2TKV=IV^7i9`#-u+lXz{SO1V#TKXxa071T9gH30nK>Z-S;TyCi6g z+igLMpKAXd;>) zal*dfj&B`^`KF*T__d+E4QUH*2Y>VBPo9_SUza^y7ll!CiJm_CDg&TG{=t`unS|TnMhY(*5rC zE7~dtTa~33yjd1}|3O%0hI%R;Qop&Ro8zTy|I|5s&||*^r@T4p;~ld+m8ln6Pp+8K zS$Tig>#zPap{p|Z!u3zKCiGJNx^+FU`lI^b{Mz&b)6ZWC{`m0Nwrw+9l&$jy{;?sw zm-5GshnKzjTW=+%b-S&*2f8UTA6=C>4=whrAKhA+vS8)q&Wk%KV;AHc{YYu6{Q9id z`C|cHl;Oo2JUy56QjB#w+rRvHSH*AFmWQ0&nu3!T9rz~rvq8#`clYfY(#lT>jsIm; zWw*xQS4TU~iV5*jVtTB2)^f~US^f6Pf${HmQ{JBc+WyG=j>_7~)-n4!c2gSMhL0QC zc9=5uwBOnNUAieRP0hT$^@{;Y-n!~fH(b3P{9OCUgPVr+Q#!jZH(4_qgHxjqxa~S} zx3Y2a1C8gs`Y5HthtCe_HcYuUaAAMTw>^}cKP|ns&F`(8J2|K^aF=$$HKOML z<$*V1zx7KPqy!Ay*Rf)Pw=(MM2it$K(4cH~8o17VWIN@DErxDqZ64KiOf^P{sY;1$Pzo8>HkulpUC#c8{`s%exVa=L9J|mp=LP;=Z-P ze|8@`{Kv`Tlx~HYE3FH?6z_!fmoIJ@p(HF_cJ0-tgO$?a!rCCy7-c~8JC{2A5~S=L z-D>tP`^G2-57$(mztUZK&tu@)X-Dr>cJ3HF%{+CCGX1gq=LU{!qts40+g|seJ3q&P zO2ZR>e$rppUn%~s?x^J$%9qv7eW-6AK5(Ezu(Cb+vFan6hbgbNEBo$^bwSFpIeY3# z-ZCgJ*3ao1^Fy#Qx^~l}`VZZd(_a^T+Am*G>Yu;+O2^Kll$Z2x%zeD~Q01*1W5P3@ z8={GOV0c=jAuWnt=v-}?{iqqtuibMpOtqm^4Z4=-5P%~y#H zKYeh){TG5|{Tr_2FMZuFa@=SoH~4(@Th_tK_C8%}50ng666XA3w%i?{JUji1i)jX& zgnwjehUJsm;6Z0%+xN~>mF>IQ?0cbmL-0GFf88_LS5cm6*EhU$Q$Ke?VExp zw(a>x*z|jqM^joi1pFSPj7dIO``e5CmE4jIA3l0ylv1(nZe7ZxkxK95U0=;tM=OsH zUhd&JYlKqo6#K#QV?oN9`n@wf-*i`Qy1l$0-4_R(Ecz}daOYTM%c?zL%TQNcpmKU~%z!;l`6<8oeKz2?6@!&SscS2yF7;Mw6ZQWJ zp4wYE``q%Z!!g%`C*7ZOe09RD;I!Ws-&H?iw6Y{mx#IcxFr~BS!kwx4W0dYsY_Ck5 zb~E_lL4`e%p12qJNa-8@?495jbZYC>(IXV^*E+TO!+R9BqfyG_7`Js>+NesO>Cc|q zJ#w7VKET+a_QOCWuE!7S#!pa{QRXoZwT&CA{Cd}pnU9Shscbp24kJJC0NCJdjfObMSDb+xfl)CoZ9${<37<)3XODmVra(N1pOf_TAfh@a4BI z2ag;#B)@k2MCGAL^;(_J%>Zdh%@B z^bJFmu=OsM`W{o1nQ!0t?SV^*@@4!vUE3vL+&@GrF=s|U_C~5nseQx!^7iO{$?Gw|7HslltkY;`cn`n$(BKPkuE%6u4c# z(E9#Os$1`swdHM^)K*W1H5@#DM=iVP^l_UbchnlEohx!axT7xmy!PGUPu)?^MZNG= zck><9{4b|NV`tq_GrRx1{Hd{b)Vsw6TJ41YchvQdHJtt7U$@o6y^4Q!I(%C_mAhkU z%m=sC^=t0#SNHU7b>xuJ>7OmTt$vh|mGj=*+iI`xjrf0C?LOz$n?nZPR?TIp8$NZv ztsdANIybfMmfC)kbHU82Tk0QQJ3XKB)h+d@(+|0Ry!nYJr6#N|F_h~ z!aa^X8+}WyZP@FyL4|wq&@rD5xTRK?t_r6ez~bWqU6Wl z-gi^2+*#hG@x7aBzb0?LyPvzMT1Na7oV@C$>U!y`vuEOOs#AMdKgqrCrh2q>)nwD? zo9fA&TakH6+OGgdg_MSBYl7O^!+!~+cl35>$Uxc z`c>TGn7=mOP-mYGxc~RHH`J8wm0O%rZm5&jC$=%px}kR8Y{dT?YH`o~j}IPlLv1x> zMdR_Va4&Yk{~PMb5uVRId9hLTPh8UNwI3VRRd~m)@P2tid1HgBj=sF&f!`a{pCx z?H!Zj)Ve_pS>tru_3CxC*VG5^?|b*0nLr$7Gcy1MsbwOil!ud9|Z zpDlT3({;6Cb9QdT`s=F4ftNbEEx)dQ`1Z(iKQFqjrerzoIXv^aIG;(46HT4g#%F;Q1TvOjVb8+vYs%z>j&u9Gd4_#BAx-sd%uASG^vIm`( zxW0EyedkEO%dxLqQ~!+44fyWKYif*ZwA0L-YwFk~QRDu6_?mj=ft5R7h`Xjredsmy z&e|`2??369TBE;eYCGndI>mL{%%;KDRIi3J7uxi?rtaU@@pNB3aFi4NUsE?l{IX!f zm8GHv((<`p32{E%r9!{a#g-Y5De_+M4;pZ(B)PYqYqF5*J?_zuU`wt@f7C92cS zD{7nVZ;jt@>5AIw?Q^?^oVlVN$a?3glP9jIf4uw1=I0JwQD+S5cQWS7D{7aZ4?72a zct!17u_Dmttt)EW8t)-JU%a9|>6UQ3Zz1@Bxt#-Zuc(*84jr45bwxF``SnU+(iOE~ z@N4sr&by+PXZzjk`M?!*V(04jmQTE*eza@WoO5HZs5g2o{&ViIE9!1fzsYC&T~R-) zIUl{u`-)n0EWqh5k1J|St9^qjoUW*e!(RV#U45N8<=CCq@2ja(U&xtK*1x(=EspYc zYIg#3NN(iKLv?D$(GNb^_*I=6;^*|f%O`cJYx$L@d%j(#zS`;BAmgib>RT?Qb26T( zQ#+UBcPh!RQzMVQ9(ZF_oqF%U+|sDbI(4Aa4=X=TtWz&Pos-`8p*r=WWlybsWm=v3 zv`;wx*Qs-2y1nqZQm6j5>!s|OBkR<`or?Q94X#tOZ}!Rkymy`Y`wUaga<4k|WZ5G3 zN$u;@P6j9ZuT#@|m#=cUep!tuT{G_L#mlO>R1Ny;%w_e<$wL=itGcZA%6;Sg*56)M zXIJ;U)p!48bwe|rgM!~|ElivA=4Svy`{!!2v?6GlS?N(^{0tAS3c4yA1S8p*^mA4z27M&_x^XU z``uB+RA}9~ChLe|x^Kxh{Dz+@rVAfB?}|mQDyEALKY95LFDRxP=HIoeH=&sNADn&k z-R~%-gGc8%&yFdkUY$K>hfgtCZ;SbU`k-Q}&6X}OeN!>L-LU1c&>qEfyKaH@)Z20X zo4z>@-=vtn|KZY{Gg=kXpNh8bKe$;j9S;n>x?+Q3+O@?gzyDRmlv(+c55KiaF+Fda za%kBy#iYxekAH}_bmw=zU!|C)t)F@DUZZ0A^_&}z+sYM_HdC|1Y*0*J-nQ!eS$f1* z55ozpF^=f+o9aR{TQ2y{%!vI)CR$^;p~6OpO57;o&m3gM8fcGj}_YI@pHwky^2bR%uGJf41(jmI;i5O zWGmXGf?RlVwC!B7C1D%UewAdSeVX6SJaO&$WMdAArSOLk^LX+sb(4hODa82(@w?2j z#5yM7+#w>wCAJTjT~o7YKCeABl~4UonwG<+wPmwuHZ7Z0JCRMR%wp3jrlh1S4a=&~ z7$y8bP_ZqW71x556IpSE&LQDP&$4SKBr_};Pe`Erq^YxH6vcbc#=akloYY;v%eeM(dfxpG@SM@0;ljXp_ zoQf=mnujfm$yoINBgml0W8}G{#?MmYOPLbOT!7!YxlGG0Ys+Ff)P)Xd=_>M4QjwMw z)#kIJ$~;!&oRrFQfd%BQcEyESZ2hc!HHkeJqR4 z)jCFcl@nM&QLcmPfPA1sqse3$Q&^V9Dq$4CUY5toY&up}TfoZDPGwHL0?(6037sQ5 zUt;+?Pv-B%=sN(ZLH3U4ZIfytL9HzJBF+Iwm zM>+I0#Y#~M<33knxf_tS8V)6LUov? zl%t_>X_T@xEPF5d4$-NxjF#qE zQ6HuAP?xsnP@m7T8PZcsoh9HC`yp=C6HvBdnnQCiUtu0aeHGWBE=zM*DcYhG_EOkO zVK1#INpX8V?0K*w9Q9x7guM*?sj`@rJzAnnP8B8#68VliYp#X&YxHN#QDtcFvYL~9 zuGE;zW>((7W=b2GPJvg+oCNO)+&2RjiIn~a=KQuRFqYS_X;KZ#6@I%kX}|N}cP{*f z|4yBw^mMSYi1g7ZqO4Wa@2KxOHO4}9Y+_!+JWAs|-QHczm}4L_l88+4OS4c$l+{_} zD75M(BUpN$2s`>;qa-L`yA$VI+6tiXwRhUN|<@}c`qnmC8(N z5-2qK5&h=rspUDW{Ma4L(6$S6(bt)-wu5OacQYB+{s|&);Li;s&r2ucKGMz8Fa!J^ zpTlyTS&q`P^3R*uR?Mc`CbOyNzmw0M^r(6lPG%gIx|Ix96S99sQHKVRu z{qgJQA(uZB;8|eAeOglM>EtsCzn71CZcx(o^3#X#{PggMK8FuV+709P`B!A@?c2rJ zP`aL{=b&79YJ$BpJp);dauKt~ z8FzQTfagSr3vq~4=Mwd?1t8Vg22i~&UmY(Je&|^1%yeW}Y4$`I`Bwze$(Vz_3n0^8 ze6CT`g--0i`5uTHVx7g|23r#DHkOz;fBO2H+9b%L7(w+rqOlm#CbJR~?Ectr5Gf*%PMHE};q6RZ@xMDS9< zD+KL=+XUT$KEZ_GVZq-B9v93L^*Bv%u3(j5wct8IyI{NEoq`Vuh6UBe!he>`Ra+XI zTP+PvY@&?4i?3~1*s`S=8+x--G}!uV;TES88*hsp(3aWQX(Oz)>Tbm_GKH+Gx3*%7 zrP{{p8|JstUTvhYri_z2*sfmQ*xFW&4UXAsnKf-q3+o-&6AJkuyY@DAm~6{iTCUsT zShA&=?{VAQhAp4iTNH8y-w%9E^No$o$Z==Jvercn8@6oP)ZnyW(|9<^o}93z*1=_ICh~E_`FYY}x^B$IznF(ImN(RYt>I(#LTOP0ZNWS?z&@f4#)Qpp zwykzFHmhyU!V_vw>|z`x9q#d+ieq$kVt?WYovFJTKOzy9H*UbD&|BF-Di2lKe47nT z(#qKPGnTj1+eG-{cGT0x%$oYf)`k=9gC|znaBZWlVWGX=$?~((YHiDjaCQmhv$*+d zjn0oElQ28XREb6jni@q!LyEI+vyB4Yk0r6KB->WQ&8$g;Z4#ydA-Yv`N`O?!&7 zpYu>Bwz;Qsq_9@4$JN-19Xj>l5!ZSpwZqfZ3$|?B*x*F3I5lf6_Oe#@b7TrHe{*X~ zQv=nOhEm6rK<5}@Nbwv>k*>G2G}3UO?Hhl@7Gk4xp2S+Vi>a+9+%@pjMH+DLA3COg zftIK9i9~7_(-50?rEdL2gZg^A03_=lfG`o=v;O{Gv25#4#&S?-d3Tl7J?P>q?;(EQXr=PuEyxQ>U#@GMy zmx}IRob_$`&J6J=@3Rf!0(yAIk&e&Lc$~DJ4#!vA>i6a&?f-n8zj+$(f1a1&6OVts zFJDB8pS%$k_vVu~YWgC){rvJAuSakj_u&(}h2it_@pH`SU%Z>pTji0p)q{!Nx^wH! zQ-#s@3$Ao|Id?pqI!>B-yGx}X@t^S=_855Z$Nt~(5jU9y5B`nu=ZpA{BmT?XN4lr6 zce)3=)4z&yGMf|WZ|_pCScUI%R~=q|*ubtiy!mj&$%sPR@!G)8Q-8qSemlQ1p26q& z^!%MBMIWhuETJ)m!S?arZ>OVu(cx)7PH@}!{u;BmJwB1sc%0wgPF(jlDy|#(^=2pM z!cVzQGJHzlX9&_D-M^;||JJND_^yQR-Jj+7NPH*LnQn*l`i#%WTg9Gz@j=@g! zL9|y%EDpW|se(NTzB3u|ppSh2KJwlC$oKa2KL0g{8}W%BL3*GQGfMCd0Xp#_$aB!m zU>Nc`bbPL0(W&@en|uHd;X&679iJCiYbn+hK*whThUb4~hfe(ZbgV5Q9h`_Cyy}3C z=X+Lg3Xg+lc@~50haJ!D?9rJ#9XzA6=MBgkc07NxtkZcMGuRB71-k=$07CVU0HR~s4bwa1_H1B{={0?wH=*0JDLJxy^U*dI3ycj}h5-ma}ej7q%+z+0K&kdxP zg9%6v{2?9@c4Gcq9>)M~J`3%HI8C4*LiIyDBJ@FU_t}iSN$G&EL588Hz=L?lm3g(K zz6XBxJoG*2&w=kl$Oi?~!8QMeux!%5_|<>gPsE4g4m&#U4Xs{QR@xd4I$t5fL>wugPSho?Pv#M5Q-lMUlsc6 zVAe(4t_4jHO2-U7a54NwUi-nfAY?xRe(5V*p9?yL-UdDop?dBI=Ul?`HG?kFk&YXD z0^)|A0N;glLRY{qn^1n}>p=QTX&QILKSLl6H;&<}x`mvVa^xD?Wa_|@S0%P>x$TfzGw%b^c|e}Ygt^aJKP2=Zij0|>>@uE4th2-3GP^owyI;CjY??Ae8f8~9C#0(&R;A*2}gVet7H ztV4sn9~^{G`oy2D#yku?1um@>Z2|6vP`-OW>l*GSapD!+t_5F*kpIN|wW2=33ZYkm zH$y0$PVi-+9|g%OXwZoOF~bAr*7qSJ`219LharG+O}aV!3W|22=!0mI}k1G zgW#N-c=~g}Y6#_93(mipu|U*7i1RnJ>Ve-rAb`Ao#(3(I@#Ue(-10G z0z3w3$F;-@ZpD2E`o-X8NGGKU4(~u)K)38fn?kf$M{pE8bQ{JtbmE-bQMb_Nf<6e9 z*AIRGp}dZR(|2*7%fK5UWOsv;I(VJv!DSG#F9&ZCx(oC{s2=>_l--!Gkk>4*210(; zg112^j{Z)pi@HnnEAV{?jbHs9%;6A1}23VRZa-^ZVe`oQJi!nlB)_(mt{?OOCnaOeT_A?U}zJNM(=IrKfC z2SUE}fIky@3jD@{e13L==RL&BRRwlHZpO7lCq7>Qy` zLXU$AgxZmKd5rsD0Uv;2U%cc;%ybE`+`g?1xa>5e<*=enl)l zfcXb;_JL;|s9l(gdseaDQq*J%IoHUpR+;6ujgq?wc80 z2cdc+PJ5cqX=UKukVS~^2G2?GI;;eTAe5Kx8T<|x#Ev-aV8!>iZkgrAVQ&K8fXs#dCb;57TnpU_J_4zRo&>-368aeQIQS?;!Ms6hb7_t3aimG4^|hoE zX^kxDL|W_WLVO~vJADp1k=Ai7!t)Z5)_+obBCVezok;7{NGHFi(uuSti*zEbYayLT>xM`t()t|IiL{P}bRw-iA)R=u&}prSEOfNOzmG4V z2CPQ32c#a-0`WuQkOU+Nu|wE7xMxA!&<)@qXap^gJ1Bg_v9bzl1Rys;mO^SFHb@&} z5#(k_9mEZBK`am}!~rowIwAi)KF1nJVO%0ReYc_8++E#m>9%(}y4$;DPu!F6Bt0pQ z;$c0y9z&0@$J}G-vGzE6Ts`g{xhLL}=t=gZdXygK)p-qGqu1=Uc&%QC*X4D4WpCV@ z@Fu+}ui|AsozLJi`piCy&+2pdTt2r?_Qib(U(%QIDL&S#>oxQmd(FL;UTd$T*VXIp zm3!m8iQZ&ys#ob{ex2XoH~P(fi{I*Z_+5UtU-rlS34hX`@+*E8&;<+uW567+1grr^ zz!h)@D0ET|swH4x-Qs`ZI0`_AWy{7}E??n0iJ$jU?9OZKPI#8lcl*x}$^`Tt-DA@qY zHi*&aZnT7q*7&VMn+<>-nj8sL;k?M#gQWvpC>=8$#J>rVEBb^aB;*Z25eUU_@Kaz|L zL{gE#h!Po&u&6ewi|V6>XnE8at%{nX)lo~dE^3Y1qmF2M)D`WBx}%*@IqHwbqkYjt zv_G1R4n$MY!6@}X+*9#?pTFNf;2-o4`?Udmpgd3&s1DQx?1A<`N1!v{5A+540|SA< zz;Hkt)CbFhRl(|DUCvFcb|%pPlxb;LSj{#akEKQ<5> zj19+7SRL-AfL7Ma<#Lr=E!W9*xn1s%J7vGzC-=((@}N8{YrFJa)m?R6cJ!W( zuFfuhS6^3u*Fe``*Kn7%Ti;#YU4;=Gb$LeV%^L0Q%psM~l8!j(%6&Qzv>|Cwg38Pk+xq&tT7RkJhXAmV2wb z)!sU<-P`W%@OFCr-ac=?cfdR79rkK{di2dIU$w8!XZN+EhjyZO_MvAEpjV=R==gs0 zeVxbPF?!4%i^uA5cw8R0NB)05^5eloFd0k*l^_f0LWYnrWDZ$E){rCQ3b{jaC>~0L zlA%;c39+y)YzP}MYAqPC4vbbeMrs_RG>H+aV07v*GL0CO7K})TIvQn+!~{lR3L}tV z^cgVn%ouf6j5rrYn~af`z$i;al_-npVuqM8W{z26)|eyain(KQEFMe5lCe}wq5f#Z z=MaF&I@ur_WwUINt+GRQ$!=Me<8ney$|+fqS(mQM&}Hm0cOkBY|DB-~EswUw>;oJA z%IRj{uU~KJvSL2$K!29eqvPn)3G``KZ-4J#uhw7gulBcN{_VsJJb>Ak1+|!6^_XAF fF~e42j;+Snw_@}=F#cVb0o*jx{`>epS_A(BTV(>; literal 0 HcmV?d00001 diff --git a/priv/khash2.exp b/priv/khash2.exp new file mode 100644 index 0000000000000000000000000000000000000000..344154ae3bf116cc46f71e0da89853f0f4e3c693 GIT binary patch literal 688 zcmY*X%}xR_5FWt)7!^+*JnVr3Mr{@(28|{}#AsABqMn+VWm}+e*^;&>@d11ZU%>b@ zK81I0-gLS@sFTjjH{VY8>vonsXW<*|D+4eGu@TNZ<0S>ABXoxTmQYieYq$vG?P0gj zN@!TdcYLHpl54PZV4>AWA7LA$o^t>5eb3XqGGRdgq3GO+P#BoUmI?G6tYVyukb}{l zCI1#+eA&eV^!4D7y-oJVGFZ;uH)9Rp zBMyhV^is*L^7_t^2)K5Nqw4k={S~+sdgujC%M6*2wCt94Xd^US;bAqWw{?0gB?_Fk z5SA*WjH|j;s?fFw>9N#iv|XXr0}TJ++$#1GXvrH zcu(H||Dcd~7uR6tAkTmh|DgEnjKty$BZl~Rzfd0^OnHWShysXoh)1ZOcf5;Zh$B!# zW^O@zUS?W+W?p6q1Drv!yNwa+Df_) zaJdwh&mbPwOUcQB_=Ql&n^?l)Ab~5TjhSJ`-AR|ZfWF`W`UV8_Qc{yj(^Y~YJO%~_ zAfpGyfzl2P0#16FDTyVCDn>9V5g_9X5I=wl00RZ6*WdtD9WX{AfCotZ1L6k|E|_#+ zP&mK@WC=2Gg85tsPpLBq3$OzPFMuT&82A_@7-r`Q`DP{;j5CH`s(-{~npga)8#1H@^$w@ubN+m`nmhk+U z(#FK#{`AAo9blJ%+=o4tq(G$LBvL9dh6#dF$r2a`N`q1fEn=UTIF$vu0cT-?lnjZ> zTSgX$(gBpWE=Fgay#urX62sU-4Nu-Og#{ESZ-Vj`GH_sUfaNU{m^et?6&MFfgVa$w pbcqRB8KC{tN{uG4B1-{OTY&OsN}I~D8z(`j36!tl#W_-z1^{q&G2H+F literal 0 HcmV?d00001 diff --git a/priv/khash2.so b/priv/khash2.so new file mode 100644 index 0000000000000000000000000000000000000000..2ef6d347e3fdf312972782c40d822782ad7f60d2 GIT binary patch literal 72096 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 delta 32 lcmZoT!`g6$b%OvSb8kXevoPa!VMfMAEf8n>bZy2=D*&)d3L5|b diff --git a/priv/neural.dll b/priv/neural.dll index b0154e0f2ecf723b0e3ace1138552277abaa87ec..3b9054f1d50be6b1021aba38f29aa4ff88a8262e 100644 GIT binary patch delta 35 ocmZqZ;cDpNTENJBEF__snX#Rjk#RdSBa@>JNMO6WF4N8k0K32nLjV8( delta 35 ocmZqZ;cDpNTENJ>Fd?j&nX#Rjk#RdSBa@>JNMO6WF4N8k0JjGT{Qv*} diff --git a/priv/nifArray.dll b/priv/nifArray.dll new file mode 100644 index 0000000000000000000000000000000000000000..c11259a47ca7c99c4f40021b2e7ee0073919a78b GIT binary patch literal 91648 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 bWKJ8_tgbPy>&)y1bGs$i(%)?F$R7AlJbVdI literal 0 HcmV?d00001 diff --git a/priv/nifArray.exp b/priv/nifArray.exp new file mode 100644 index 0000000000000000000000000000000000000000..1f3abb46684bee48d01c68e74786b32f6e6e4a0d GIT binary patch literal 686 zcmY*X!EVz)5S=8n0aS(f#-Tz!kz;S7RUL1%-n5|~ zz)x`F%*XHO{&W2x$^cV7{K=hP0!bIpBe;dG^K||<|Ewu~Z z#bTkDuI9DTmZl17{K|r@?^5;DLfa$zgfz-D;~x50&faS6e_>%15rT<~rJ+3}J%W~K zA9g22F04IIiE6Or6xCs$r05X#afzFBfd4N&mdVPG+>u#@Qt_tOc*Xm%5gG?g1RwYKl~O1@ z7y&j~tcle=YP9%N#{5hrW_{e^RwEf-Gux4#QQNT>p5J6QP>Us#A^xdg=Tf5XO!$U* zUKQ81VeY>IySYU7=Uq&hu`Af|`*(vvxe z|ABb*FA$WTym=F$&|@h*_TZ-kwQXNt)so%DjT@7Tqf9%0FWtZ(t7-_#Fi}4gnWrm|Y$j zC{PimCq%p6hea!_t=UQ1F=um=8U5`B;%^!(oLUHnA5JfOiAgsx;udq(*FJq5UiK|u znv`8>2x%ZX9dn&*H9M@!p~s}0(feCC=wM)A^hCtH7H;&iLi@=$HVtSeD#?C4ViP?< zQqR#JeMru8ZwR!&Q|uM{7TjE=v{l-uYA-S`H8Yi-7voK$hb%_IFXfo0MX9I6BVTYE zLXj&K$^>_>s3=!g!8sHR#-~EEHUJNCUV1!!{!St$&M`Cz^_WD%!~P-eMB~+gNnx>2 zVNrj-Eb3gg=m}ZSs}ZNBV&HxL{F{f4G2Wgnjz3iI1KBbzot!NP(h!of<^NZ_{6aMThj67jwbGw2-=BCq!;KGo@X~M%O7}lD literal 0 HcmV?d00001 diff --git a/priv/nifArray.so b/priv/nifArray.so new file mode 100644 index 0000000000000000000000000000000000000000..e516d981dab0e90df565d7c00c5de1ec22f20ba1 GIT binary patch literal 18032 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 literal 0 HcmV?d00001 diff --git a/priv/nifHashb.so b/priv/nifHashb.so new file mode 100644 index 0000000000000000000000000000000000000000..da071506c931e60cab49afa1f60d98ab8e7ee61b GIT binary patch literal 25768 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 literal 0 HcmV?d00001 diff --git a/priv/nif_skiplist.dll b/priv/nif_skiplist.dll index 61d1b06c6367fd080b21508d43c04cd5aef1ce67..f6fb207cf65044f518eec959ac5f4bdc324265f0 100644 GIT binary patch delta 32 mcmZp8z~1nHeS-ia^NEmzW?{zd!i?kgF0x&Z*b?hA1M delta 32 mcmZp8z~1nHeS-ia^WucCW?{zd!i?kgF0x&Z*X!3!$@ 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().