MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
hash.hpp
Go to the documentation of this file.
1// Copyright (c) 2010-2026, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11
12#ifndef MFEM_HASH
13#define MFEM_HASH
14
15#include "../config/config.hpp"
16#include "array.hpp"
17#include "globals.hpp"
18#include "hash_util.hpp"
19
20#include <cstdint>
21#include <type_traits>
22#include <utility>
23
24namespace mfem
25{
26
27/** A concept for items that should be used in HashTable and be accessible by
28 * hashing two IDs.
29 */
30struct Hashed2
31{
32 int p1, p2;
33 int next;
34};
35
36/** A concept for items that should be used in HashTable and be accessible by
37 * hashing four IDs.
38 */
39struct Hashed4
40{
41 int p1, p2, p3; // NOTE: p4 is neither hashed nor stored
42 int next;
43};
44
45
46/** HashTable is a container for items that require associative access through
47 * pairs (or quadruples) of indices:
48 *
49 * (p1, p2) -> item
50 * (p1, p2, p3, p4) -> item
51 *
52 * An example of this are edges and faces in a mesh. Each edge is uniquely
53 * identified by two parent vertices and so can be easily accessed from
54 * different elements using this class. Similarly for faces.
55 *
56 * The order of the p1, p2, ... indices is not relevant as they are sorted
57 * each time this class is invoked.
58 *
59 * There are two main methods this class provides. The Get(...) methods always
60 * return an item given the two or four indices. If the item did not previously
61 * exist, the methods creates a new one. The Find(...) methods, on the other
62 * hand, just return NULL or -1 if the item does not exist.
63 *
64 * Each new item is automatically assigned a unique ID - the index of the item
65 * inside the BlockArray. The IDs may (but need not) be used as p1, p2, ... of
66 * other items.
67 *
68 * The item type (T) needs to follow either the Hashed2 or the Hashed4
69 * concept. It is easiest to just inherit from these structs.
70 *
71 * All items in the container can also be accessed sequentially using the
72 * provided iterator.
73 *
74 * Notes:
75 * The data structure and implementation is based on a BlockArray<T> which
76 * provides an efficient item storage that avoids heap fragmentation, and
77 * index-based item access. The hash table implemented on top of the
78 * BlockArray provides fast associative (key -> value) access by grouping
79 * items into bins (buckets) of O(1) size.
80 * - "id" denotes the index of an item in the underlying BlockArray<T>,
81 * - "idx" denotes the index of a bin, determined by hashing a key with
82 * the function `Hash`.
83 */
84template<typename T>
85class HashTable : public BlockArray<T>
86{
87protected:
89
90public:
91 /** @brief Main constructor of the HashTable class.
92
93 @param[in] block_size The size of the storage blocks of the underlying
94 BlockArray<T>.
95 @param[in] init_hash_size The initial size of the hash table. Must be
96 a power of 2. */
97 HashTable(int block_size = 16*1024, int init_hash_size = 32*1024);
98 /// Deep copy
99 HashTable(const HashTable& other);
100 /// Copy assignment not supported
101 HashTable& operator=(const HashTable&) = delete;
103
104 /** @brief Item accessor with key (or parents) the pair p1, p2. Default
105 construct an item of type T if no value corresponds to the requested key.
106
107 @param[in] p1 First part of the key.
108 @param[in] p2 Second part of the key.
109 @return The index "id" of the key in the BlockArray<T>.
110
111 @warning This method should only be called if T inherits from Hashed2. */
112 T* Get(int p1, int p2);
113
114 /** @brief Item accessor with key (or parents) the quadruplet p1, p2, p3, p4.
115 The key p4 is optional. Default construct an item of type T if no value
116 corresponds to the requested key.
117
118 @param[in] p1 First part of the key.
119 @param[in] p2 Second part of the key.
120 @param[in] p3 Third part of the key.
121 @param[in] p4 Fourth part of the key (optional).
122 @return The index "id" of the key in the BlockArray<T>.
123
124 @warning This method should only be called if T inherits from Hashed4. */
125 T* Get(int p1, int p2, int p3, int p4 = -1 /* p4 optional */);
126
127 /** @brief Get the "id" of the item whose parents are p1, p2, this "id"
128 corresponding to the index of the item in the underlying BlockArray<T>
129 object. Default construct an item and "id" if no value corresponds to the
130 requested key.
131
132 @param[in] p1 First part of the key.
133 @param[in] p2 Second part of the key.
134 @return The index "id" of the key in the BlockArray<T>.
135
136 @warning This method should only be called if T inherits from Hashed2. */
137 int GetId(int p1, int p2);
138
139 /** @brief Get the "id" of an item, this "id" corresponding to the index of
140 the item in the underlying BlockArray<T> object. Default construct an item
141 and "id" if no value corresponds to the requested key.
142
143 @param[in] p1 First part of the key.
144 @param[in] p2 Second part of the key.
145 @param[in] p3 Third part of the key.
146 @param[in] p4 Fourth part of the key (optional).
147 @return The index "id" of the key in the BlockArray<T>.
148
149 @warning This method should only be called if T inherits from Hashed4. */
150 int GetId(int p1, int p2, int p3, int p4 = -1);
151
152 /** @brief Item accessor with key (or parents) the pair p1, p2. Return
153 NULL if no value corresponds to the requested key.
154
155 @param[in] p1 First part of the key.
156 @param[in] p2 Second part of the key.
157 @return The item associated to the key (p1,p2).
158
159 @warning This method should only be called if T inherits from Hashed2. */
160 T* Find(int p1, int p2);
161
162 /** @brief Item accessor with key (or parents) the quadruplet p1, p2, p3, p4.
163 The key p4 is optional. Return NULL if no value corresponds to the
164 requested key.
165
166 @param[in] p1 First part of the key.
167 @param[in] p2 Second part of the key.
168 @param[in] p3 Third part of the key.
169 @param[in] p4 Fourth part of the key (optional).
170 @return The item associated to the key (p1,p2,p3,p4).
171
172 @warning This method should only be called if T inherits from Hashed4. */
173 T* Find(int p1, int p2, int p3, int p4 = -1);
174
175 /** @brief Item const accessor with key (or parents) the pair p1, p2.
176 Return NULL if no value corresponds to the requested key.
177
178 @param[in] p1 First part of the key.
179 @param[in] p2 Second part of the key.
180 @return The item associated to the key (p1,p2).
181
182 @warning This method should only be called if T inherits from Hashed2. */
183 const T* Find(int p1, int p2) const;
184
185 /** @brief Item const accessor with key (or parents) the quadruplet p1, p2,
186 p3, p4. The key p4 is optional. Return NULL if no value corresponds to the
187 requested key.
188
189 @param[in] p1 First part of the key.
190 @param[in] p2 Second part of the key.
191 @param[in] p3 Third part of the key.
192 @param[in] p4 Fourth part of the key (optional).
193 @return The item associated to the key (p1,p2,p3,p4).
194
195 @warning This method should only be called if T inherits from Hashed4. */
196 const T* Find(int p1, int p2, int p3, int p4 = -1) const;
197
198 /** @brief Find the "id" of an item whose parents are p1, p2. Return -1 if it
199 does not exist.
200
201 This "id" corresponds to the index of the item in the underlying
202 BlockArray<T> object. Default construct an item and "id" if no value
203 corresponds to the requested key.
204
205 @param[in] p1 First part of the key.
206 @param[in] p2 Second part of the key.
207 @return The index "id" of the key in the BlockArray<T>.
208
209 @warning This method should only be called if T inherits from Hashed2. */
210 int FindId(int p1, int p2) const;
211
212 /** @brief Find the "id" of an item, this "id" corresponding to the index of
213 the item in the underlying BlockArray<T> object. Return -1 if it does not
214 exist. Default construct an item and "id" if no value corresponds to the
215 requested key.
216
217 @param[in] p1 First part of the key.
218 @param[in] p2 Second part of the key.
219 @param[in] p3 Third part of the key.
220 @param[in] p4 Fourth part of the key (optional).
221 @return The index "id" of the key in the BlockArray<T>.
222
223 @warning This method should only be called if T inherits from Hashed4. */
224 int FindId(int p1, int p2, int p3, int p4 = -1) const;
225
226 /// Return the number of elements currently stored in the HashTable.
227 int Size() const { return Base::Size() - unused.Size(); }
228
229 /// Return the total number of ids (used and unused) in the HashTable.
230 int NumIds() const { return Base::Size(); }
231
232 /// Return the number of free/unused ids in the HashTable.
233 int NumFreeIds() const { return unused.Size(); }
234
235 /** @brief Return true if item @a id exists in (is used by) the container.
236
237 @param[in] id Index of the item in the underlying BlockArray<T>.
238
239 @warning It is assumed that 0 <= id < NumIds(). */
240 bool IdExists(int id) const { return (Base::At(id).next != -2); }
241
242 /** @brief Remove an item from the hash table.
243
244 @param[in] id Index of the item in the underlying BlockArray<T>.
245
246 @warning Its @a id will be reused by newly added items. */
247 void Delete(int id);
248
249 /// Remove all items.
250 void DeleteAll();
251
252 /** @brief Allocate an item at @a id. Enlarge the underlying BlockArray if
253 necessary.
254
255 @param[in] id Index of the item in the underlying BlockArray<T>.
256 @param[in] p1 First part of the key.
257 @param[in] p2 Second part of the key.
258
259 @warning This is a special purpose method used when loading data from a
260 file. Does nothing if the slot @a id has already been allocated. */
261 void Alloc(int id, int p1, int p2);
262
263 /** @brief Reinitialize the internal list of unallocated items.
264
265 @warning This is a special purpose method used when loading data from a file. */
267
268 /** @brief Change the key associated with an item.
269
270 In other words, makes an item hashed under different parent IDs.
271
272 @param[in] id Index of the item in the underlying BlockArray<T>.
273 @param[in] new_p1 First part of the new key.
274 @param[in] new_p2 Second part of the new key.
275
276 @warning This method should only be called if T inherits from Hashed2. */
277 void Reparent(int id, int new_p1, int new_p2);
278
279 /** @brief Change the key associated with an item.
280
281 In other words, makes an item hashed under different parent IDs.
282
283 @param[in] id Index of the item in the underlying BlockArray<T>.
284 @param[in] new_p1 First part of the new key.
285 @param[in] new_p2 Second part of the new key.
286 @param[in] new_p3 Third part of the new key.
287 @param[in] new_p4 Fourth part of the new key (optional).
288
289 @warning This method should only be called if T inherits from Hashed4. */
290 void Reparent(int id, int new_p1, int new_p2, int new_p3, int new_p4 = -1);
291
292 /// Return total size of allocated memory (tables plus items), in bytes.
293 std::size_t MemoryUsage() const;
294
295 /// Write details of the memory usage to the mfem output stream.
296 void PrintMemoryDetail() const;
297
298 /// Print a histogram of bin sizes for debugging purposes.
299 void PrintStats() const;
300
302 {
303 protected:
304 friend class HashTable;
305 typedef typename Base::iterator base;
306
308 iterator(const base &it) : base(it)
309 {
310 while (base::good() && (*this)->next == -2) { base::next(); }
311 }
312
313 public:
315 {
316 while (base::next(), base::good() && (*this)->next == -2) { }
317 return *this;
318 }
319 };
320
322 {
323 protected:
324 friend class HashTable;
325 typedef typename Base::const_iterator base;
326
328 const_iterator(const base &it) : base(it)
329 {
330 while (base::good() && (*this)->next == -2) { base::next(); }
331 }
332
333 public:
335 {
336 while (base::next(), base::good() && (*this)->next == -2) { }
337 return *this;
338 }
339 };
340
342 iterator end() { return iterator(); }
344 const_iterator end() const { return const_iterator(); }
345
347 const_iterator cend() const { return const_iterator(); }
348
349protected:
350 /** The hash table: each bin is a linked list of items. For each non-empty
351 bin, this arrays stores the "id" of the first item in the list, or -1
352 if the bin is empty. */
353 int* table;
354
355 /** mask = table_size-1. Used for fast modulo operation in Hash(), to wrap
356 the raw hashed index around the current table size (which must be a power
357 of two). */
358 int mask;
359
360 /** List of deleted items in the BlockArray<T>. New items are created with
361 these ids first, before they are appended to the block array. */
363
364 /** @brief hash function for Hashed2 items.
365
366 @param[in] p1 First part of the key.
367 @param[in] p2 Second part of the key.
368 @return The hash key "idx" identifying a bin/bucket.
369
370 NOTE: the constants are arbitrary
371 @warning This method should only be called if T inherits from Hashed2. */
372 inline int Hash(size_t p1, size_t p2) const
373 { return (984120265ul*p1 + 125965121ul*p2) & mask; }
374
375 /** @brief hash function for Hashed4 items.
376
377 @param[in] p1 First part of the key.
378 @param[in] p2 Second part of the key.
379 @param[in] p3 Third part of the key.
380 @return The hash key "idx" identifying a bin/bucket.
381
382 NOTE: The constants are arbitrary.
383 NOTE: p4 is not hashed nor stored as p1, p2, p3 identify a face uniquely.
384 @warning This method should only be called if T inherits from Hashed4. */
385 inline int Hash(size_t p1, size_t p2, size_t p3) const
386 { return (984120265ul*p1 + 125965121ul*p2 + 495698413ul*p3) & mask; }
387
388 // Delete() and Reparent() use one of these:
389 /// Hash function for items of type T that inherit from Hashed2.
390 inline int Hash(const Hashed2& item) const
391 { return Hash(item.p1, item.p2); }
392
393 /// Hash function for items of type T that inherit from Hashed4.
394 inline int Hash(const Hashed4& item) const
395 { return Hash(item.p1, item.p2, item.p3); }
396
397 /** @brief Search the index of the item associated to the key (p1,p2)
398 starting from the item with index @a id.
399
400 @param[in] id Index of the item in the underlying BlockArray<T>.
401 @param[in] p1 First part of the key.
402 @param[in] p2 Second part of the key.
403 @return The index "id" of the key in the BlockArray<T>.
404
405 @warning This method should only be called if T inherits from Hashed2. */
406 int SearchList(int id, int p1, int p2) const;
407
408 /** @brief Search the index of the item associated to the key (p1,p2,p3,(p4))
409 starting from the item with index @a id.
410
411 @param[in] id Index of the item in the underlying BlockArray<T>.
412 @param[in] p1 First part of the key.
413 @param[in] p2 Second part of the key.
414 @param[in] p3 Third part of the key.
415 @return The index "id" of the key in the BlockArray<T>.
416
417 @warning This method should only be called if T inherits from Hashed4. */
418 int SearchList(int id, int p1, int p2, int p3) const;
419
420 /** @brief Insert the item @a id into bin @a idx.
421
422 @param[in] idx The bin/bucket index.
423 @param[in] id The index of the item in the BlockArray<T>.
424 @param[in] item The item to insert at the beginning of the linked list.
425
426 @warning The method only works with bin @a idx and does not check the
427 overall fill factor of the hash table. If appropriate, use
428 CheckRehash() for that. */
429 inline void Insert(int idx, int id, T &item);
430
431 /** @brief Unlink an item @a id from the linked list of bin @a idx.
432
433 @param[in] idx The bin/bucket index.
434 @param[in] id The index of the item in the BlockArray<T>.
435
436 @warning The method aborts if the item is not found. */
437 void Unlink(int idx, int id);
438
439 /** @brief Check table fill factor and resize if necessary.
440
441 The method checks the average size of the bins (i.e., the fill factor).
442 If the fill factor is > 2, the table is enlarged (see DoRehash()). */
443 inline void CheckRehash();
444
445 /** @brief Double the size of the hash table (i.e., double the number of bins)
446 and reinsert all items into the new bins.
447
448 NOTE: Rehashing is computationally expensive (O(N) in the number of items),
449 but since it is only done rarely (when the number of items doubles), the
450 amortized complexity of inserting an item is still O(1). */
451 void DoRehash();
452
453 /** @brief Return the size of the bin @a idx.
454
455 @param[in] idx The index of the bin.
456 @return The size of the bin. */
457 int BinSize(int idx) const;
458};
459
460/// Hash function for data sequences.
461/** Depends on GnuTLS for SHA-256 hashing. */
463{
464protected:
466
467 /// Add a sequence of bytes for hashing
468 void HashBuffer(const void *buffer, size_t num_bytes);
469
470 /// Integer encoding method; result is independent of endianness and type
471 template <typename int_type_const_iter>
472 HashFunction &EncodeAndHashInts(int_type_const_iter begin,
473 int_type_const_iter end);
474
475 /// Double encoding method: encode in little-endian byte-order
476 template <typename double_const_iter>
477 HashFunction &EncodeAndHashDoubles(double_const_iter begin,
478 double_const_iter end);
479
480public:
481 /// Default constructor: initialize the hash function
482 HashFunction();
483
484 /// Destructor
486
487 /// Add a sequence of bytes for hashing
488 HashFunction &AppendBytes(const void *seq, size_t num_bytes)
489 { HashBuffer(seq, num_bytes); return *this; }
490
491 /// Add a sequence of integers for hashing, given as a c-array.
492 /** Before hashing the sequence is encoded so that the result is independent
493 of endianness and type: int, long, unsigned, etc. */
494 template <typename int_type>
495 HashFunction &AppendInts(const int_type *ints, size_t num_ints)
496 { return EncodeAndHashInts(ints, ints + num_ints); }
497
498 /// Add a sequence of integers for hashing, given as a fixed-size c-array.
499 /** Before hashing the sequence is encoded so that the result is independent
500 of endianness and type: int, long, unsigned, etc. */
501 template <typename int_type, size_t num_ints>
502 HashFunction &AppendInts(const int_type (&ints)[num_ints])
503 { return EncodeAndHashInts(ints, ints + num_ints); }
504
505 /// Add a sequence of integers for hashing, given as a container.
506 /** Before hashing the sequence is encoded so that the result is independent
507 of endianness and type: int, long, unsigned, etc. */
508 template <typename int_type_container>
509 HashFunction &AppendInts(const int_type_container &ints)
510 { return EncodeAndHashInts(ints.begin(), ints.end()); }
511
512 /// Add a sequence of doubles for hashing, given as a c-array.
513 /** Before hashing the sequence is encoded so that the result is independent
514 of endianness. */
515 HashFunction &AppendDoubles(const real_t *doubles, size_t num_doubles)
516 { return EncodeAndHashDoubles(doubles, doubles + num_doubles); }
517
518 /// Add a sequence of doubles for hashing, given as a fixed-size c-array.
519 /** Before hashing the sequence is encoded so that the result is independent
520 of endianness. */
521 template <size_t num_doubles>
522 HashFunction &AppendDoubles(const real_t (&doubles)[num_doubles])
523 { return EncodeAndHashDoubles(doubles, doubles + num_doubles); }
524
525 /// Add a sequence of doubles for hashing, given as a container.
526 /** Before hashing the sequence is encoded so that the result is independent
527 of endianness. */
528 template <typename double_container>
529 HashFunction &AppendDoubles(const double_container &doubles)
530 { return EncodeAndHashDoubles(doubles.begin(), doubles.end()); }
531
532 /** @brief Return the hash string for the current sequence and reset (clear)
533 the sequence. */
534 std::string GetHash() const;
535};
536
537
538// implementation
539
540template<typename T>
541HashTable<T>::HashTable(int block_size, int init_hash_size)
542 : Base(block_size)
543{
544 mask = init_hash_size-1;
545 MFEM_VERIFY(!(init_hash_size & mask), "init_size must be a power of two.");
546
547 table = new int[init_hash_size];
548 for (int i = 0; i < init_hash_size; i++)
549 {
550 table[i] = -1;
551 }
552}
553
554template<typename T>
556 : Base(other), mask(other.mask)
557{
558 int size = mask+1;
559 table = new int[size];
560 memcpy(table, other.table, size*sizeof(int));
561 other.unused.Copy(unused);
562}
563
564template<typename T>
566{
567 delete [] table;
568}
569
570namespace internal
571{
572
573inline void sort3(int &a, int &b, int &c)
574{
575 if (a > b) { std::swap(a, b); }
576 if (a > c) { std::swap(a, c); }
577 if (b > c) { std::swap(b, c); }
578}
579
580inline void sort4(int &a, int &b, int &c, int &d)
581{
582 if (a > b) { std::swap(a, b); }
583 if (a > c) { std::swap(a, c); }
584 if (a > d) { std::swap(a, d); }
585 sort3(b, c, d);
586}
587
588inline void sort4_ext(int &a, int &b, int &c, int &d)
589{
590 if (d < 0) // support optional last index
591 {
592 sort3(a, b, c);
593 }
594 else
595 {
596 sort4(a, b, c, d);
597 }
598}
599
600} // internal
601
602template<typename T>
603inline T* HashTable<T>::Get(int p1, int p2)
604{
605 return &(Base::At(GetId(p1, p2)));
606}
607
608template<typename T>
609inline T* HashTable<T>::Get(int p1, int p2, int p3, int p4)
610{
611 return &(Base::At(GetId(p1, p2, p3, p4)));
612}
613
614template<typename T>
615int HashTable<T>::GetId(int p1, int p2)
616{
617 // search for the item in the hashtable
618 if (p1 > p2) { std::swap(p1, p2); }
619 int idx = Hash(p1, p2);
620 int id = SearchList(table[idx], p1, p2);
621 if (id >= 0) { return id; }
622
623 // not found - use an unused item or create a new one
624 int new_id;
625 if (unused.Size())
626 {
627 new_id = unused.Last();
628 unused.DeleteLast();
629 }
630 else
631 {
632 new_id = Base::Append();
633 }
634 T& item = Base::At(new_id);
635 item.p1 = p1;
636 item.p2 = p2;
637
638 // insert into hashtable
639 Insert(idx, new_id, item);
640 CheckRehash();
641
642 return new_id;
643}
644
645template<typename T>
646int HashTable<T>::GetId(int p1, int p2, int p3, int p4)
647{
648 // search for the item in the hashtable
649 internal::sort4_ext(p1, p2, p3, p4);
650 int idx = Hash(p1, p2, p3);
651 int id = SearchList(table[idx], p1, p2, p3);
652 if (id >= 0) { return id; }
653
654 // not found - use an unused item or create a new one
655 int new_id;
656 if (unused.Size())
657 {
658 new_id = unused.Last();
659 unused.DeleteLast();
660 }
661 else
662 {
663 new_id = Base::Append();
664 }
665 T& item = Base::At(new_id);
666 item.p1 = p1;
667 item.p2 = p2;
668 item.p3 = p3;
669
670 // insert into hashtable
671 Insert(idx, new_id, item);
672 CheckRehash();
673
674 return new_id;
675}
676
677template<typename T>
678inline T* HashTable<T>::Find(int p1, int p2)
679{
680 int id = FindId(p1, p2);
681 return (id >= 0) ? &(Base::At(id)) : NULL;
682}
683
684template<typename T>
685inline T* HashTable<T>::Find(int p1, int p2, int p3, int p4)
686{
687 int id = FindId(p1, p2, p3, p4);
688 return (id >= 0) ? &(Base::At(id)) : NULL;
689}
690
691template<typename T>
692inline const T* HashTable<T>::Find(int p1, int p2) const
693{
694 int id = FindId(p1, p2);
695 return (id >= 0) ? &(Base::At(id)) : NULL;
696}
697
698template<typename T>
699inline const T* HashTable<T>::Find(int p1, int p2, int p3, int p4) const
700{
701 int id = FindId(p1, p2, p3, p4);
702 return (id >= 0) ? &(Base::At(id)) : NULL;
703}
704
705template<typename T>
706int HashTable<T>::FindId(int p1, int p2) const
707{
708 if (p1 > p2) { std::swap(p1, p2); }
709 return SearchList(table[Hash(p1, p2)], p1, p2);
710}
711
712template<typename T>
713int HashTable<T>::FindId(int p1, int p2, int p3, int p4) const
714{
715 internal::sort4_ext(p1, p2, p3, p4);
716 return SearchList(table[Hash(p1, p2, p3)], p1, p2, p3);
717}
718
719template<typename T>
720int HashTable<T>::SearchList(int id, int p1, int p2) const
721{
722 while (id >= 0)
723 {
724 const T& item = Base::At(id);
725 if (item.p1 == p1 && item.p2 == p2) { return id; }
726 id = item.next;
727 }
728 return -1;
729}
730
731template<typename T>
732int HashTable<T>::SearchList(int id, int p1, int p2, int p3) const
733{
734 while (id >= 0)
735 {
736 const T& item = Base::At(id);
737 if (item.p1 == p1 && item.p2 == p2 && item.p3 == p3) { return id; }
738 id = item.next;
739 }
740 return -1;
741}
742
743template<typename T>
745{
746 const int fill_factor = 2;
747
748 // is the table overfull?
749 if (Base::Size() > (mask+1) * fill_factor)
750 {
751 DoRehash();
752 }
753}
754
755template<typename T>
757{
758 delete [] table;
759
760 // double the table size
761 int new_table_size = 2*(mask+1);
762 table = new int[new_table_size];
763 for (int i = 0; i < new_table_size; i++) { table[i] = -1; }
764 mask = new_table_size-1;
765
766#if defined(MFEM_DEBUG) && !defined(MFEM_USE_MPI)
767 mfem::out << _MFEM_FUNC_NAME << ": rehashing to size " << new_table_size
768 << std::endl;
769#endif
770
771 // reinsert all items
772 for (iterator it = begin(); it != end(); ++it)
773 {
774 Insert(Hash(*it), it.index(), *it);
775 }
776}
777
778template<typename T>
779inline void HashTable<T>::Insert(int idx, int id, T &item)
780{
781 // add item at the beginning of the linked list
782 item.next = table[idx];
783 table[idx] = id;
784}
785
786template<typename T>
787void HashTable<T>::Unlink(int idx, int id)
788{
789 // remove item from the linked list
790 int* p_id = table + idx;
791 while (*p_id >= 0)
792 {
793 T& item = Base::At(*p_id);
794 if (*p_id == id)
795 {
796 *p_id = item.next;
797 return;
798 }
799 p_id = &(item.next);
800 }
801 MFEM_ABORT("HashTable<>::Unlink: item not found!");
802}
803
804template<typename T>
806{
807 T& item = Base::At(id);
808 Unlink(Hash(item), id);
809 item.next = -2; // mark item as unused
810 unused.Append(id); // add its id to the unused ids
811}
812
813template<typename T>
815{
816 Base::DeleteAll();
817 for (int i = 0; i <= mask; i++) { table[i] = -1; }
818 unused.DeleteAll();
819}
820
821template<typename T>
822void HashTable<T>::Alloc(int id, int p1, int p2)
823{
824 // enlarge the BlockArray to hold 'id'
825 while (id >= Base::Size())
826 {
827 Base::At(Base::Append()).next = -2; // append "unused" items
828 }
829
830 T& item = Base::At(id);
831 if (item.next == -2)
832 {
833 item.next = -1;
834 item.p1 = p1;
835 item.p2 = p2;
836
837 Insert(Hash(p1, p2), id, item);
838 CheckRehash();
839 }
840}
841
842template<typename T>
844{
845 unused.DeleteAll();
846 for (int i = 0; i < Base::Size(); i++)
847 {
848 if (Base::At(i).next == -2) { unused.Append(i); }
849 }
850}
851
852template<typename T>
853void HashTable<T>::Reparent(int id, int new_p1, int new_p2)
854{
855 T& item = Base::At(id);
856 Unlink(Hash(item), id);
857
858 if (new_p1 > new_p2) { std::swap(new_p1, new_p2); }
859 item.p1 = new_p1;
860 item.p2 = new_p2;
861
862 // reinsert under new parent IDs
863 int new_idx = Hash(new_p1, new_p2);
864 Insert(new_idx, id, item);
865}
866
867template<typename T>
869 int new_p1, int new_p2, int new_p3, int new_p4)
870{
871 T& item = Base::At(id);
872 Unlink(Hash(item), id);
873
874 internal::sort4_ext(new_p1, new_p2, new_p3, new_p4);
875 item.p1 = new_p1;
876 item.p2 = new_p2;
877 item.p3 = new_p3;
878
879 // reinsert under new parent IDs
880 int new_idx = Hash(new_p1, new_p2, new_p3);
881 Insert(new_idx, id, item);
882}
883
884template<typename T>
885std::size_t HashTable<T>::MemoryUsage() const
886{
887 return (mask+1) * sizeof(int) + Base::MemoryUsage() + unused.MemoryUsage();
888}
889
890template<typename T>
892{
893 mfem::out << Base::MemoryUsage() << " + " << (mask+1) * sizeof(int)
894 << " + " << unused.MemoryUsage();
895}
896
897template<typename T>
898int HashTable<T>::BinSize(int idx) const
899{
900 int count = 0;
901 int id = table[idx];
902 while (id >= 0)
903 {
904 const T& item = Base::At(id);
905 id = item.next;
906 count++;
907 }
908 return count;
909}
910
911template<typename T>
913{
914 int table_size = mask+1;
915 mfem::out << "Hash table size: " << table_size << "\n";
916 mfem::out << "Item count: " << Size() << "\n";
917 mfem::out << "BlockArray size: " << Base::Size() << "\n";
918
919 const int H = 16;
920 int hist[H];
921
922 for (int i = 0; i < H; i++) { hist[i] = 0; }
923
924 for (int i = 0; i < table_size; i++)
925 {
926 int bs = BinSize(i);
927 if (bs >= H) { bs = H-1; }
928 hist[bs]++;
929 }
930
931 mfem::out << "Bin size histogram:\n";
932 for (int i = 0; i < H; i++)
933 {
934 mfem::out << " size " << i << ": "
935 << hist[i] << " bins" << std::endl;
936 }
937}
938
939
940template <typename int_type_const_iter>
942 int_type_const_iter end)
943{
944 // For hashing, an integer k is encoded as follows:
945 // * 1 byte = sign_bit(k) + num_bytes(k), where
946 // - sign_bit(k) = (k >= 0) ? 0 : 128
947 // - num_bytes(k) = minimum number of bytes needed to represent abs(k)
948 // with the convention that num_bytes(0) = 0.
949 // * num_bytes(k) bytes = the bytes of abs(k), starting with the least
950 // significant byte.
951
952 static_assert(
953 std::is_integral<
954 /**/ typename std::remove_reference<decltype(*begin)>::type
955 /**/ >::value,
956 "invalid iterator type");
957
958 // Skip encoding if hashing is not available:
959 if (hash_data == nullptr) { return *this; }
960
961 constexpr int max_buffer_bytes = 64*1024;
962 unsigned char buffer[max_buffer_bytes];
963 int buffer_counter = 0;
964 while (begin != end)
965 {
966 int byte_counter = 0;
967 auto k = *begin;
968 buffer[buffer_counter] = (k >= 0) ? 0 : (k = -k, 128);
969 while (k != 0)
970 {
971 byte_counter++;
972 buffer[buffer_counter + byte_counter] = (unsigned char)(k % 256);
973 k /= 256; // (k >>= 8) results in error, e.g. for 'char'
974 }
975 buffer[buffer_counter] |= byte_counter;
976 buffer_counter += (byte_counter + 1);
977
978 ++begin;
979
980 if (begin == end ||
981 buffer_counter + (1 + sizeof(*begin)) > max_buffer_bytes)
982 {
983 HashBuffer(buffer, buffer_counter);
984 buffer_counter = 0;
985 }
986 }
987 return *this;
988}
989
990template <typename double_const_iter>
992 double_const_iter end)
993{
994 // For hashing, a double is encoded in little endian byte-order.
995
996 static_assert(
997 std::is_same<decltype(*begin), const real_t &>::value,
998 "invalid iterator type");
999
1000 // Skip encoding if hashing is not available:
1001 if (hash_data == nullptr) { return *this; }
1002
1003 constexpr int max_buffer_bytes = 64*1024;
1004 unsigned char buffer[max_buffer_bytes];
1005 int buffer_counter = 0;
1006 while (begin != end)
1007 {
1008 auto k = reinterpret_cast<const uint64_t &>(*begin);
1009 for (int i = 0; i != 7; i++)
1010 {
1011 buffer[buffer_counter++] = (unsigned char)(k & 255); k >>= 8;
1012 }
1013 buffer[buffer_counter++] = (unsigned char)k;
1014
1015 ++begin;
1016
1017 if (begin == end || buffer_counter + 8 > max_buffer_bytes)
1018 {
1019 HashBuffer(buffer, buffer_counter);
1020 buffer_counter = 0;
1021 }
1022 }
1023 return *this;
1024}
1025
1026} // namespace mfem
1027
1028#endif
int Size() const
Return the logical size of the array.
Definition array.hpp:192
void Copy(Array &copy) const
Create a copy of the internal array to the provided copy.
Definition array.hpp:1071
iterator begin()
Definition array.hpp:739
T & At(int index)
Access item of the array.
Definition array.hpp:629
const_iterator cbegin() const
Definition array.hpp:744
int Size() const
Return the number of items actually stored.
Definition array.hpp:645
Hash function for data sequences.
Definition hash.hpp:463
HashFunction()
Default constructor: initialize the hash function.
Definition hash.cpp:29
HashFunction & AppendInts(const int_type(&ints)[num_ints])
Add a sequence of integers for hashing, given as a fixed-size c-array.
Definition hash.hpp:502
HashFunction & AppendDoubles(const real_t(&doubles)[num_doubles])
Add a sequence of doubles for hashing, given as a fixed-size c-array.
Definition hash.hpp:522
HashFunction & AppendDoubles(const double_container &doubles)
Add a sequence of doubles for hashing, given as a container.
Definition hash.hpp:529
HashFunction & EncodeAndHashInts(int_type_const_iter begin, int_type_const_iter end)
Integer encoding method; result is independent of endianness and type.
Definition hash.hpp:941
HashFunction & EncodeAndHashDoubles(double_const_iter begin, double_const_iter end)
Double encoding method: encode in little-endian byte-order.
Definition hash.hpp:991
std::string GetHash() const
Return the hash string for the current sequence and reset (clear) the sequence.
Definition hash.cpp:60
~HashFunction()
Destructor.
Definition hash.cpp:38
HashFunction & AppendBytes(const void *seq, size_t num_bytes)
Add a sequence of bytes for hashing.
Definition hash.hpp:488
HashFunction & AppendDoubles(const real_t *doubles, size_t num_doubles)
Add a sequence of doubles for hashing, given as a c-array.
Definition hash.hpp:515
HashFunction & AppendInts(const int_type *ints, size_t num_ints)
Add a sequence of integers for hashing, given as a c-array.
Definition hash.hpp:495
HashFunction & AppendInts(const int_type_container &ints)
Add a sequence of integers for hashing, given as a container.
Definition hash.hpp:509
void HashBuffer(const void *buffer, size_t num_bytes)
Add a sequence of bytes for hashing.
Definition hash.cpp:45
Base::const_iterator base
Definition hash.hpp:325
const_iterator(const base &it)
Definition hash.hpp:328
const_iterator & operator++()
Definition hash.hpp:334
iterator(const base &it)
Definition hash.hpp:308
Base::iterator base
Definition hash.hpp:305
iterator & operator++()
Definition hash.hpp:314
void Reparent(int id, int new_p1, int new_p2, int new_p3, int new_p4=-1)
Change the key associated with an item.
Definition hash.hpp:868
const_iterator end() const
Definition hash.hpp:344
T * Get(int p1, int p2)
Item accessor with key (or parents) the pair p1, p2. Default construct an item of type T if no value ...
Definition hash.hpp:603
void PrintMemoryDetail() const
Write details of the memory usage to the mfem output stream.
Definition hash.hpp:891
int Hash(const Hashed4 &item) const
Hash function for items of type T that inherit from Hashed4.
Definition hash.hpp:394
void DeleteAll()
Remove all items.
Definition hash.hpp:814
T * Find(int p1, int p2)
Item accessor with key (or parents) the pair p1, p2. Return NULL if no value corresponds to the reque...
Definition hash.hpp:678
bool IdExists(int id) const
Return true if item id exists in (is used by) the container.
Definition hash.hpp:240
int Hash(size_t p1, size_t p2, size_t p3) const
hash function for Hashed4 items.
Definition hash.hpp:385
iterator begin()
Definition hash.hpp:341
HashTable(int block_size=16 *1024, int init_hash_size=32 *1024)
Main constructor of the HashTable class.
Definition hash.hpp:541
void Reparent(int id, int new_p1, int new_p2)
Change the key associated with an item.
Definition hash.hpp:853
BlockArray< T > Base
Definition hash.hpp:88
T * Get(int p1, int p2, int p3, int p4=-1)
Item accessor with key (or parents) the quadruplet p1, p2, p3, p4. The key p4 is optional....
Definition hash.hpp:609
int Hash(size_t p1, size_t p2) const
hash function for Hashed2 items.
Definition hash.hpp:372
const_iterator begin() const
Definition hash.hpp:343
void Insert(int idx, int id, T &item)
Insert the item id into bin idx.
Definition hash.hpp:779
int FindId(int p1, int p2, int p3, int p4=-1) const
Find the "id" of an item, this "id" corresponding to the index of the item in the underlying BlockArr...
Definition hash.hpp:713
int SearchList(int id, int p1, int p2) const
Search the index of the item associated to the key (p1,p2) starting from the item with index id.
Definition hash.hpp:720
int GetId(int p1, int p2)
Get the "id" of the item whose parents are p1, p2, this "id" corresponding to the index of the item i...
Definition hash.hpp:615
void CheckRehash()
Check table fill factor and resize if necessary.
Definition hash.hpp:744
int NumIds() const
Return the total number of ids (used and unused) in the HashTable.
Definition hash.hpp:230
const T * Find(int p1, int p2, int p3, int p4=-1) const
Item const accessor with key (or parents) the quadruplet p1, p2, p3, p4. The key p4 is optional....
Definition hash.hpp:699
int SearchList(int id, int p1, int p2, int p3) const
Search the index of the item associated to the key (p1,p2,p3,(p4)) starting from the item with index ...
Definition hash.hpp:732
iterator end()
Definition hash.hpp:342
int Hash(const Hashed2 &item) const
Hash function for items of type T that inherit from Hashed2.
Definition hash.hpp:390
int GetId(int p1, int p2, int p3, int p4=-1)
Get the "id" of an item, this "id" corresponding to the index of the item in the underlying BlockArra...
Definition hash.hpp:646
void Delete(int id)
Remove an item from the hash table.
Definition hash.hpp:805
HashTable(const HashTable &other)
Deep copy.
Definition hash.hpp:555
int BinSize(int idx) const
Return the size of the bin idx.
Definition hash.hpp:898
int FindId(int p1, int p2) const
Find the "id" of an item whose parents are p1, p2. Return -1 if it does not exist.
Definition hash.hpp:706
Array< int > unused
Definition hash.hpp:362
void DoRehash()
Double the size of the hash table (i.e., double the number of bins) and reinsert all items into the n...
Definition hash.hpp:756
T * Find(int p1, int p2, int p3, int p4=-1)
Item accessor with key (or parents) the quadruplet p1, p2, p3, p4. The key p4 is optional....
Definition hash.hpp:685
void Alloc(int id, int p1, int p2)
Allocate an item at id. Enlarge the underlying BlockArray if necessary.
Definition hash.hpp:822
const_iterator cend() const
Definition hash.hpp:347
int Size() const
Return the number of elements currently stored in the HashTable.
Definition hash.hpp:227
void Unlink(int idx, int id)
Unlink an item id from the linked list of bin idx.
Definition hash.hpp:787
const T * Find(int p1, int p2) const
Item const accessor with key (or parents) the pair p1, p2. Return NULL if no value corresponds to the...
Definition hash.hpp:692
void UpdateUnused()
Reinitialize the internal list of unallocated items.
Definition hash.hpp:843
const_iterator cbegin() const
Definition hash.hpp:346
int NumFreeIds() const
Return the number of free/unused ids in the HashTable.
Definition hash.hpp:233
void PrintStats() const
Print a histogram of bin sizes for debugging purposes.
Definition hash.hpp:912
HashTable & operator=(const HashTable &)=delete
Copy assignment not supported.
std::size_t MemoryUsage() const
Return total size of allocated memory (tables plus items), in bytes.
Definition hash.hpp:885
real_t b
Definition lissajous.cpp:42
real_t a
Definition lissajous.cpp:41
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
Definition globals.hpp:66
float real_t
Definition config.hpp:46