Commit: 1f64371ec036c2f6ab450e37ad5a9c1120f1c54f
Author: Campbell Barton
Date:   Thu Jan 30 20:51:44 2014 +1100
https://developer.blender.org/rB1f64371ec036c2f6ab450e37ad5a9c1120f1c54f

Smallhash: refactor and fixes

- BLI_smallhash_remove didnt decrement total entries.
- rename vars to match closer to ghash.
- smallhash_lookup returns NULL when no entry found.
- using a zero value key wasn't supported.
- no need to memset or calloc bucket arrays
- add asserts for unsupported conditions.
- added BLI_smallhash_lookup_p

===================================================================

M       source/blender/blenlib/BLI_smallhash.h
M       source/blender/blenlib/intern/smallhash.c

===================================================================

diff --git a/source/blender/blenlib/BLI_smallhash.h 
b/source/blender/blenlib/BLI_smallhash.h
index ec7b27f..07bd997 100644
--- a/source/blender/blenlib/BLI_smallhash.h
+++ b/source/blender/blenlib/BLI_smallhash.h
@@ -42,29 +42,31 @@ typedef struct {
 /*how much stack space to use before dynamically allocating memory*/
 #define SMSTACKSIZE 64
 typedef struct SmallHash {
-       SmallHashEntry *table;
-       SmallHashEntry _stacktable[SMSTACKSIZE];
-       SmallHashEntry _copytable[SMSTACKSIZE];
-       SmallHashEntry *stacktable, *copytable;
-       unsigned int used;
-       unsigned int curhash;
-       unsigned int size;
+       SmallHashEntry *buckets;
+       SmallHashEntry *buckets_stack;
+       SmallHashEntry *buckets_copy;
+       SmallHashEntry _buckets_stack[SMSTACKSIZE];
+       SmallHashEntry _buckets_copy[SMSTACKSIZE];
+       unsigned int nbuckets;
+       unsigned int nentries;
+       unsigned int cursize;
 } SmallHash;
 
 typedef struct {
-       SmallHash *hash;
+       SmallHash *sh;
        unsigned int i;
 } SmallHashIter;
 
-void    BLI_smallhash_init(SmallHash *hash) ATTR_NONNULL(1);
-void    BLI_smallhash_release(SmallHash *hash) ATTR_NONNULL(1);
-void    BLI_smallhash_insert(SmallHash *hash, uintptr_t key, void *item) 
ATTR_NONNULL(1);
-void    BLI_smallhash_remove(SmallHash *hash, uintptr_t key) ATTR_NONNULL(1);
-void   *BLI_smallhash_lookup(SmallHash *hash, uintptr_t key) ATTR_NONNULL(1) 
ATTR_WARN_UNUSED_RESULT;
-bool    BLI_smallhash_haskey(SmallHash *hash, uintptr_t key) ATTR_NONNULL(1);
-int     BLI_smallhash_count(SmallHash *hash)  ATTR_NONNULL(1);
+void    BLI_smallhash_init(SmallHash *sh) ATTR_NONNULL(1);
+void    BLI_smallhash_release(SmallHash *sh) ATTR_NONNULL(1);
+void    BLI_smallhash_insert(SmallHash *sh, uintptr_t key, void *item) 
ATTR_NONNULL(1);
+bool    BLI_smallhash_remove(SmallHash *sh, uintptr_t key) ATTR_NONNULL(1);
+void   *BLI_smallhash_lookup(SmallHash *sh, uintptr_t key) ATTR_NONNULL(1) 
ATTR_WARN_UNUSED_RESULT;
+void  **BLI_smallhash_lookup_p(SmallHash *sh, uintptr_t key) ATTR_NONNULL(1) 
ATTR_WARN_UNUSED_RESULT;
+bool    BLI_smallhash_haskey(SmallHash *sh, uintptr_t key) ATTR_NONNULL(1);
+int     BLI_smallhash_count(SmallHash *sh)  ATTR_NONNULL(1);
 void   *BLI_smallhash_iternext(SmallHashIter *iter, uintptr_t *key)  
ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
-void   *BLI_smallhash_iternew(SmallHash *hash, SmallHashIter *iter, uintptr_t 
*key) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
-/* void BLI_smallhash_print(SmallHash *hash); */ /* UNUSED */
+void   *BLI_smallhash_iternew(SmallHash *sh, SmallHashIter *iter, uintptr_t 
*key) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
+/* void BLI_smallhash_print(SmallHash *sh); */ /* UNUSED */
 
 #endif /* __BLI_SMALLHASH_H__ */
diff --git a/source/blender/blenlib/intern/smallhash.c 
b/source/blender/blenlib/intern/smallhash.c
index 3812d29..7f9acab 100644
--- a/source/blender/blenlib/intern/smallhash.c
+++ b/source/blender/blenlib/intern/smallhash.c
@@ -35,6 +35,7 @@
  */
 
 #include <string.h>
+#include <stdlib.h>
 
 #include "MEM_guardedalloc.h"
 #include "BLI_utildefines.h"
@@ -53,6 +54,7 @@
  */
 #define SMHASH_CELL_UNUSED  ((void *)0x7FFFFFFF)
 #define SMHASH_CELL_FREE    ((void *)0x7FFFFFFD)
+#define SMHASH_KEY_UNUSED ((uintptr_t)-1)
 
 /* typically this re-assigns 'h' */
 #define SMHASH_NEXT(h, hoff)  ( \
@@ -63,152 +65,190 @@
 
 extern const unsigned int hashsizes[];
 
-void BLI_smallhash_init(SmallHash *hash)
+/**
+ * Check if the number of items in the smallhash is large enough to require 
more buckets.
+ */
+BLI_INLINE bool smallhash_test_expand_buckets(const unsigned int nentries, 
const unsigned int nbuckets)
 {
-       unsigned int i;
-
-       memset(hash, 0, sizeof(*hash));
-
-       hash->table = hash->_stacktable;
-       hash->curhash = 2;
-       hash->size = hashsizes[hash->curhash];
+       return nentries * 3 > nbuckets;
+}
 
-       hash->copytable = hash->_copytable;
-       hash->stacktable = hash->_stacktable;
+BLI_INLINE void smallhash_init_empty(SmallHash *sh)
+{
+       unsigned int i;
 
-       for (i = 0; i < hash->size; i++) {
-               hash->table[i].val = SMHASH_CELL_FREE;
+       for (i = 0; i < sh->nbuckets; i++) {
+               sh->buckets[i].key = SMHASH_KEY_UNUSED;
+               sh->buckets[i].val = SMHASH_CELL_FREE;
        }
 }
 
-/*NOTE: does *not* free *hash itself!  only the direct data!*/
-void BLI_smallhash_release(SmallHash *hash)
+BLI_INLINE SmallHashEntry *smallhash_lookup(SmallHash *sh, const uintptr_t key)
 {
-       if (hash->table != hash->stacktable) {
-               MEM_freeN(hash->table);
+       SmallHashEntry *e;
+       unsigned int h = (unsigned int)key;
+       unsigned int hoff = 1;
+
+       BLI_assert(key != SMHASH_KEY_UNUSED);
+
+       /* note: there are always more buckets then entries,
+        * so we know there will always be a free bucket if the key isn't 
found. */
+       for (e = &sh->buckets[h % sh->nbuckets];
+            e->val != SMHASH_CELL_FREE;
+            h = SMHASH_NEXT(h, hoff), e = &sh->buckets[h % sh->nbuckets])
+       {
+               if (e->key == key) {
+                       /* should never happen because unused keys are zero'd */
+                       BLI_assert(e->val != SMHASH_CELL_UNUSED);
+                       return e;
+               }
        }
+
+       return NULL;
 }
 
-BLI_INLINE SmallHashEntry *smallhash_lookup_first_free(SmallHash *hash, 
uintptr_t key)
+BLI_INLINE SmallHashEntry *smallhash_lookup_first_free(SmallHash *sh, const 
uintptr_t key)
 {
-       SmallHashEntry *entry;
+       SmallHashEntry *e;
        unsigned int h = (unsigned int)key;
        unsigned int hoff = 1;
 
-       for (entry = &hash->table[h % hash->size];
-            !ELEM(entry->val, SMHASH_CELL_UNUSED, SMHASH_CELL_FREE);
-            h = SMHASH_NEXT(h, hoff), entry = &hash->table[h % hash->size])
+       for (e = &sh->buckets[h % sh->nbuckets];
+            !ELEM(e->val, SMHASH_CELL_UNUSED, SMHASH_CELL_FREE);
+            h = SMHASH_NEXT(h, hoff), e = &sh->buckets[h % sh->nbuckets])
        {
-               /* Nothing else to do! */
+               /* pass */
        }
 
-       return entry;
+       return e;
 }
 
-void BLI_smallhash_insert(SmallHash *hash, uintptr_t key, void *item)
+BLI_INLINE void smallhash_resize_buckets(SmallHash *sh, const unsigned int 
nbuckets)
 {
-       SmallHashEntry *entry;
+       SmallHashEntry *buckets_old = sh->buckets;
+       const unsigned int nbuckets_old = sh->nbuckets;
+       unsigned int i = 0;
 
-       if (hash->size < hash->used * 3) {
-               unsigned int newsize = hashsizes[++hash->curhash];
-               SmallHashEntry *tmp;
-               unsigned int i = 0;
+       BLI_assert(sh->nbuckets != nbuckets);
 
-               if (hash->table != hash->stacktable || newsize > SMSTACKSIZE) {
-                       tmp = MEM_callocN(sizeof(*hash->table) * newsize, 
__func__);
-               }
-               else {
-                       SWAP(SmallHashEntry *, hash->stacktable, 
hash->copytable);
-                       tmp = hash->stacktable;
-               }
+       if (buckets_old == sh->buckets_stack && nbuckets <= SMSTACKSIZE) {
+               SWAP(SmallHashEntry *, sh->buckets_stack, sh->buckets_copy);
+               sh->buckets = sh->buckets_stack;
+       }
+       else {
+               sh->buckets = MEM_mallocN(sizeof(*sh->buckets) * nbuckets, 
__func__);
+       }
 
-               SWAP(SmallHashEntry *, tmp, hash->table);
+       sh->nbuckets = nbuckets;
 
-               hash->size = newsize;
+       smallhash_init_empty(sh);
 
-               for (i = 0; i < hash->size; i++) {
-                       hash->table[i].val = SMHASH_CELL_FREE;
+       for (i = 0; i < nbuckets_old; i++) {
+               if (!ELEM(buckets_old[i].val, SMHASH_CELL_UNUSED, 
SMHASH_CELL_FREE)) {
+                       SmallHashEntry *e = smallhash_lookup_first_free(sh, 
buckets_old[i].key);
+                       e->key = buckets_old[i].key;
+                       e->val = buckets_old[i].val;
                }
+       }
 
-               for (i = 0; i < hashsizes[hash->curhash - 1]; i++) {
-                       if (ELEM(tmp[i].val, SMHASH_CELL_UNUSED, 
SMHASH_CELL_FREE)) {
-                               continue;
-                       }
+       if (buckets_old != sh->buckets_stack && buckets_old != 
sh->buckets_copy) {
+               MEM_freeN(buckets_old);
+       }
+}
 
-                       entry = smallhash_lookup_first_free(hash, tmp[i].key);
-                       entry->key = tmp[i].key;
-                       entry->val = tmp[i].val;
-               }
+void BLI_smallhash_init(SmallHash *sh)
+{
+       /* assume 'sh' is uninitialized */
 
-               if (tmp != hash->stacktable && tmp != hash->copytable) {
-                       MEM_freeN(tmp);
-               }
-       }
+       sh->nentries = 0;
+       sh->cursize = 2;
+       sh->nbuckets = hashsizes[sh->cursize];
 
-       entry = smallhash_lookup_first_free(hash, key);
-       entry->key = key;
-       entry->val = item;
+       sh->buckets         = sh->_buckets_stack;
+       sh->buckets_copy    = sh->_buckets_copy;
+       sh->buckets_stack   = sh->_buckets_stack;
 
-       hash->used++;
+       smallhash_init_empty(sh);
 }
 
-BLI_INLINE SmallHashEntry *smallhash_lookup(SmallHash *hash, uintptr_t key)
+/*NOTE: does *not* free *sh itself!  only the direct data!*/
+void BLI_smallhash_release(SmallHash *sh)
 {
-       SmallHashEntry *entry;
-       unsigned int h = (unsigned int)key;
-       unsigned int hoff = 1;
+       if (sh->buckets != sh->buckets_stack) {
+               MEM_freeN(sh->buckets);
+       }
+}
 
-       for (entry = &hash->table[h % hash->size];
-            ((entry->key != key) || (entry->val == SMHASH_CELL_UNUSED)) && 
(entry->val != SMHASH_CELL_FREE);
-            h = SMHASH_NEXT(h, hoff), entry = &hash->table[h % hash->size])
-       {
-               /* Nothing else to do! */
+void BLI_smallhash_insert(SmallHash *sh, uintptr_t key, void *val)
+{
+       SmallHashEntry *e;
+
+       BLI_assert(key != SMHASH_KEY_UNUSED);
+       BLI_assert(!ELEM(val, SMHASH_CELL_UNUSED, SMHASH_CELL_FREE));
+       BLI_assert(BLI_smallhash_haskey(sh, key) == false);
+
+       if (UNLIKELY(smallhash_test_expand_buckets(++sh->nentries, 
sh->nbuckets))) {
+               smallhash_resize_buckets(sh, hashsizes[++sh->cursize]);
        }
 
-       return entry;
+       e = smallhash_lookup_first_free(sh, key);
+       e->key = key;
+       e->val = val;
 }
 
-void BLI_smallhash_remove(SmallHash *hash, uintptr_t key)
+bool BLI_smallhash_remove(SmallHash *sh, uintptr_t key)
 {
-       SmallHashEntry *entry = smallhash_lookup(hash, key);
+       SmallHashEntry *e = smallhash_lookup(sh, key);
 
-       if (entry->val != SMHASH_CELL_FREE) {
-               entry->key = 0;
-               entry->val = SMHASH_CELL_UNUSED;
+       if (e) {
+               e->key = SMHASH_KEY_UNUSED;
+               e->val = SMHASH_CELL_UNUSED;
+               sh->nentries--;
+
+               return true;
+       }
+       else {
+               return false;
        }
 }
 
-void *BLI_smallhash_lookup(SmallHash *hash, uintptr_t key)
+void *BLI_smallhash_lookup(SmallHash *sh, uintptr_t key)
 {
-       SmallHashEntry *entry = smallhash_lookup(hash, key);
+       SmallHashEntry *e = smallhash_lookup(sh, key);
 
-       return ELEM(entry->val, SMHASH_CELL_UNUSED, SMHASH_CELL_FREE) ? NULL : 
entry->val;
+       return e ? e->val : NULL;
 }
 
+void **BLI_smallhash_lookup_p(SmallHash *sh, uintptr_t key)
+{
+       SmallHashEntry *e = smallhash_lookup(sh, key);
+
+       return e ? &e->val : NULL;
+}
 
-bool BLI_smallhash_haskey(SmallHash *hash, uintptr_t key)
+bool BLI_smallhash_haskey(SmallHash *sh, uintp

@@ Diff output truncated at 10240 characters. @@

_______________________________________________
Bf-blender-cvs mailing list
[email protected]
http://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to