2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * ***** END GPL LICENSE BLOCK *****
21 /** \file blender/blenlib/intern/edgehash.c
24 * An (edge -> pointer) hash table.
25 * Using unordered int-pairs as keys.
27 * \note The API matches BLI_ghash.c, but the implementation is different.
34 #include "MEM_guardedalloc.h"
36 #include "BLI_utildefines.h"
37 #include "BLI_edgehash.h"
38 #include "BLI_strict_flags.h"
40 typedef struct _EdgeHash_Edge Edge;
41 typedef struct _EdgeHash_Entry EdgeHashEntry;
43 typedef struct EdgeHash {
44 EdgeHashEntry *entries;
52 typedef struct EdgeSet {
60 /* -------------------------------------------------------------------- */
61 /** \name Internal Helper Macros & Defines
64 #define ENTRIES_CAPACITY(container) (uint)(1 << (container)->capacity_exp)
65 #define MAP_CAPACITY(container) (uint)(1 << ((container)->capacity_exp + 1))
66 #define CLEAR_MAP(container) memset(container->map, 0xFF, sizeof(int32_t) * MAP_CAPACITY(container))
67 #define UPDATE_SLOT_MASK(container) (container)->slot_mask = MAP_CAPACITY(container) - 1
68 #define PERTURB_SHIFT 5
70 #define ITER_SLOTS(CONTAINER, EDGE, SLOT, INDEX) \
71 uint32_t hash = calc_edge_hash(EDGE); \
72 uint32_t mask = (CONTAINER)->slot_mask; \
73 uint32_t perturb = hash; \
74 int32_t *map = (CONTAINER)->map; \
75 uint32_t SLOT = mask & hash; \
76 int INDEX = map[SLOT]; \
77 for (;;SLOT = mask & ((5 * SLOT) + 1 + perturb), perturb >>= PERTURB_SHIFT, INDEX = map[SLOT])
82 #define CAPACITY_EXP_DEFAULT 3
86 /* -------------------------------------------------------------------- */
87 /** \name Internal Edge API
90 BLI_INLINE uint32_t calc_edge_hash(Edge edge)
92 return (edge.v_low << 8) ^ edge.v_high;
95 BLI_INLINE Edge init_edge(uint v0, uint v1)
97 /* If there are use cases where we need this it could be removed (or flag to allow),
98 * for now this helps avoid incorrect usage (creating degenerate geometry). */
112 BLI_INLINE bool edges_equal(Edge e1, Edge e2)
114 return memcmp(&e1, &e2, sizeof(Edge)) == 0;
117 static uint calc_capacity_exp_for_reserve(uint reserve)
120 while (reserve >>= 1) result++;
126 /* -------------------------------------------------------------------- */
127 /** \name Internal Utility API
130 #define EH_INDEX_HAS_EDGE(eh, index, edge) (index) >= 0 && edges_equal((edge), (eh)->entries[index].edge)
132 static void edgehash_free_values(EdgeHash *eh, EdgeHashFreeFP free_value)
135 for (uint i = 0; i < eh->length; i++) {
136 free_value(eh->entries[i].value);
141 BLI_INLINE void edgehash_insert_index(EdgeHash *eh, Edge edge, uint entry_index)
143 ITER_SLOTS(eh, edge, slot, index) {
144 if (index == SLOT_EMPTY) {
145 eh->map[slot] = (int32_t)entry_index;
151 BLI_INLINE EdgeHashEntry *edgehash_insert_at_slot(EdgeHash *eh, uint slot, Edge edge, void *value)
153 EdgeHashEntry *entry = &eh->entries[eh->length];
155 entry->value = value;
156 eh->map[slot] = (int32_t)eh->length;
161 BLI_INLINE bool edgehash_ensure_can_insert(EdgeHash *eh)
163 if (UNLIKELY(ENTRIES_CAPACITY(eh) <= eh->length + eh->dummy_count)) {
165 UPDATE_SLOT_MASK(eh);
167 eh->entries = MEM_reallocN(eh->entries, sizeof(EdgeHashEntry) * ENTRIES_CAPACITY(eh));
168 eh->map = MEM_reallocN(eh->map, sizeof(int32_t) * MAP_CAPACITY(eh));
170 for (uint i = 0; i < eh->length; i++) {
171 edgehash_insert_index(eh, eh->entries[i].edge, i);
178 BLI_INLINE EdgeHashEntry *edgehash_insert(EdgeHash *eh, Edge edge, void *value)
180 ITER_SLOTS(eh, edge, slot, index) {
181 if (index == SLOT_EMPTY) {
182 return edgehash_insert_at_slot(eh, slot, edge, value);
184 else if (index == SLOT_DUMMY) {
186 return edgehash_insert_at_slot(eh, slot, edge, value);
191 BLI_INLINE EdgeHashEntry *edgehash_lookup_entry(EdgeHash *eh, uint v0, uint v1)
193 Edge edge = init_edge(v0, v1);
195 ITER_SLOTS(eh, edge, slot, index) {
196 if (EH_INDEX_HAS_EDGE(eh, index, edge)) {
197 return &eh->entries[index];
199 else if (index == SLOT_EMPTY) {
205 BLI_INLINE void edgehash_change_index(EdgeHash *eh, Edge edge, int new_index)
207 ITER_SLOTS(eh, edge, slot, index) {
208 if (EH_INDEX_HAS_EDGE(eh, index, edge)) {
209 eh->map[slot] = new_index;
217 /* -------------------------------------------------------------------- */
218 /** \name Edge Hash API
221 EdgeHash *BLI_edgehash_new_ex(const char *info, const uint reserve)
223 EdgeHash *eh = MEM_mallocN(sizeof(EdgeHash), info);
224 eh->capacity_exp = calc_capacity_exp_for_reserve(reserve);
225 UPDATE_SLOT_MASK(eh);
228 eh->entries = MEM_calloc_arrayN(sizeof(EdgeHashEntry), ENTRIES_CAPACITY(eh), "eh entries");
229 eh->map = MEM_malloc_arrayN(sizeof(int32_t), MAP_CAPACITY(eh), "eh map");
234 EdgeHash *BLI_edgehash_new(const char *info)
236 return BLI_edgehash_new_ex(info, 1 << CAPACITY_EXP_DEFAULT);
239 void BLI_edgehash_free(EdgeHash *eh, EdgeHashFreeFP free_value)
241 edgehash_free_values(eh, free_value);
243 MEM_freeN(eh->entries);
247 void BLI_edgehash_print(EdgeHash *eh)
249 printf("Edgehash at %p:\n", eh);
251 for (uint i = 0; i < MAP_CAPACITY(eh); i++) {
252 int index = eh->map[i];
253 printf(" %u: %d", i, index);
255 EdgeHashEntry entry = eh->entries[index];
256 printf(" -> (%u, %u) -> %p", entry.edge.v_low, entry.edge.v_high, entry.value);
260 printf(" Entries:\n");
261 for (uint i = 0; i < ENTRIES_CAPACITY(eh); i++) {
262 if (i == eh->length) printf(" **** below is rest capacity ****\n");
263 EdgeHashEntry entry = eh->entries[i];
264 printf(" %u: (%u, %u) -> %p\n", i, entry.edge.v_low, entry.edge.v_high, entry.value);
270 * Insert edge (\a v0, \a v1) into hash with given value, does
271 * not check for duplicates.
273 void BLI_edgehash_insert(EdgeHash *eh, uint v0, uint v1, void *value)
275 edgehash_ensure_can_insert(eh);
276 Edge edge = init_edge(v0, v1);
277 edgehash_insert(eh, edge, value);
281 * Assign a new value to a key that may already be in edgehash.
283 bool BLI_edgehash_reinsert(EdgeHash *eh, uint v0, uint v1, void *value)
285 Edge edge = init_edge(v0, v1);
287 ITER_SLOTS(eh, edge, slot, index) {
288 if (EH_INDEX_HAS_EDGE(eh, index, edge)) {
289 eh->entries[index].value = value;
292 else if (index == SLOT_EMPTY) {
293 if (edgehash_ensure_can_insert(eh)) {
294 edgehash_insert(eh, edge, value);
297 edgehash_insert_at_slot(eh, slot, edge, value);
305 * A version of #BLI_edgehash_lookup which accepts a fallback argument.
307 void *BLI_edgehash_lookup_default(EdgeHash *eh, uint v0, uint v1, void *default_value)
309 EdgeHashEntry *entry = edgehash_lookup_entry(eh, v0, v1);
310 return entry ? entry->value : default_value;
314 * Return value for given edge (\a v0, \a v1), or NULL if
315 * if key does not exist in hash. (If need exists
316 * to differentiate between key-value being NULL and
317 * lack of key then see BLI_edgehash_lookup_p().
319 void *BLI_edgehash_lookup(EdgeHash *eh, uint v0, uint v1)
321 EdgeHashEntry *entry = edgehash_lookup_entry(eh, v0, v1);
322 return entry ? entry->value : NULL;
326 * Return pointer to value for given edge (\a v0, \a v1),
327 * or NULL if key does not exist in hash.
329 void **BLI_edgehash_lookup_p(EdgeHash *eh, uint v0, uint v1)
331 EdgeHashEntry *entry = edgehash_lookup_entry(eh, v0, v1);
332 return entry ? &entry->value : NULL;
336 * Ensure \a (v0, v1) is exists in \a eh.
338 * This handles the common situation where the caller needs ensure a key is added to \a eh,
339 * constructing a new value in the case the key isn't found.
340 * Otherwise use the existing value.
342 * Such situations typically incur multiple lookups, however this function
343 * avoids them by ensuring the key is added,
344 * returning a pointer to the value so it can be used or initialized by the caller.
346 * \returns true when the value didn't need to be added.
347 * (when false, the caller _must_ initialize the value).
349 bool BLI_edgehash_ensure_p(EdgeHash *eh, uint v0, uint v1, void ***r_value)
351 Edge edge = init_edge(v0, v1);
353 ITER_SLOTS(eh, edge, slot, index) {
354 if (EH_INDEX_HAS_EDGE(eh, index, edge)) {
355 *r_value = &eh->entries[index].value;
358 else if (index == SLOT_EMPTY) {
359 if (edgehash_ensure_can_insert(eh)) {
360 edgehash_insert(eh, edge, NULL);
363 *r_value = &edgehash_insert_at_slot(eh, slot, edge, NULL)->value;
371 * Remove \a key (v0, v1) from \a eh, or return false if the key wasn't found.
373 * \param v0, v1: The key to remove.
374 * \param valfreefp: Optional callback to free the value.
375 * \return true if \a key was removed from \a eh.
377 bool BLI_edgehash_remove(EdgeHash *eh, uint v0, uint v1, EdgeHashFreeFP free_value)
379 uint old_length = eh->length;
380 void *value = BLI_edgehash_popkey(eh, v0, v1);
381 if (free_value && value) free_value(value);
382 return old_length > eh->length;
385 /* same as above but return the value,
386 * no free value argument since it will be returned */
388 * Remove \a key (v0, v1) from \a eh, returning the value or NULL if the key wasn't found.
390 * \param v0, v1: The key to remove.
391 * \return the value of \a key int \a eh or NULL.
393 void *BLI_edgehash_popkey(EdgeHash *eh, uint v0, uint v1)
395 Edge edge = init_edge(v0, v1);
397 ITER_SLOTS(eh, edge, slot, index) {
398 if (EH_INDEX_HAS_EDGE(eh, index, edge)) {
399 void *value = eh->entries[index].value;
402 eh->map[slot] = SLOT_DUMMY;
403 eh->entries[index] = eh->entries[eh->length];
404 if ((uint)index < eh->length) {
405 edgehash_change_index(eh, eh->entries[index].edge, index);
409 else if (index == SLOT_EMPTY) {
416 * Return boolean true/false if edge (v0,v1) in hash.
418 bool BLI_edgehash_haskey(EdgeHash *eh, uint v0, uint v1)
420 return edgehash_lookup_entry(eh, v0, v1) != NULL;
424 * Return number of keys in hash.
426 int BLI_edgehash_len(EdgeHash *eh)
428 return (int)eh->length;
432 * Remove all edges from hash.
434 void BLI_edgehash_clear_ex(EdgeHash *eh, EdgeHashFreeFP free_value, const uint UNUSED(reserve))
436 /* TODO: handle reserve */
437 edgehash_free_values(eh, free_value);
440 eh->capacity_exp = CAPACITY_EXP_DEFAULT;
445 * Wraps #BLI_edgehash_clear_ex with zero entries reserved.
447 void BLI_edgehash_clear(EdgeHash *eh, EdgeHashFreeFP free_value)
449 BLI_edgehash_clear_ex(eh, free_value, 0);
454 /* -------------------------------------------------------------------- */
455 /** \name Edge Hash Iterator API
459 * Create a new EdgeHashIterator. The hash table must not be mutated
460 * while the iterator is in use, and the iterator will step exactly
461 * BLI_edgehash_len(eh) times before becoming done.
463 EdgeHashIterator *BLI_edgehashIterator_new(EdgeHash *eh)
465 EdgeHashIterator *ehi = MEM_mallocN(sizeof(EdgeHashIterator), __func__);
466 BLI_edgehashIterator_init(ehi, eh);
471 * Init an already allocated EdgeHashIterator. The hash table must not
472 * be mutated while the iterator is in use, and the iterator will
473 * step exactly BLI_edgehash_len(eh) times before becoming done.
475 * \param ehi: The EdgeHashIterator to initialize.
476 * \param eh: The EdgeHash to iterate over.
478 void BLI_edgehashIterator_init(EdgeHashIterator *ehi, EdgeHash *eh)
480 ehi->entries = eh->entries;
481 ehi->length = eh->length;
486 * Free an EdgeHashIterator.
488 void BLI_edgehashIterator_free(EdgeHashIterator *ehi)
496 /* -------------------------------------------------------------------- */
497 /** \name EdgeSet API
499 * Use edgehash API to give 'set' functionality
502 #define ES_INDEX_HAS_EDGE(es, index, edge) (index) >= 0 && edges_equal((edge), (es)->entries[index])
504 EdgeSet *BLI_edgeset_new_ex(const char *info, const uint reserve)
506 EdgeSet *es = MEM_mallocN(sizeof(EdgeSet), info);
507 es->capacity_exp = calc_capacity_exp_for_reserve(reserve);
508 UPDATE_SLOT_MASK(es);
510 es->entries = MEM_malloc_arrayN(sizeof(Edge), ENTRIES_CAPACITY(es), "es entries");
511 es->map = MEM_malloc_arrayN(sizeof(int32_t), MAP_CAPACITY(es), "es map");
516 EdgeSet *BLI_edgeset_new(const char *info)
518 return BLI_edgeset_new_ex(info, 1 << CAPACITY_EXP_DEFAULT);
521 void BLI_edgeset_free(EdgeSet *es)
523 MEM_freeN(es->entries);
528 int BLI_edgeset_len(EdgeSet *es)
530 return (int)es->length;
533 static void edgeset_insert_index(EdgeSet *es, Edge edge, uint entry_index)
535 ITER_SLOTS(es, edge, slot, index) {
536 if (index == SLOT_EMPTY) {
537 es->map[slot] = (int)entry_index;
543 BLI_INLINE void edgeset_ensure_can_insert(EdgeSet *es)
545 if (UNLIKELY(ENTRIES_CAPACITY(es) <= es->length)) {
547 UPDATE_SLOT_MASK(es);
548 es->entries = MEM_reallocN(es->entries, sizeof(Edge) * ENTRIES_CAPACITY(es));
549 es->map = MEM_reallocN(es->map, sizeof(int32_t) * MAP_CAPACITY(es));
551 for (uint i = 0; i < es->length; i++) {
552 edgeset_insert_index(es, es->entries[i], i);
557 BLI_INLINE void edgeset_insert_at_slot(EdgeSet *es, uint slot, Edge edge)
559 es->entries[es->length] = edge;
560 es->map[slot] = (int)es->length;
565 * A version of BLI_edgeset_insert which checks first if the key is in the set.
566 * \returns true if a new key has been added.
568 * \note EdgeHash has no equivalent to this because typically the value would be different.
570 bool BLI_edgeset_add(EdgeSet *es, uint v0, uint v1)
572 edgeset_ensure_can_insert(es);
573 Edge edge = init_edge(v0, v1);
575 ITER_SLOTS(es, edge, slot, index) {
576 if (ES_INDEX_HAS_EDGE(es, index, edge)) {
579 else if (index == SLOT_EMPTY) {
580 edgeset_insert_at_slot(es, slot, edge);
587 * Adds the key to the set (no checks for unique keys!).
588 * Matching #BLI_edgehash_insert
590 void BLI_edgeset_insert(EdgeSet *es, uint v0, uint v1)
592 edgeset_ensure_can_insert(es);
593 Edge edge = init_edge(v0, v1);
595 ITER_SLOTS(es, edge, slot, index) {
596 if (index == SLOT_EMPTY) {
597 edgeset_insert_at_slot(es, slot, edge);
603 bool BLI_edgeset_haskey(EdgeSet *es, uint v0, uint v1)
605 Edge edge = init_edge(v0, v1);
607 ITER_SLOTS(es, edge, slot, index) {
608 if (ES_INDEX_HAS_EDGE(es, index, edge)) {
611 else if (index == SLOT_EMPTY) {
617 EdgeSetIterator *BLI_edgesetIterator_new(EdgeSet *es)
619 EdgeSetIterator *esi = MEM_mallocN(sizeof(EdgeSetIterator), __func__);
620 esi->edges = es->entries;
621 esi->length = es->length;
626 void BLI_edgesetIterator_free(EdgeSetIterator *esi)