]> git.ozlabs.org Git - ccan/blobdiff - ccan/htable/htable.c
htable: allow htable_type keys to be non-pointers.
[ccan] / ccan / htable / htable.c
index 0a01ead8978ad4c4e914a37dbe029c2ef1219004..e1d13699ea85811fe2f61adbb1ce4eca942406c8 100644 (file)
@@ -5,6 +5,7 @@
 #include <limits.h>
 #include <stdbool.h>
 #include <assert.h>
+#include <string.h>
 
 /* We use 0x1 as deleted marker. */
 #define HTABLE_DELETED (0x1)
@@ -52,6 +53,34 @@ void htable_init(struct htable *ht,
        ht->table = &ht->perfect_bit;
 }
 
+/* We've changed ht->bits, update ht->max and ht->max_with_deleted */
+static void htable_adjust_capacity(struct htable *ht)
+{
+       ht->max = ((size_t)3 << ht->bits) / 4;
+       ht->max_with_deleted = ((size_t)9 << ht->bits) / 10;
+}
+
+bool htable_init_sized(struct htable *ht,
+                      size_t (*rehash)(const void *, void *),
+                      void *priv, size_t expect)
+{
+       htable_init(ht, rehash, priv);
+
+       /* Don't go insane with sizing. */
+       for (ht->bits = 1; ((size_t)3 << ht->bits) / 4 < expect; ht->bits++) {
+               if (ht->bits == 30)
+                       break;
+       }
+
+       ht->table = calloc(1 << ht->bits, sizeof(size_t));
+       if (!ht->table) {
+               ht->table = &ht->perfect_bit;
+               return false;
+       }
+       htable_adjust_capacity(ht);
+       return true;
+}
+       
 void htable_clear(struct htable *ht)
 {
        if (ht->table != &ht->perfect_bit)
@@ -59,6 +88,19 @@ void htable_clear(struct htable *ht)
        htable_init(ht, ht->rehash, ht->priv);
 }
 
+bool htable_copy(struct htable *dst, const struct htable *src)
+{
+       uintptr_t *htable = malloc(sizeof(size_t) << src->bits);
+
+       if (!htable)
+               return false;
+
+       *dst = *src;
+       dst->table = htable;
+       memcpy(dst->table, src->table, sizeof(size_t) << src->bits);
+       return true;
+}
+
 static size_t hash_bucket(const struct htable *ht, size_t h)
 {
        return h & ((1 << ht->bits)-1);
@@ -112,6 +154,17 @@ void *htable_next(const struct htable *ht, struct htable_iter *i)
        return NULL;
 }
 
+void *htable_prev(const struct htable *ht, struct htable_iter *i)
+{
+       for (;;) {
+               if (!i->off)
+                       return NULL;
+               i->off --;
+               if (entry_is_valid(ht->table[i->off]))
+                       return get_raw_ptr(ht, ht->table[i->off]);
+       }
+}
+
 /* 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)
 {
@@ -140,8 +193,7 @@ static COLD bool double_table(struct htable *ht)
                return false;
        }
        ht->bits++;
-       ht->max = ((size_t)3 << ht->bits) / 4;
-       ht->max_with_deleted = ((size_t)9 << ht->bits) / 10;
+       htable_adjust_capacity(ht);
 
        /* If we lost our "perfect bit", get it back now. */
        if (!ht->perfect_bit && ht->common_mask) {
@@ -197,8 +249,17 @@ static COLD void update_common(struct htable *ht, const void *p)
        uintptr_t maskdiff, bitsdiff;
 
        if (ht->elems == 0) {
-               ht->common_mask = -1;
-               ht->common_bits = (uintptr_t)p;
+               /* Always reveal one bit of the pointer in the bucket,
+                * so it's not zero or HTABLE_DELETED (1), even if
+                * hash happens to be 0.  Assumes (void *)1 is not a
+                * valid pointer. */
+               for (i = sizeof(uintptr_t)*CHAR_BIT - 1; i > 0; i--) {
+                       if ((uintptr_t)p & ((uintptr_t)1 << i))
+                               break;
+               }
+
+               ht->common_mask = ~((uintptr_t)1 << i);
+               ht->common_bits = ((uintptr_t)p & ht->common_mask);
                ht->perfect_bit = 1;
                return;
        }