Blender  V2.93
smallhash.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2008 Blender Foundation.
17  * All rights reserved.
18  */
19 
47 #include <stdlib.h>
48 #include <string.h>
49 
50 #include "BLI_sys_types.h"
51 
52 #include "MEM_guardedalloc.h"
53 
54 #include "BLI_utildefines.h"
55 
56 #include "BLI_smallhash.h"
57 
58 #include "BLI_strict_flags.h"
59 
60 #define SMHASH_KEY_UNUSED ((uintptr_t)(UINTPTR_MAX - 0))
61 #define SMHASH_CELL_FREE ((void *)(UINTPTR_MAX - 1))
62 #define SMHASH_CELL_UNUSED ((void *)(UINTPTR_MAX - 2))
63 
64 /* typically this re-assigns 'h' */
65 #define SMHASH_NEXT(h, hoff) \
66  (CHECK_TYPE_INLINE(&(h), uint *), \
67  CHECK_TYPE_INLINE(&(hoff), uint *), \
68  ((h) + (((hoff) = ((hoff)*2) + 1), (hoff))))
69 
70 /* nothing uses BLI_smallhash_remove yet */
71 // #define USE_REMOVE
72 
73 BLI_INLINE bool smallhash_val_is_used(const void *val)
74 {
75 #ifdef USE_REMOVE
77 #else
78  return (val != SMHASH_CELL_FREE);
79 #endif
80 }
81 
82 extern const uint BLI_ghash_hash_sizes[];
83 #define hashsizes BLI_ghash_hash_sizes
84 
86 {
87  return (uint)key;
88 }
89 
93 BLI_INLINE bool smallhash_test_expand_buckets(const uint nentries, const uint nbuckets)
94 {
95  /* (approx * 1.5) */
96  return (nentries + (nentries >> 1)) > nbuckets;
97 }
98 
100 {
101  uint i;
102 
103  for (i = 0; i < sh->nbuckets; i++) {
104  sh->buckets[i].key = SMHASH_KEY_UNUSED;
105  sh->buckets[i].val = SMHASH_CELL_FREE;
106  }
107 }
108 
112 BLI_INLINE void smallhash_buckets_reserve(SmallHash *sh, const uint nentries_reserve)
113 {
114  while (smallhash_test_expand_buckets(nentries_reserve, sh->nbuckets)) {
115  sh->nbuckets = hashsizes[++sh->cursize];
116  }
117 }
118 
120 {
121  SmallHashEntry *e;
122  uint h = smallhash_key(key);
123  uint hoff = 1;
124 
126 
127  /* note: there are always more buckets than entries,
128  * so we know there will always be a free bucket if the key isn't found. */
129  for (e = &sh->buckets[h % sh->nbuckets]; e->val != SMHASH_CELL_FREE;
130  h = SMHASH_NEXT(h, hoff), e = &sh->buckets[h % sh->nbuckets]) {
131  if (e->key == key) {
132  /* should never happen because unused keys are zero'd */
134  return e;
135  }
136  }
137 
138  return NULL;
139 }
140 
142 {
143  SmallHashEntry *e;
144  uint h = smallhash_key(key);
145  uint hoff = 1;
146 
147  for (e = &sh->buckets[h % sh->nbuckets]; smallhash_val_is_used(e->val);
148  h = SMHASH_NEXT(h, hoff), e = &sh->buckets[h % sh->nbuckets]) {
149  /* pass */
150  }
151 
152  return e;
153 }
154 
156 {
157  SmallHashEntry *buckets_old = sh->buckets;
158  const uint nbuckets_old = sh->nbuckets;
159  const bool was_alloc = (buckets_old != sh->buckets_stack);
160  uint i = 0;
161 
162  BLI_assert(sh->nbuckets != nbuckets);
163  if (nbuckets <= SMSTACKSIZE) {
164  const size_t size = sizeof(*buckets_old) * nbuckets_old;
165  buckets_old = alloca(size);
166  memcpy(buckets_old, sh->buckets, size);
167 
168  sh->buckets = sh->buckets_stack;
169  }
170  else {
171  sh->buckets = MEM_mallocN(sizeof(*sh->buckets) * nbuckets, __func__);
172  }
173 
174  sh->nbuckets = nbuckets;
175 
177 
178  for (i = 0; i < nbuckets_old; i++) {
179  if (smallhash_val_is_used(buckets_old[i].val)) {
180  SmallHashEntry *e = smallhash_lookup_first_free(sh, buckets_old[i].key);
181  e->key = buckets_old[i].key;
182  e->val = buckets_old[i].val;
183  }
184  }
185 
186  if (was_alloc) {
187  MEM_freeN(buckets_old);
188  }
189 }
190 
191 void BLI_smallhash_init_ex(SmallHash *sh, const uint nentries_reserve)
192 {
193  /* assume 'sh' is uninitialized */
194 
195  sh->nentries = 0;
196  sh->cursize = 2;
197  sh->nbuckets = hashsizes[sh->cursize];
198 
199  sh->buckets = sh->buckets_stack;
200 
201  if (nentries_reserve) {
202  smallhash_buckets_reserve(sh, nentries_reserve);
203 
204  if (sh->nbuckets > SMSTACKSIZE) {
205  sh->buckets = MEM_mallocN(sizeof(*sh->buckets) * sh->nbuckets, __func__);
206  }
207  }
208 
210 }
211 
213 {
214  BLI_smallhash_init_ex(sh, 0);
215 }
216 
217 /* NOTE: does *not* free *sh itself! only the direct data! */
219 {
220  if (sh->buckets != sh->buckets_stack) {
221  MEM_freeN(sh->buckets);
222  }
223 }
224 
225 void BLI_smallhash_insert(SmallHash *sh, uintptr_t key, void *item)
226 {
227  SmallHashEntry *e;
228 
231  BLI_assert(BLI_smallhash_haskey(sh, key) == false);
232 
235  }
236 
237  e = smallhash_lookup_first_free(sh, key);
238  e->key = key;
239  e->val = item;
240 }
241 
249 bool BLI_smallhash_reinsert(SmallHash *sh, uintptr_t key, void *item)
250 {
251  SmallHashEntry *e = smallhash_lookup(sh, key);
252  if (e) {
253  e->val = item;
254  return false;
255  }
256 
257  BLI_smallhash_insert(sh, key, item);
258  return true;
259 }
260 
261 #ifdef USE_REMOVE
263 {
264  SmallHashEntry *e = smallhash_lookup(sh, key);
265 
266  if (e) {
267  e->key = SMHASH_KEY_UNUSED;
268  e->val = SMHASH_CELL_UNUSED;
269  sh->nentries--;
270 
271  return true;
272  }
273  else {
274  return false;
275  }
276 }
277 #endif
278 
280 {
281  SmallHashEntry *e = smallhash_lookup(sh, key);
282 
283  return e ? e->val : NULL;
284 }
285 
287 {
288  SmallHashEntry *e = smallhash_lookup(sh, key);
289 
290  return e ? &e->val : NULL;
291 }
292 
294 {
295  SmallHashEntry *e = smallhash_lookup(sh, key);
296 
297  return (e != NULL);
298 }
299 
301 {
302  return (int)sh->nentries;
303 }
304 
306 {
307  while (iter->i < iter->sh->nbuckets) {
308  if (smallhash_val_is_used(iter->sh->buckets[iter->i].val)) {
309  if (key) {
310  *key = iter->sh->buckets[iter->i].key;
311  }
312 
313  return &iter->sh->buckets[iter->i++];
314  }
315 
316  iter->i++;
317  }
318 
319  return NULL;
320 }
321 
323 {
324  SmallHashEntry *e = smallhash_iternext(iter, key);
325 
326  return e ? e->val : NULL;
327 }
328 
330 {
331  SmallHashEntry *e = smallhash_iternext(iter, key);
332 
333  return e ? &e->val : NULL;
334 }
335 
337 {
338  iter->sh = sh;
339  iter->i = 0;
340 
341  return BLI_smallhash_iternext(iter, key);
342 }
343 
345 {
346  iter->sh = sh;
347  iter->i = 0;
348 
349  return BLI_smallhash_iternext_p(iter, key);
350 }
351 
352 /* -------------------------------------------------------------------- */
356 /* note, this was called _print_smhash in knifetool.c
357  * it may not be intended for general use - campbell */
358 #if 0
359 void BLI_smallhash_print(SmallHash *sh)
360 {
361  uint i, linecol = 79, c = 0;
362 
363  printf("{");
364  for (i = 0; i < sh->nbuckets; i++) {
365  if (sh->buckets[i].val == SMHASH_CELL_UNUSED) {
366  printf("--u-");
367  }
368  else if (sh->buckets[i].val == SMHASH_CELL_FREE) {
369  printf("--f-");
370  }
371  else {
372  printf("%2x", (uint)sh->buckets[i].key);
373  }
374 
375  if (i != sh->nbuckets - 1) {
376  printf(", ");
377  }
378 
379  c += 6;
380 
381  if (c >= linecol) {
382  printf("\n ");
383  c = 0;
384  }
385  }
386 
387  fflush(stdout);
388 }
389 #endif
390 
391 #ifdef DEBUG
398 double BLI_smallhash_calc_quality(SmallHash *sh)
399 {
400  uint64_t sum = 0;
401  uint i;
402 
403  if (sh->nentries == 0) {
404  return -1.0;
405  }
406 
407  for (i = 0; i < sh->nbuckets; i++) {
408  if (sh->buckets[i].key != SMHASH_KEY_UNUSED) {
409  uint64_t count = 0;
410  SmallHashEntry *e, *e_final = &sh->buckets[i];
411  uint h = smallhash_key(e_final->key);
412  uint hoff = 1;
413 
414  for (e = &sh->buckets[h % sh->nbuckets]; e != e_final;
415  h = SMHASH_NEXT(h, hoff), e = &sh->buckets[h % sh->nbuckets]) {
416  count += 1;
417  }
418 
419  sum += count;
420  }
421  }
422  return ((double)(sh->nentries + sum) / (double)sh->nentries);
423 }
424 #endif
425 
#define BLI_assert(a)
Definition: BLI_assert.h:58
#define BLI_INLINE
#define SMSTACKSIZE
Definition: BLI_smallhash.h:39
bool BLI_smallhash_remove(SmallHash *sh, uintptr_t key) ATTR_NONNULL(1)
Strict compiler flags for areas of code we want to ensure don't do conversions without us knowing abo...
unsigned int uint
Definition: BLI_sys_types.h:83
#define UNLIKELY(x)
#define ELEM(...)
Read Guarded memory(de)allocation.
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
static T sum(const btAlignedObjectArray< T > &items)
int count
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:47
static unsigned c
Definition: RandGen.cpp:97
void ** BLI_smallhash_lookup_p(const SmallHash *sh, uintptr_t key)
Definition: smallhash.c:286
void BLI_smallhash_insert(SmallHash *sh, uintptr_t key, void *item)
Definition: smallhash.c:225
void BLI_smallhash_init_ex(SmallHash *sh, const uint nentries_reserve)
Definition: smallhash.c:191
BLI_INLINE bool smallhash_val_is_used(const void *val)
Definition: smallhash.c:73
#define SMHASH_CELL_UNUSED
Definition: smallhash.c:62
BLI_INLINE SmallHashEntry * smallhash_iternext(SmallHashIter *iter, uintptr_t *key)
Definition: smallhash.c:305
#define hashsizes
Definition: smallhash.c:83
bool BLI_smallhash_reinsert(SmallHash *sh, uintptr_t key, void *item)
Definition: smallhash.c:249
BLI_INLINE SmallHashEntry * smallhash_lookup_first_free(SmallHash *sh, const uintptr_t key)
Definition: smallhash.c:141
BLI_INLINE void smallhash_init_empty(SmallHash *sh)
Definition: smallhash.c:99
BLI_INLINE void smallhash_buckets_reserve(SmallHash *sh, const uint nentries_reserve)
Definition: smallhash.c:112
#define SMHASH_CELL_FREE
Definition: smallhash.c:61
BLI_INLINE void smallhash_resize_buckets(SmallHash *sh, const uint nbuckets)
Definition: smallhash.c:155
void * BLI_smallhash_lookup(const SmallHash *sh, uintptr_t key)
Definition: smallhash.c:279
bool BLI_smallhash_haskey(const SmallHash *sh, uintptr_t key)
Definition: smallhash.c:293
BLI_INLINE SmallHashEntry * smallhash_lookup(const SmallHash *sh, const uintptr_t key)
Definition: smallhash.c:119
#define SMHASH_KEY_UNUSED
Definition: smallhash.c:60
void ** BLI_smallhash_iternew_p(const SmallHash *sh, SmallHashIter *iter, uintptr_t *key)
Definition: smallhash.c:344
BLI_INLINE uint smallhash_key(const uintptr_t key)
Definition: smallhash.c:85
void BLI_smallhash_release(SmallHash *sh)
Definition: smallhash.c:218
const uint BLI_ghash_hash_sizes[]
Definition: BLI_ghash.c:56
void BLI_smallhash_init(SmallHash *sh)
Definition: smallhash.c:212
int BLI_smallhash_len(const SmallHash *sh)
Definition: smallhash.c:300
void ** BLI_smallhash_iternext_p(SmallHashIter *iter, uintptr_t *key)
Definition: smallhash.c:329
void * BLI_smallhash_iternext(SmallHashIter *iter, uintptr_t *key)
Definition: smallhash.c:322
BLI_INLINE bool smallhash_test_expand_buckets(const uint nentries, const uint nbuckets)
Definition: smallhash.c:93
#define SMHASH_NEXT(h, hoff)
Definition: smallhash.c:65
void * BLI_smallhash_iternew(const SmallHash *sh, SmallHashIter *iter, uintptr_t *key)
Definition: smallhash.c:336
_W64 unsigned int uintptr_t
Definition: stdint.h:122
unsigned __int64 uint64_t
Definition: stdint.h:93
uintptr_t key
Definition: BLI_smallhash.h:33
unsigned int i
Definition: BLI_smallhash.h:51
const SmallHash * sh
Definition: BLI_smallhash.h:50
unsigned int cursize
Definition: BLI_smallhash.h:43
unsigned int nentries
Definition: BLI_smallhash.h:42
SmallHashEntry * buckets
Definition: BLI_smallhash.h:45
SmallHashEntry buckets_stack[SMSTACKSIZE]
Definition: BLI_smallhash.h:46
unsigned int nbuckets
Definition: BLI_smallhash.h:41