SisMaker пре 5 година
родитељ
комит
6340783e80
54 измењених фајлова са 5232 додато и 26 уклоњено
  1. +0
    -0
      c_src/.mqtree/mqtree.c
  2. +0
    -0
      c_src/.mqtree/rebar.config
  3. +0
    -0
      c_src/.mqtree/uthash.h
  4. +843
    -0
      c_src/couchdb-khash/hash.c
  5. +240
    -0
      c_src/couchdb-khash/hash.h
  6. +658
    -0
      c_src/couchdb-khash/khash.c
  7. +12
    -0
      c_src/couchdb-khash/rebar.config
  8. +843
    -0
      c_src/couchdb-khash2/hash.c
  9. +240
    -0
      c_src/couchdb-khash2/hash.h
  10. +658
    -0
      c_src/couchdb-khash2/khash.c
  11. +12
    -0
      c_src/couchdb-khash2/rebar.config
  12. +110
    -0
      c_src/nifArray/nifArray.bak
  13. +108
    -0
      c_src/nifArray/nifArray.c
  14. +7
    -0
      c_src/nifArray/rebar.config
  15. +455
    -0
      c_src/nifHashb/nifHashb.c
  16. +435
    -0
      c_src/nifHashb/nifHashb.cbak
  17. +7
    -0
      c_src/nifHashb/rebar.config
  18. BIN
      priv/binary_tools.dll
  19. BIN
      priv/bitmap_filter.dll
  20. BIN
      priv/bsn_ext.dll
  21. BIN
      priv/bsn_int.dll
  22. BIN
      priv/btreelru_nif.dll
  23. BIN
      priv/cbase64.dll
  24. BIN
      priv/enlfq.dll
  25. BIN
      priv/epqueue_nif.dll
  26. BIN
      priv/etsq.dll
  27. BIN
      priv/hqueue.dll
  28. BIN
      priv/khash.dll
  29. BIN
      priv/khash.exp
  30. BIN
      priv/khash.lib
  31. BIN
      priv/khash.so
  32. BIN
      priv/khash2.dll
  33. BIN
      priv/khash2.exp
  34. BIN
      priv/khash2.lib
  35. BIN
      priv/khash2.so
  36. BIN
      priv/native_array_nif.dll
  37. BIN
      priv/neural.dll
  38. BIN
      priv/nifArray.dll
  39. BIN
      priv/nifArray.exp
  40. BIN
      priv/nifArray.lib
  41. BIN
      priv/nifArray.so
  42. BIN
      priv/nifHashb.so
  43. BIN
      priv/nif_skiplist.dll
  44. +24
    -21
      src/dataType/utGenTerm.erl
  45. +157
    -0
      src/nifSrc/couchdb-khash/khash.erl
  46. +49
    -0
      src/nifSrc/nifArray/nifArray.erl
  47. +77
    -0
      src/nifSrc/nifHashb/nifHashb.erl
  48. +64
    -0
      src/nifSrc/nifHashb/nifHashbbak.erl
  49. +67
    -0
      src/testCase/DsTest/utKhashDs.erl
  50. +60
    -0
      src/testCase/DsTest/utNifArrayDs.erl
  51. +55
    -0
      src/testCase/DsTest/utNifHashbDs.erl
  52. +7
    -4
      src/testCase/DsTest/utTestDs.erl
  53. +43
    -0
      src/testCase/utTestPerformance.erl
  54. +1
    -1
      src/testCase/utTestTime.erl

c_src/mqtree/mqtree.c → c_src/.mqtree/mqtree.c Прегледај датотеку


c_src/mqtree/rebar.config → c_src/.mqtree/rebar.config Прегледај датотеку


c_src/mqtree/uthash.h → c_src/.mqtree/uthash.h Прегледај датотеку


+ 843
- 0
c_src/couchdb-khash/hash.c Прегледај датотеку

@ -0,0 +1,843 @@
/*
* Hash Table Data Type
* Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
*
* 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 <stdlib.h>
#include <stddef.h>
#include <assert.h>
#include <string.h>
#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);
}

+ 240
- 0
c_src/couchdb-khash/hash.h Прегледај датотеку

@ -0,0 +1,240 @@
/*
* Hash Table Data Type
* Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
*
* 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 <limits.h>
#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

+ 658
- 0
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 <support@cloudant.com>
#include <assert.h>
#include <string.h>
#include <stdint.h>
#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);

+ 12
- 0
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"}
]}.

+ 843
- 0
c_src/couchdb-khash2/hash.c Прегледај датотеку

@ -0,0 +1,843 @@
/*
* Hash Table Data Type
* Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
*
* 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 <stdlib.h>
#include <stddef.h>
#include <assert.h>
#include <string.h>
#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);
}

+ 240
- 0
c_src/couchdb-khash2/hash.h Прегледај датотеку

@ -0,0 +1,240 @@
/*
* Hash Table Data Type
* Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
*
* 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 <limits.h>
#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

+ 658
- 0
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 <support@cloudant.com>
#include <assert.h>
#include <string.h>
#include <stdint.h>
#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);

+ 12
- 0
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"}
]}.

+ 110
- 0
c_src/nifArray/nifArray.bak Прегледај датотеку

@ -0,0 +1,110 @@
#include <stdint.h>
#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)

+ 108
- 0
c_src/nifArray/nifArray.c Прегледај датотеку

@ -0,0 +1,108 @@
#include <stdint.h>
#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)

+ 7
- 0
c_src/nifArray/rebar.config Прегледај датотеку

@ -0,0 +1,7 @@
{port_specs, [
{"../../priv/nifArray.so", ["*.c"]}
]}.

+ 455
- 0
c_src/nifHashb/nifHashb.c Прегледај датотеку

@ -0,0 +1,455 @@
#include <stdint.h>
#include <string.h>
#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)

+ 435
- 0
c_src/nifHashb/nifHashb.cbak Прегледај датотеку

@ -0,0 +1,435 @@
#include <stdint.h>
#include <string.h>
#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)

+ 7
- 0
c_src/nifHashb/rebar.config Прегледај датотеку

@ -0,0 +1,7 @@
{port_specs, [
{"../../priv/nifHashb.so", ["*.c"]}
]}.

BIN
priv/binary_tools.dll Прегледај датотеку


BIN
priv/bitmap_filter.dll Прегледај датотеку




BIN
priv/btreelru_nif.dll Прегледај датотеку




BIN
priv/epqueue_nif.dll Прегледај датотеку












BIN
priv/native_array_nif.dll Прегледај датотеку



BIN
priv/nifArray.dll Прегледај датотеку


BIN
priv/nifArray.exp Прегледај датотеку


BIN
priv/nifArray.lib Прегледај датотеку




BIN
priv/nif_skiplist.dll Прегледај датотеку


+ 24
- 21
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].
allTypes() ->
valueTypes() ++ [genTuple, genList].

+ 157
- 0
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 <support@cloudant.com>
-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}]}).

+ 49
- 0
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).

+ 77
- 0
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}).

+ 64
- 0
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}).

+ 67
- 0
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).

+ 60
- 0
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.

+ 55
- 0
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.

+ 7
- 4
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]).
%pan>-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().

+ 43
- 0
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).

+ 1
- 1
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().

Loading…
Откажи
Сачувај