|
@@ -22,14 +22,54 @@
|
|
|
#define APPROX_40_PERCENT(x) (((x) * 409) >> 10)
|
|
|
|
|
|
#define MIN_HT_SIZE 1 << 3
|
|
|
+#define MAX_KEY_SIZE 64
|
|
|
|
|
|
+/** @brief Bucket types.
|
|
|
+ * Table columns are: bucket type tag; key type; value type.
|
|
|
+ * NOTE This macro can be redefined BEFORE including this library in order to
|
|
|
+ * redefine the bucket types for general purpose use.
|
|
|
+ */
|
|
|
+#ifndef LSUP_HTABLE_BUCKET_TYPES
|
|
|
+#define LSUP_HTABLE_BUCKET_TYPES \
|
|
|
+ ENTRY( ks, LSUP_Key, void* ) \
|
|
|
+ ENTRY( kt, LSUP_Key, LSUP_TripleKey ) \
|
|
|
+ ENTRY( tk, LSUP_TripleKey, LSUP_Key ) \
|
|
|
+
|
|
|
+#endif
|
|
|
|
|
|
-typedef struct {
|
|
|
- LSUP_TripleKey key; // TODO Make configurable but
|
|
|
- // statically allocated via macros
|
|
|
- void * val;
|
|
|
- uint64_t hash;
|
|
|
- uint16_t psl;
|
|
|
+//#ifdef LSUP_BIG_HT // TODO
|
|
|
+typedef uint64_t ht_hash_t;
|
|
|
+//else
|
|
|
+//typedef uint32_t ht_hash_t;
|
|
|
+//#endif
|
|
|
+
|
|
|
+/** @brief Bucket structure.
|
|
|
+ *
|
|
|
+ * Note that the address of "key" is reliably findable across multiple bucket
|
|
|
+ * types in a union, since hash and psl don't change. The address of the value,
|
|
|
+ * however, cannot be determined without knowing the bucket type or key size.
|
|
|
+ */
|
|
|
+#define ENTRY(tag, k, v) \
|
|
|
+typedef struct { \
|
|
|
+ ht_hash_t hash; \
|
|
|
+ uint16_t psl; \
|
|
|
+ k key; \
|
|
|
+ v val; \
|
|
|
+} bucket_##tag##_t;
|
|
|
+LSUP_HTABLE_BUCKET_TYPES
|
|
|
+#undef ENTRY
|
|
|
+
|
|
|
+typedef enum {
|
|
|
+#define ENTRY(tag, k, v) BT_##tag,
|
|
|
+LSUP_HTABLE_BUCKET_TYPES
|
|
|
+#undef ENTRY
|
|
|
+} BucketTypeTag;
|
|
|
+
|
|
|
+typedef union {
|
|
|
+#define ENTRY(tag, k, v) \
|
|
|
+ bucket_##tag##_t * tag;
|
|
|
+LSUP_HTABLE_BUCKET_TYPES
|
|
|
+#undef ENTRY
|
|
|
} bucket_t;
|
|
|
|
|
|
typedef struct htable_t {
|
|
@@ -37,7 +77,9 @@ typedef struct htable_t {
|
|
|
htsize_t nitems;
|
|
|
unsigned flags;
|
|
|
uint64_t divinfo;
|
|
|
+
|
|
|
bucket_t * buckets;
|
|
|
+ BucketTypeTag bucket_type;
|
|
|
uint64_t seed;
|
|
|
|
|
|
key_hash_fn_t key_hash_fn;
|
|
@@ -45,16 +87,32 @@ typedef struct htable_t {
|
|
|
|
|
|
ksize_t ksize;
|
|
|
vsize_t vsize;
|
|
|
-
|
|
|
- void * del_marker; // Used to fill deleted buckets.
|
|
|
} HTable;
|
|
|
|
|
|
+// Fill and compare empty buckets.
|
|
|
+static const unsigned char del_marker[MAX_KEY_SIZE] = {0};
|
|
|
+
|
|
|
+/*
|
|
|
+ * Byte offset for key address in a bucket.
|
|
|
+ *
|
|
|
+ * This is applicable to any type of bucket if handled as a null pointer.
|
|
|
+ */
|
|
|
+static const unsigned int k_offset = sizeof(ht_hash_t) + sizeof(uint16_t);
|
|
|
|
|
|
+/** @brief Specific bucket access in union.
|
|
|
+ *
|
|
|
+ * Use: ht->HT_BUCKET_NAME(ht)
|
|
|
+ */
|
|
|
+#define ENTRY(tag, k, v) if (ht->bucket_type == BT_##tag) return b->tag;
|
|
|
+static inline void *bkey(LSUP_HTable *ht, bucket_t *b) { LSUP_HTABLE_BUCKET_TYPES; }
|
|
|
+#undef ENTRY
|
|
|
+
|
|
|
+#define HT_BUCKET(tag, b, ht) b.ht->##tag
|
|
|
|
|
|
/* * * GENERIC UTILITIES * * */
|
|
|
|
|
|
-static inline bool is_empty_bucket(const HTable *ht, const bucket_t *bucket)
|
|
|
-{ return memcmp(bucket->key, ht->del_marker, ht->ksize) == 0; }
|
|
|
+static inline bool is_empty_bucket(const HTable *ht, const void *bucket)
|
|
|
+{ return memcmp(bucket + k_offset, del_marker, ht->ksize) == 0; }
|
|
|
|
|
|
/*
|
|
|
* Find first bit.
|
|
@@ -110,15 +168,16 @@ fast_rem32(uint32_t v, uint32_t div, uint64_t divinfo)
|
|
|
{ return v - div * fast_div32(v, div, divinfo); }
|
|
|
|
|
|
|
|
|
+/*
|
|
|
static int __attribute__((__unused__))
|
|
|
//static int
|
|
|
-validate_psl_p(const HTable *ht, const bucket_t *bucket, unsigned i)
|
|
|
+validate_psl_p(const HTable *ht, unsigned i)
|
|
|
{
|
|
|
unsigned base_i = fast_rem32(bucket->hash, ht->size, ht->divinfo);
|
|
|
unsigned diff = (base_i > i) ? ht->size - base_i + i : i - base_i;
|
|
|
- return is_empty_bucket(ht, bucket) || diff == bucket->psl;
|
|
|
+ return is_empty_bucket(ht, ht->buckets + i) || diff == bucket->psl;
|
|
|
}
|
|
|
-
|
|
|
+*/
|
|
|
|
|
|
/* * * PUBLIC API * * */
|
|
|
|
|
@@ -136,8 +195,6 @@ HTable *LSUP_htable_new(
|
|
|
ht->flags = flags;
|
|
|
ht->size = 0;
|
|
|
|
|
|
- CRITICAL(ht->del_marker = calloc(1, ksize));
|
|
|
-
|
|
|
LSUP_htable_resize(ht, size);
|
|
|
|
|
|
return ht;
|
|
@@ -222,7 +279,7 @@ int LSUP_htable_insert(HTable *ht, const void *key, void *val)
|
|
|
|
|
|
if(is_empty_bucket(ht, ht->buckets + i)) break;
|
|
|
|
|
|
- ASSERT(validate_psl_p(ht, bucket, i));
|
|
|
+ //ASSERT(validate_psl_p(ht, i));
|
|
|
|
|
|
// There is a key in the bucket.
|
|
|
TRACE("Entry key: {%lu, %lu, %lu}; bucket key: {%lu, %lu, %lu}", entry.key[0], entry.key[1], entry.key[2], bucket->key[0], bucket->key[1], bucket->key[2]);
|
|
@@ -252,7 +309,7 @@ int LSUP_htable_insert(HTable *ht, const void *key, void *val)
|
|
|
entry.psl++;
|
|
|
|
|
|
/* Continue to the next bucket. */
|
|
|
- ASSERT(validate_psl_p(ht, bucket, i));
|
|
|
+ //ASSERT(validate_psl_p(ht, bucket, i));
|
|
|
i = fast_rem32(i + 1, ht->size, ht->divinfo);
|
|
|
}
|
|
|
|
|
@@ -264,7 +321,7 @@ int LSUP_htable_insert(HTable *ht, const void *key, void *val)
|
|
|
memcpy(bucket, &entry, sizeof(bucket_t)); // copy
|
|
|
ht->nitems++;
|
|
|
|
|
|
- ASSERT(validate_psl_p(ht, bucket, i));
|
|
|
+ //ASSERT(validate_psl_p(ht, bucket, i));
|
|
|
|
|
|
return LSUP_OK;
|
|
|
}
|
|
@@ -309,7 +366,7 @@ int LSUP_htable_get(const HTable *ht, const void *key, void **valp)
|
|
|
*/
|
|
|
for(;;) {
|
|
|
bucket_t *bucket = ht->buckets + i;
|
|
|
- ASSERT(validate_psl_p(ht, bucket, i));
|
|
|
+ //ASSERT(validate_psl_p(ht, bucket, i));
|
|
|
|
|
|
if (ht->key_eq_fn(bucket->key, key, ht->ksize)) {
|
|
|
// Key found within max probe length.
|
|
@@ -357,7 +414,7 @@ int LSUP_htable_del(HTable *ht, const void *key)
|
|
|
if (is_empty_bucket(ht, bucket) || n > bucket->psl)
|
|
|
return LSUP_NOACTION;
|
|
|
|
|
|
- ASSERT(validate_psl_p(ht, bucket, i));
|
|
|
+ //ASSERT(validate_psl_p(ht, bucket, i));
|
|
|
|
|
|
if (!ht->key_eq_fn(bucket->key, key, ht->ksize)) {
|
|
|
/* Continue to the next bucket. */
|
|
@@ -376,11 +433,11 @@ int LSUP_htable_del(HTable *ht, const void *key)
|
|
|
while(1) {
|
|
|
bucket_t *nbucket;
|
|
|
|
|
|
- memcpy(bucket->key, ht->del_marker, ht->ksize);
|
|
|
+ memcpy(bucket->key, del_marker, ht->ksize);
|
|
|
|
|
|
i = fast_rem32(i + 1, ht->size, ht->divinfo);
|
|
|
nbucket = ht->buckets + i;
|
|
|
- ASSERT(validate_psl_p(ht, nbucket, i));
|
|
|
+ //ASSERT(validate_psl_p(ht, nbucket, i));
|
|
|
|
|
|
/*
|
|
|
* Stop if we reach an empty bucket or hit a key which
|
|
@@ -433,7 +490,6 @@ extern int LSUP_htable_iter(
|
|
|
void LSUP_htable_done(HTable *ht)
|
|
|
{
|
|
|
if(LIKELY(ht->buckets != NULL)) free(ht->buckets);
|
|
|
- free(ht->del_marker);
|
|
|
}
|
|
|
|
|
|
|