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