Commit 198d85ad authored by Rusty Russell's avatar Rusty Russell

htable: first implementation

parent b84d624e
../../licenses/GPL-2
\ No newline at end of file
#include <string.h>
#include <stdio.h>
/**
* htable - hash table routines
*
* A hash table is an efficient structure for looking up keys. This version
* grows with usage and allows efficient deletion.
*
* Example:
* #include <ccan/htable/htable.h>
* #include <ccan/hash/hash.h>
* #include <stdio.h>
* #include <err.h>
* #include <string.h>
*
* struct name_to_digit {
* const char *name;
* unsigned int val;
* };
*
* static struct name_to_digit map[] = {
* { "zero", 0},
* { "one", 1 },
* { "two", 2 },
* { "three", 3 },
* { "four", 4 },
* { "five", 5 },
* { "six", 6 },
* { "seven", 7 },
* { "eight", 8 },
* { "nine", 9 }
* };
*
* // Wrapper for rehash function pointer.
* static size_t rehash(const void *e, void *unused)
* {
* return hash_string(((struct name_to_digit *)e)->name);
* }
*
* // Comparison function.
* static bool streq(const void *e, void *string)
* {
* return strcmp(((struct name_to_digit *)e)->name, string) == 0;
* }
*
* // We let them add their own aliases, eg. --alias=v=5
* static void add_alias(struct htable *ht, const char *alias)
* {
* char *eq;
* struct name_to_digit *n;
*
* n = malloc(sizeof(*n));
* n->name = strdup(alias);
*
* eq = strchr(n->name, '=');
* if (!eq || ((n->val = atoi(eq+1)) == 0 && !strcmp(eq+1, "0")))
* errx(1, "Usage: --alias=<name>=<value>");
* *eq = '\0';
* htable_add(ht, hash_string(n->name), n);
* }
*
* int main(int argc, char *argv[])
* {
* struct htable *ht;
* unsigned int i;
* unsigned long val;
*
* if (argc < 2)
* errx(1, "Usage: %s [--alias=<name>=<val>]... <str>...",
* argv[0]);
*
* // Create and populate hash table.
* ht = htable_new(rehash, NULL);
* for (i = 0; i < sizeof(map)/sizeof(map[0]); i++)
* htable_add(ht, hash_string(map[i].name), &map[i]);
*
* // Add any aliases to the hash table.
* for (i = 1; i < argc; i++) {
* if (!strncmp(argv[i], "--alias=", strlen("--alias=")))
* add_alias(ht, argv[i] + strlen("--alias="));
* else
* break;
* }
*
* // Find the other args in the hash table.
* for (val = 0; i < argc; i++) {
* struct name_to_digit *n;
* n = htable_get(ht, hash_string(argv[i]),
* streq, argv[i]);
* if (!n)
* errx(1, "Invalid digit name %s", argv[i]);
* // Append it to the value we are building up.
* val *= 10;
* val += n->val;
* }
* printf("%lu\n", val);
* return 0;
* }
*
* License: GPLv2 (or later)
* Author: Rusty Russell <rusty@rustcorp.com.au>
*/
int main(int argc, char *argv[])
{
if (argc != 2)
return 1;
if (strcmp(argv[1], "depends") == 0) {
printf("ccan/compiler\n");
return 0;
}
return 1;
}
#include <ccan/htable/htable.h>
#include <ccan/compiler/compiler.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
/* This means a struct htable takes at least 512 bytes / 1k (32/64 bits). */
#define HTABLE_BASE_BITS 7
/* We use 0x1 as deleted marker. */
#define HTABLE_DELETED (0x1)
struct htable {
size_t (*rehash)(const void *elem, void *priv);
void *priv;
unsigned int bits;
size_t elems, max;
/* These are the bits which are the same in all pointers. */
uintptr_t common_mask, common_bits;
uintptr_t *table;
};
/* We clear out the bits which are always the same, and put metadata there. */
static inline uintptr_t get_extra_ptr_bits(const struct htable *ht,
uintptr_t e)
{
return e & ht->common_mask;
}
static inline void *get_raw_ptr(const struct htable *ht, uintptr_t e)
{
return (void *)((e & ~ht->common_mask) | ht->common_bits);
}
static inline uintptr_t make_hval(const struct htable *ht,
const void *p, uintptr_t bits)
{
return ((uintptr_t)p & ~ht->common_mask) | bits;
}
static inline bool entry_is_valid(uintptr_t e)
{
return e > HTABLE_DELETED;
}
static inline uintptr_t get_hash_ptr_bits(const struct htable *ht,
size_t hash)
{
/* Shuffling the extra bits (as specified in mask) down the
* end is quite expensive. But the lower bits are redundant, so
* we fold the value first. */
return (hash ^ (hash >> ht->bits)) & ht->common_mask;
}
struct htable *htable_new(size_t (*rehash)(const void *elem, void *priv),
void *priv)
{
struct htable *ht = malloc(sizeof(struct htable));
if (ht) {
ht->bits = HTABLE_BASE_BITS;
ht->rehash = rehash;
ht->priv = priv;
ht->elems = 0;
ht->max = (1 << ht->bits) * 3 / 4;
/* This guarantees we enter update_common first add. */
ht->common_mask = -1;
ht->common_bits = 0;
ht->table = calloc(1 << ht->bits, sizeof(uintptr_t));
if (!ht->table) {
free(ht);
ht = NULL;
}
}
return ht;
}
void htable_free(const struct htable *ht)
{
free((void *)ht->table);
free((void *)ht);
}
static size_t hash_bucket(const struct htable *ht, size_t h)
{
return h & ((1 << ht->bits)-1);
}
static void *htable_val(const struct htable *ht,
struct htable_iter *i, size_t hash)
{
uintptr_t h2 = get_hash_ptr_bits(ht, hash);
while (ht->table[i->off]) {
if (ht->table[i->off] != HTABLE_DELETED) {
if (get_extra_ptr_bits(ht, ht->table[i->off]) == h2)
return get_raw_ptr(ht, ht->table[i->off]);
}
i->off = (i->off + 1) & ((1 << ht->bits)-1);
}
return NULL;
}
void *htable_firstval(const struct htable *ht,
struct htable_iter *i, size_t hash)
{
i->off = hash_bucket(ht, hash);
return htable_val(ht, i, hash);
}
void *htable_nextval(const struct htable *ht,
struct htable_iter *i, size_t hash)
{
i->off = (i->off + 1) & ((1 << ht->bits)-1);
return htable_val(ht, i, hash);
}
void *htable_first(const struct htable *ht, struct htable_iter *i)
{
for (i->off = 0; i->off < (size_t)1 << ht->bits; i->off++) {
if (entry_is_valid(ht->table[i->off]))
return get_raw_ptr(ht, ht->table[i->off]);
}
return NULL;
}
void *htable_next(const struct htable *ht, struct htable_iter *i)
{
for (i->off++; i->off < (size_t)1 << ht->bits; i->off++) {
if (entry_is_valid(ht->table[i->off]))
return get_raw_ptr(ht, ht->table[i->off]);
}
return NULL;
}
/* This does not expand the hash table, that's up to caller. */
static void ht_add(struct htable *ht, const void *new, size_t h)
{
size_t i;
i = hash_bucket(ht, h);
while (entry_is_valid(ht->table[i]))
i = (i + 1) & ((1 << ht->bits)-1);
ht->table[i] = make_hval(ht, new, get_hash_ptr_bits(ht, h));
}
static COLD_ATTRIBUTE bool double_table(struct htable *ht)
{
unsigned int i;
size_t oldnum = (size_t)1 << ht->bits;
size_t *oldtable, e;
oldtable = ht->table;
ht->table = calloc(1 << (ht->bits+1), sizeof(size_t));
if (!ht->table) {
ht->table = oldtable;
return false;
}
ht->bits++;
ht->max *= 2;
for (i = 0; i < oldnum; i++) {
if (entry_is_valid(e = oldtable[i])) {
void *p = get_raw_ptr(ht, e);
ht_add(ht, p, ht->rehash(p, ht->priv));
}
}
free(oldtable);
return true;
}
/* We stole some bits, now we need to put them back... */
static COLD_ATTRIBUTE void update_common(struct htable *ht, const void *p)
{
unsigned int i;
uintptr_t maskdiff, bitsdiff;
if (ht->elems == 0) {
ht->common_mask = -1;
ht->common_bits = (uintptr_t)p;
return;
}
/* Find bits which are unequal to old common set. */
maskdiff = ht->common_bits ^ ((uintptr_t)p & ht->common_mask);
/* These are the bits which go there in existing entries. */
bitsdiff = ht->common_bits & maskdiff;
for (i = 0; i < (size_t)1 << ht->bits; i++) {
if (!entry_is_valid(ht->table[i]))
continue;
/* Clear the bits no longer in the mask, set them as
* expected. */
ht->table[i] &= ~maskdiff;
ht->table[i] |= bitsdiff;
}
/* Take away those bits from our mask and set. */
ht->common_mask &= ~maskdiff;
ht->common_bits &= ~maskdiff;
}
bool htable_add(struct htable *ht, size_t hash, const void *p)
{
if (ht->elems+1 > ht->max && !double_table(ht))
return false;
assert(p);
if (((uintptr_t)p & ht->common_mask) != ht->common_bits)
update_common(ht, p);
ht_add(ht, p, hash);
ht->elems++;
return true;
}
/* If every one of the following buckets are DELETED (up to the next unused
one), we can actually mark them all unused. */
static void delete_run(struct htable *ht, unsigned int num)
{
unsigned int i, last = num + 1;
size_t mask = (((size_t)1 << ht->bits)-1);
while (ht->table[last & mask]) {
if (entry_is_valid(ht->table[last & mask]))
return;
last++;
}
/* Now see if we can step backwards to find previous deleted ones. */
for (i = num-1; ht->table[i & mask] == HTABLE_DELETED; i--);
for (i++; i < last; i++)
ht->table[i & ((1 << ht->bits)-1)] = 0;
}
bool htable_del(struct htable *ht, size_t h, const void *p)
{
struct htable_iter i;
void *c;
for (c = htable_firstval(ht,&i,h); c; c = htable_nextval(ht,&i,h)) {
if (c == p) {
htable_delval(ht, &i);
return true;
}
}
return false;
}
void htable_delval(struct htable *ht, struct htable_iter *i)
{
assert(i->off < (size_t)1 << ht->bits);
assert(entry_is_valid(ht->table[i->off]));
ht->elems--;
ht->table[i->off] = HTABLE_DELETED;
delete_run(ht, i->off);
}
#ifndef CCAN_HTABLE_H
#define CCAN_HTABLE_H
#include "config.h"
#include <stdbool.h>
#include <stdlib.h>
struct htable;
/**
* htable_new - allocate a hash tree.
* @rehash: hash function to use for rehashing.
* @priv: private argument to @rehash function.
*/
struct htable *htable_new(size_t (*hash)(const void *elem, void *priv),
void *priv);
/**
* htable_free - dellocate a hash tree.
*
* This doesn't do anything to any pointers left in it.
*/
void htable_free(const struct htable *);
/**
* htable_rehash - use a hashtree's rehash function
* @elem: the argument to rehash()
*
*/
size_t htable_rehash(const void *elem);
/**
* htable_add - add a pointer into a hash tree.
* @ht: the htable
* @hash: the hash value of the object
* @p: the non-NULL pointer
*
* Also note that this can only fail due to allocation failure. Otherwise, it
* returns true.
*/
bool htable_add(struct htable *ht, size_t hash, const void *p);
/**
* htable_del - remove a pointer from a hash tree
* @ht: the htable
* @hash: the hash value of the object
* @p: the pointer
*
* Returns true if the pointer was found (and deleted).
*/
bool htable_del(struct htable *ht, size_t hash, const void *p);
/**
* struct htable_iter - iterator or htable_first or htable_firstval etc.
*
* This refers to a location inside the hashtable.
*/
struct htable_iter {
size_t off;
};
/**
* htable_firstval - find a candidate for a given hash value
* @htable: the hashtable
* @i: the struct htable_iter to initialize
* @hash: the hash value
*
* You'll need to check the value is what you want; returns NULL if none.
* See Also:
* htable_delval()
*/
void *htable_firstval(const struct htable *htable,
struct htable_iter *i, size_t hash);
/**
* htable_nextval - find another candidate for a given hash value
* @htable: the hashtable
* @i: the struct htable_iter to initialize
* @hash: the hash value
*
* You'll need to check the value is what you want; returns NULL if no more.
*/
void *htable_nextval(const struct htable *htable,
struct htable_iter *i, size_t hash);
/**
* htable_get - find an entry in the hash table
* @ht: the hashtable
* @h: the hash value of the entry
* @cmp: the comparison function
* @ptr: the pointer to hand to the comparison function.
*
* Convenient inline wrapper for htable_firstval/htable_nextval loop.
*/
static inline void *htable_get(const struct htable *ht,
size_t h,
bool (*cmp)(const void *candidate, void *ptr),
const void *ptr)
{
struct htable_iter i;
void *c;
for (c = htable_firstval(ht,&i,h); c; c = htable_nextval(ht,&i,h)) {
if (cmp(c, (void *)ptr))
return c;
}
return NULL;
}
/**
* htable_first - find an entry in the hash table
* @ht: the hashtable
* @i: the struct htable_iter to initialize
*
* Get an entry in the hashtable; NULL if empty.
*/
void *htable_first(const struct htable *htable, struct htable_iter *i);
/**
* htable_next - find another entry in the hash table
* @ht: the hashtable
* @i: the struct htable_iter to use
*
* Get another entry in the hashtable; NULL if all done.
* This is usually used after htable_first or prior non-NULL htable_next.
*/
void *htable_next(const struct htable *htable, struct htable_iter *i);
/**
* htable_delval - remove an iterated pointer from a hash tree
* @ht: the htable
* @i: the htable_iter
*
* Usually used to delete a hash entry after it has been found with
* htable_firstval etc.
*/
void htable_delval(struct htable *ht, struct htable_iter *i);
#endif /* CCAN_HTABLE_H */
#ifndef CCAN_HTABLE_TYPE_H
#define CCAN_HTABLE_TYPE_H
#include <ccan/htable/htable.h>
#include "config.h"
/**
* HTABLE_DEFINE_TYPE - create a set of htable ops for a type
* @type: a type whose pointers will be values in the hash.
* @keyof: a function/macro to extract a key from a @type element.
* @hashfn: a hash function for a @key
* @cmpfn: a comparison function for two keyof()s.
* @name: a name for all the functions to define (of form htable_<name>_*)
*
* NULL values may not be placed into the hash table.
*
* The following wrapper functions are defined; each one is a
* simplified version of the htable.h equivalent:
*
* // Creating and freeing.
* struct htable_@name *htable_@name_new(void);
* void htable_@name_free(const struct htable_@name *ht);
*
* // Add, delete and find.
* bool htable_@name_add(struct htable_@name *ht, const type *e);
* bool htable_@name_del(struct htable_@name *ht, const type *e);
* bool htable_@name_delkey(struct htable_@name *ht, const ktype *k);
* type *htable_@name_get(const struct htable_@name *ht, const ktype *k);
*
* // Iteration.
* struct htable_@name_iter;
* type *htable_@name_first(const struct htable_@name *ht,
* struct htable_@name_iter *i);
* type *htable_@name_next(const struct htable_@name *ht,
* struct htable_@name_iter *i);
*/
#define HTABLE_DEFINE_TYPE(type, keyof, hashfn, cmpfn, name) \
struct htable_##name; \
struct htable_##name##_iter { struct htable_iter i; }; \
static inline size_t htable_##name##_hash(const void *elem, void *priv) \
{ \
return hashfn(keyof((const type *)elem)); \
} \
static inline struct htable_##name *htable_##name##_new(void) \
{ \
return (struct htable_##name *)htable_new(htable_##name##_hash, \
NULL); \
} \
static inline void htable_##name##_free(const struct htable_##name *ht) \
{ \
htable_free((const struct htable *)ht); \
} \
static inline bool htable_##name##_add(struct htable_##name *ht, \
const type *elem) \
{ \
return htable_add((struct htable *)ht, hashfn(keyof(elem)), elem); \
} \
static inline bool htable_##name##_del(const struct htable_##name *ht, \
const type *elem) \
{ \
return htable_del((struct htable *)ht, hashfn(keyof(elem)), elem); \
} \
static inline type *htable_##name##_get(const struct htable_##name *ht, \
const HTABLE_KTYPE(keyof) k) \
{ \
/* Typecheck for cmpfn */ \
(void)sizeof(cmpfn(keyof((const type *)NULL), \
keyof((const type *)NULL))); \
return (type *)htable_get((const struct htable *)ht, \
hashfn(k), \
(bool (*)(const void *, void *))(cmpfn), \
k); \
} \
static inline bool htable_##name##_delkey(struct htable_##name *ht, \
const HTABLE_KTYPE(keyof) k) \
{ \
type *elem = htable_##name##_get(ht, k); \
if (elem) \
return htable_##name##_del(ht, elem); \
return false; \
} \
static inline type *htable_##name##_first(const struct htable_##name *ht, \
struct htable_##name##_iter *iter) \
{ \
return htable_first((const struct htable *)ht, &iter->i); \
} \
static inline type *htable_##name##_next(const struct htable_##name *ht, \
struct htable_##name##_iter *iter) \
{ \
return htable_next((const struct htable *)ht, &iter->i); \
}
#if HAVE_TYPEOF
#define HTABLE_KTYPE(keyof) typeof(keyof(NULL))
#else
#define HTABLE_KTYPE(keyof) void *
#endif
#endif /* CCAN_HTABLE_TYPE_H */
#include <ccan/htable/htable_type.h>
#include <ccan/htable/htable.c>
#include <ccan/tap/tap.h>
#include <stdbool.h>
#include <string.h>
#define NUM_VALS (1 << HTABLE_BASE_BITS)
struct obj {
unsigned int key;
};
static const unsigned int *objkey(const struct obj *obj)
{
return &obj->key;
}
/* We use the number divided by two as the hash (for lots of
collisions), plus set all the higher bits so we can detect if they
don't get masked out. */
static size_t objhash(const unsigned int *key)
{
size_t h = *key / 2;
h |= -1UL << HTABLE_BASE_BITS;
return h;
}
static bool cmp(const unsigned int *key1, const unsigned int *key2)
{
return *key1 == *key2;
}
HTABLE_DEFINE_TYPE(struct obj, objkey, objhash, cmp, obj);
static void add_vals(struct htable_obj *ht,
struct obj val[], unsigned int num)
{
unsigned int i;
for (i = 0; i < num; i++) {
if (htable_obj_get(ht, &i)) {
fail("%u already in hash", i);
return;
}
htable_obj_add(ht, &val[i]);
if (htable_obj_get(ht, &i) != &val[i]) {
fail("%u not added to hash", i);
return;
}
}
pass("Added %u numbers to hash", i);
}
static void find_vals(const struct htable_obj *ht,
const struct obj val[], unsigned int num)
{
unsigned int i;
for (i = 0; i < num; i++) {
if (htable_obj_get(ht, &i) != &val[i]) {
fail("%u not found in hash", i);
return;
}
}
pass("Found %u numbers in hash", i);
}
static void del_vals(struct htable_obj *ht,
const struct obj val[], unsigned int num)
{
unsigned int i;
for (i = 0; i < num; i++) {
if (!htable_obj_delkey(ht, &val[i].key)) {
fail("%u not deleted from hash", i);
return;
}
}
pass("Deleted %u numbers in hash", i);
}
static void del_vals_bykey(struct htable_obj *ht,
const struct obj val[], unsigned int num)
{
unsigned int i;
for (i = 0; i < num; i++) {
if (!htable_obj_delkey(ht, &i)) {
fail("%u not deleted by key from hash", i);
return;
}
}
pass("Deleted %u numbers by key from hash", i);
}
static bool check_mask(struct htable *ht, const struct obj val[], unsigned num)
{
uint64_t i;
for (i = 0; i < num; i++) {
if (((uintptr_t)&val[i] & ht->common_mask) != ht->common_bits)
return false;
}
return true;
}
int main(int argc, char *argv[])
{
unsigned int i;
struct htable_obj *ht;
struct obj val[NUM_VALS];
unsigned int dne;
void *p;
struct htable_obj_iter iter;
plan_tests(20);
for (i = 0; i < NUM_VALS; i++)
val[i].key = i;
dne = i;
ht = htable_obj_new();
ok1(((struct htable *)ht)->max < (1 << ((struct htable *)ht)->bits));
ok1(((struct htable *)ht)->bits == HTABLE_BASE_BITS);
/* We cannot find an entry which doesn't exist. */
ok1(!htable_obj_get(ht, &dne));
/* Fill it, it should increase in size (once). */
add_vals(ht, val, NUM_VALS);
ok1(((struct htable *)ht)->bits == HTABLE_BASE_BITS + 1);
ok1(((struct htable *)ht)->max < (1 << ((struct htable *)ht)->bits));
/* Mask should be set. */
ok1(((struct htable *)ht)->common_mask != 0);
ok1(((struct htable *)ht)->common_mask != -1);
ok1(check_mask((struct htable *)ht, val, NUM_VALS));
/* Find all. */
find_vals(ht, val, NUM_VALS);
ok1(!htable_obj_get(ht, &dne));
/* Walk once, should get them all. */
i = 0;
for (p = htable_obj_first(ht,&iter); p; p = htable_obj_next(ht, &iter))
i++;
ok1(i == NUM_VALS);
/* Delete all. */
del_vals(ht, val, NUM_VALS);
ok1(!htable_obj_get(ht, &val[0].key));
/* Worst case, a "pointer" which doesn't have any matching bits. */
htable_add((struct htable *)ht, 0,
(void *)~(uintptr_t)&val[NUM_VALS-1]);
htable_obj_add(ht, &val[NUM_VALS-1]);
ok1(((struct htable *)ht)->common_mask == 0);
ok1(((struct htable *)ht)->common_bits == 0);
/* Delete the bogus one before we trip over it. */
htable_del((struct htable *)ht, 0,
(void *)~(uintptr_t)&val[NUM_VALS-1]);
/* Add the rest. */
add_vals(ht, val, NUM_VALS-1);
/* Check we can find them all. */
find_vals(ht, val, NUM_VALS);
ok1(!htable_obj_get(ht, &dne));
/* Delete them all by key. */
del_vals_bykey(ht, val, NUM_VALS);
htable_obj_free(ht);
return exit_status();
}
#include <ccan/htable/htable.h>
#include <ccan/htable/htable.c>
#include <ccan/tap/tap.h>
#include <stdbool.h>
#include <string.h>
#define NUM_VALS (1 << HTABLE_BASE_BITS)
/* We use the number divided by two as the hash (for lots of
collisions), plus set all the higher bits so we can detect if they
don't get masked out. */
static size_t hash(const void *elem, void *unused)
{
size_t h = *(uint64_t *)elem / 2;
h |= -1UL << HTABLE_BASE_BITS;
return h;
}
static bool objcmp(const void *htelem, void *cmpdata)
{
return *(uint64_t *)htelem == *(uint64_t *)cmpdata;
}
static void add_vals(struct htable *ht,
const uint64_t val[], unsigned int num)
{
uint64_t i;
for (i = 0; i < num; i++) {
if (htable_get(ht, hash(&i, NULL), objcmp, &i)) {
fail("%llu already in hash", (long long)i);
return;
}
htable_add(ht, hash(&val[i], NULL), &val[i]);
if (htable_get(ht, hash(&i, NULL), objcmp, &i) != &val[i]) {
fail("%llu not added to hash", (long long)i);
return;
}
}
pass("Added %llu numbers to hash", (long long)i);
}
#if 0
static void refill_vals(struct htable *ht,
const uint64_t val[], unsigned int num)
{
uint64_t i;
for (i = 0; i < num; i++) {
if (htable_get(ht, hash(&i, NULL), objcmp, &i))
continue;
htable_add(ht, hash(&val[i], NULL), &val[i]);
}
}
#endif
static void find_vals(struct htable *ht,
const uint64_t val[], unsigned int num)
{
uint64_t i;
for (i = 0; i < num; i++) {
if (htable_get(ht, hash(&i, NULL), objcmp, &i) != &val[i]) {
fail("%llu not found in hash", (long long)i);
return;
}
}
pass("Found %llu numbers in hash", (long long)i);
}
static void del_vals(struct htable *ht,
const uint64_t val[], unsigned int num)
{
uint64_t i;
for (i = 0; i < num; i++) {
if (!htable_del(ht, hash(&val[i], NULL), &val[i])) {
fail("%llu not deleted from hash", (long long)i);
return;
}
}
pass("Deleted %llu numbers in hash", (long long)i);
}
static bool check_mask(struct htable *ht, uint64_t val[], unsigned num)
{
uint64_t i;
for (i = 0; i < num; i++) {
if (((uintptr_t)&val[i] & ht->common_mask) != ht->common_bits)
return false;
}
return true;
}
int main(int argc, char *argv[])
{
unsigned int i;
struct htable *ht;
uint64_t val[NUM_VALS];
uint64_t dne;
void *p;
struct htable_iter iter;
plan_tests(19);
for (i = 0; i < NUM_VALS; i++)
val[i] = i;
dne = i;
ht = htable_new(hash, NULL);
ok1(ht->max < (1 << ht->bits));
ok1(ht->bits == HTABLE_BASE_BITS);
/* We cannot find an entry which doesn't exist. */
ok1(!htable_get(ht, hash(&dne, NULL), objcmp, &dne));
/* Fill it, it should increase in size (once). */
add_vals(ht, val, NUM_VALS);
ok1(ht->bits == HTABLE_BASE_BITS + 1);
ok1(ht->max < (1 << ht->bits));
/* Mask should be set. */
ok1(ht->common_mask != 0);
ok1(ht->common_mask != -1);
ok1(check_mask(ht, val, NUM_VALS));
/* Find all. */
find_vals(ht, val, NUM_VALS);
ok1(!htable_get(ht, hash(&dne, NULL), objcmp, &dne));
/* Walk once, should get them all. */
i = 0;
for (p = htable_first(ht,&iter); p; p = htable_next(ht, &iter))
i++;
ok1(i == NUM_VALS);
/* Delete all. */
del_vals(ht, val, NUM_VALS);
ok1(!htable_get(ht, hash(&val[0], NULL), objcmp, &val[0]));
/* Worst case, a "pointer" which doesn't have any matching bits. */
htable_add(ht, 0, (void *)~(uintptr_t)&val[NUM_VALS-1]);
htable_add(ht, hash(&val[NUM_VALS-1], NULL), &val[NUM_VALS-1]);
ok1(ht->common_mask == 0);
ok1(ht->common_bits == 0);
/* Add the rest. */
add_vals(ht, val, NUM_VALS-1);
/* Check we can find them all. */
find_vals(ht, val, NUM_VALS);
ok1(!htable_get(ht, hash(&dne, NULL), objcmp, &dne));
return exit_status();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment