MFEM  v4.2.0
Finite element discretization library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
hash.hpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2020, 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 
19 namespace mfem
20 {
21 
22 /** A concept for items that should be used in HashTable and be accessible by
23  * hashing two IDs.
24  */
25 struct Hashed2
26 {
27  int p1, p2;
28  int next;
29 };
30 
31 /** A concept for items that should be used in HashTable and be accessible by
32  * hashing four IDs.
33  */
34 struct Hashed4
35 {
36  int p1, p2, p3; // NOTE: p4 is neither hashed nor stored
37  int next;
38 };
39 
40 
41 /** HashTable is a container for items that require associative access through
42  * pairs (or quadruples) of indices:
43  *
44  * (p1, p2) -> item
45  * (p1, p2, p3, p4) -> item
46  *
47  * An example of this are edges and faces in a mesh. Each edge is uniquely
48  * identified by two parent vertices and so can be easily accessed from
49  * different elements using this class. Similarly for faces.
50  *
51  * The order of the p1, p2, ... indices is not relevant as they are sorted
52  * each time this class is invoked.
53  *
54  * There are two main methods this class provides. The Get(...) methods always
55  * return an item given the two or four indices. If the item didn't previously
56  * exist, the methods creates a new one. The Find(...) methods, on the other
57  * hand, just return NULL or -1 if the item doesn't exist.
58  *
59  * Each new item is automatically assigned a unique ID - the index of the item
60  * inside the BlockArray. The IDs may (but need not) be used as p1, p2, ... of
61  * other items.
62  *
63  * The item type (T) needs to follow either the Hashed2 or the Hashed4
64  * concept. It is easiest to just inherit from these structs.
65  *
66  * All items in the container can also be accessed sequentially using the
67  * provided iterator.
68  */
69 template<typename T>
70 class HashTable : public BlockArray<T>
71 {
72 protected:
74 
75 public:
76  HashTable(int block_size = 16*1024, int init_hash_size = 32*1024);
77  HashTable(const HashTable& other); // deep copy
78  ~HashTable();
79 
80  /// Get item whose parents are 'p1', 'p2'... Create it if it doesn't exist.
81  T* Get(int p1, int p2);
82  T* Get(int p1, int p2, int p3, int p4 = -1 /* p4 optional */);
83 
84  /// Get id of item whose parents are p1, p2... Create it if it doesn't exist.
85  int GetId(int p1, int p2);
86  int GetId(int p1, int p2, int p3, int p4 = -1);
87 
88  /// Find item whose parents are p1, p2... Return NULL if it doesn't exist.
89  T* Find(int p1, int p2);
90  T* Find(int p1, int p2, int p3, int p4 = -1);
91 
92  const T* Find(int p1, int p2) const;
93  const T* Find(int p1, int p2, int p3, int p4 = -1) const;
94 
95  /// Find id of item whose parents are p1, p2... Return -1 if it doesn't exist.
96  int FindId(int p1, int p2) const;
97  int FindId(int p1, int p2, int p3, int p4 = -1) const;
98 
99  /// Return the number of elements currently stored in the HashTable.
100  int Size() const { return Base::Size() - unused.Size(); }
101 
102  /// Return the total number of ids (used and unused) in the HashTable.
103  int NumIds() const { return Base::Size(); }
104 
105  /// Return the number of free/unused ids in the HashTable.
106  int NumFreeIds() const { return unused.Size(); }
107 
108  /// Return true if item 'id' exists in (is used by) the container.
109  /** It is assumed that 0 <= id < NumIds(). */
110  bool IdExists(int id) const { return (Base::At(id).next != -2); }
111 
112  /// Remove an item from the hash table.
113  /** Its id will be reused by newly added items. */
114  void Delete(int id);
115 
116  /// Remove all items.
117  void DeleteAll();
118 
119  /// Make an item hashed under different parent IDs.
120  void Reparent(int id, int new_p1, int new_p2);
121  void Reparent(int id, int new_p1, int new_p2, int new_p3, int new_p4 = -1);
122 
123  /// Return total size of allocated memory (tables plus items), in bytes.
124  long MemoryUsage() const;
125 
126  /// Write details of the memory usage to the mfem output stream.
127  void PrintMemoryDetail() const;
128 
129  class iterator : public Base::iterator
130  {
131  protected:
132  friend class HashTable;
133  typedef typename Base::iterator base;
134 
135  iterator() { }
136  iterator(const base &it) : base(it)
137  {
138  while (base::good() && (*this)->next == -2) { base::next(); }
139  }
140 
141  public:
143  {
144  while (base::next(), base::good() && (*this)->next == -2) { }
145  return *this;
146  }
147  };
148 
150  {
151  protected:
152  friend class HashTable;
153  typedef typename Base::const_iterator base;
154 
156  const_iterator(const base &it) : base(it)
157  {
158  while (base::good() && (*this)->next == -2) { base::next(); }
159  }
160 
161  public:
163  {
164  while (base::next(), base::good() && (*this)->next == -2) { }
165  return *this;
166  }
167  };
168 
169  iterator begin() { return iterator(Base::begin()); }
170  iterator end() { return iterator(); }
171 
172  const_iterator cbegin() const { return const_iterator(Base::cbegin()); }
173  const_iterator cend() const { return const_iterator(); }
174 
175 protected:
176  int* table;
177  int mask;
179 
180  // hash functions (NOTE: the constants are arbitrary)
181  inline int Hash(int p1, int p2) const
182  { return (984120265*p1 + 125965121*p2) & mask; }
183 
184  inline int Hash(int p1, int p2, int p3) const
185  { return (984120265*p1 + 125965121*p2 + 495698413*p3) & mask; }
186 
187  // Delete() and Reparent() use one of these:
188  inline int Hash(const Hashed2& item) const
189  { return Hash(item.p1, item.p2); }
190 
191  inline int Hash(const Hashed4& item) const
192  { return Hash(item.p1, item.p2, item.p3); }
193 
194  int SearchList(int id, int p1, int p2) const;
195  int SearchList(int id, int p1, int p2, int p3) const;
196 
197  inline void Insert(int idx, int id, T &item);
198  void Unlink(int idx, int id);
199 
200  /// Check table load factor and resize if necessary
201  inline void CheckRehash();
202  void DoRehash();
203 };
204 
205 
206 // implementation
207 
208 template<typename T>
209 HashTable<T>::HashTable(int block_size, int init_hash_size)
210  : Base(block_size)
211 {
212  mask = init_hash_size-1;
213  MFEM_VERIFY(!(init_hash_size & mask), "init_size must be a power of two.");
214 
215  table = new int[init_hash_size];
216  for (int i = 0; i < init_hash_size; i++) { table[i] = -1; }
217 }
218 
219 template<typename T>
221  : Base(other), mask(other.mask)
222 {
223  int size = mask+1;
224  table = new int[size];
225  memcpy(table, other.table, size*sizeof(int));
226  other.unused.Copy(unused);
227 }
228 
229 template<typename T>
231 {
232  delete [] table;
233 }
234 
235 namespace internal
236 {
237 
238 inline void sort3(int &a, int &b, int &c)
239 {
240  if (a > b) { std::swap(a, b); }
241  if (a > c) { std::swap(a, c); }
242  if (b > c) { std::swap(b, c); }
243 }
244 
245 inline void sort4(int &a, int &b, int &c, int &d)
246 {
247  if (a > b) { std::swap(a, b); }
248  if (a > c) { std::swap(a, c); }
249  if (a > d) { std::swap(a, d); }
250  sort3(b, c, d);
251 }
252 
253 inline void sort4_ext(int &a, int &b, int &c, int &d)
254 {
255  if (d < 0) // support optional last index
256  {
257  sort3(a, b, c);
258  }
259  else
260  {
261  sort4(a, b, c, d);
262  }
263 }
264 
265 } // internal
266 
267 template<typename T>
268 inline T* HashTable<T>::Get(int p1, int p2)
269 {
270  return &(Base::At(GetId(p1, p2)));
271 }
272 
273 template<typename T>
274 inline T* HashTable<T>::Get(int p1, int p2, int p3, int p4)
275 {
276  return &(Base::At(GetId(p1, p2, p3, p4)));
277 }
278 
279 template<typename T>
280 int HashTable<T>::GetId(int p1, int p2)
281 {
282  // search for the item in the hashtable
283  if (p1 > p2) { std::swap(p1, p2); }
284  int idx = Hash(p1, p2);
285  int id = SearchList(table[idx], p1, p2);
286  if (id >= 0) { return id; }
287 
288  // not found - use an unused item or create a new one
289  int new_id;
290  if (unused.Size())
291  {
292  new_id = unused.Last();
293  unused.DeleteLast();
294  }
295  else
296  {
297  new_id = Base::Append();
298  }
299  T& item = Base::At(new_id);
300  item.p1 = p1;
301  item.p2 = p2;
302 
303  // insert into hashtable
304  Insert(idx, new_id, item);
305  CheckRehash();
306 
307  return new_id;
308 }
309 
310 template<typename T>
311 int HashTable<T>::GetId(int p1, int p2, int p3, int p4)
312 {
313  // search for the item in the hashtable
314  internal::sort4_ext(p1, p2, p3, p4);
315  int idx = Hash(p1, p2, p3);
316  int id = SearchList(table[idx], p1, p2, p3);
317  if (id >= 0) { return id; }
318 
319  // not found - use an unused item or create a new one
320  int new_id;
321  if (unused.Size())
322  {
323  new_id = unused.Last();
324  unused.DeleteLast();
325  }
326  else
327  {
328  new_id = Base::Append();
329  }
330  T& item = Base::At(new_id);
331  item.p1 = p1;
332  item.p2 = p2;
333  item.p3 = p3;
334 
335  // insert into hashtable
336  Insert(idx, new_id, item);
337  CheckRehash();
338 
339  return new_id;
340 }
341 
342 template<typename T>
343 inline T* HashTable<T>::Find(int p1, int p2)
344 {
345  int id = FindId(p1, p2);
346  return (id >= 0) ? &(Base::At(id)) : NULL;
347 }
348 
349 template<typename T>
350 inline T* HashTable<T>::Find(int p1, int p2, int p3, int p4)
351 {
352  int id = FindId(p1, p2, p3, p4);
353  return (id >= 0) ? &(Base::At(id)) : NULL;
354 }
355 
356 template<typename T>
357 inline const T* HashTable<T>::Find(int p1, int p2) const
358 {
359  int id = FindId(p1, p2);
360  return (id >= 0) ? &(Base::At(id)) : NULL;
361 }
362 
363 template<typename T>
364 inline const T* HashTable<T>::Find(int p1, int p2, int p3, int p4) const
365 {
366  int id = FindId(p1, p2, p3, p4);
367  return (id >= 0) ? &(Base::At(id)) : NULL;
368 }
369 
370 template<typename T>
371 int HashTable<T>::FindId(int p1, int p2) const
372 {
373  if (p1 > p2) { std::swap(p1, p2); }
374  return SearchList(table[Hash(p1, p2)], p1, p2);
375 }
376 
377 template<typename T>
378 int HashTable<T>::FindId(int p1, int p2, int p3, int p4) const
379 {
380  internal::sort4_ext(p1, p2, p3, p4);
381  return SearchList(table[Hash(p1, p2, p3)], p1, p2, p3);
382 }
383 
384 template<typename T>
385 int HashTable<T>::SearchList(int id, int p1, int p2) const
386 {
387  while (id >= 0)
388  {
389  const T& item = Base::At(id);
390  if (item.p1 == p1 && item.p2 == p2) { return id; }
391  id = item.next;
392  }
393  return -1;
394 }
395 
396 template<typename T>
397 int HashTable<T>::SearchList(int id, int p1, int p2, int p3) const
398 {
399  while (id >= 0)
400  {
401  const T& item = Base::At(id);
402  if (item.p1 == p1 && item.p2 == p2 && item.p3 == p3) { return id; }
403  id = item.next;
404  }
405  return -1;
406 }
407 
408 template<typename T>
410 {
411  const int fill_factor = 2;
412 
413  // is the table overfull?
414  if (Base::Size() > (mask+1) * fill_factor)
415  {
416  DoRehash();
417  }
418 }
419 
420 template<typename T>
422 {
423  delete [] table;
424 
425  // double the table size
426  int new_table_size = 2*(mask+1);
427  table = new int[new_table_size];
428  for (int i = 0; i < new_table_size; i++) { table[i] = -1; }
429  mask = new_table_size-1;
430 
431 #if defined(MFEM_DEBUG) && !defined(MFEM_USE_MPI)
432  mfem::out << _MFEM_FUNC_NAME << ": rehashing to size " << new_table_size
433  << std::endl;
434 #endif
435 
436  // reinsert all items
437  for (iterator it = begin(); it != end(); ++it)
438  {
439  Insert(Hash(*it), it.index(), *it);
440  }
441 }
442 
443 template<typename T>
444 inline void HashTable<T>::Insert(int idx, int id, T &item)
445 {
446  // add item at the beginning of the linked list
447  item.next = table[idx];
448  table[idx] = id;
449 }
450 
451 template<typename T>
452 void HashTable<T>::Unlink(int idx, int id)
453 {
454  // remove item from the linked list
455  int* p_id = table + idx;
456  while (*p_id >= 0)
457  {
458  T& item = Base::At(*p_id);
459  if (*p_id == id)
460  {
461  *p_id = item.next;
462  return;
463  }
464  p_id = &(item.next);
465  }
466  MFEM_ABORT("HashTable<>::Unlink: item not found!");
467 }
468 
469 template<typename T>
471 {
472  T& item = Base::At(id);
473  Unlink(Hash(item), id);
474  item.next = -2; // mark item as unused
475  unused.Append(id); // add its id to the unused ids
476 }
477 
478 template<typename T>
480 {
481  Base::DeleteAll();
482  for (int i = 0; i <= mask; i++) { table[i] = -1; }
483  unused.DeleteAll();
484 }
485 
486 template<typename T>
487 void HashTable<T>::Reparent(int id, int new_p1, int new_p2)
488 {
489  T& item = Base::At(id);
490  Unlink(Hash(item), id);
491 
492  if (new_p1 > new_p2) { std::swap(new_p1, new_p2); }
493  item.p1 = new_p1;
494  item.p2 = new_p2;
495 
496  // reinsert under new parent IDs
497  int new_idx = Hash(new_p1, new_p2);
498  Insert(new_idx, id, item);
499 }
500 
501 template<typename T>
503  int new_p1, int new_p2, int new_p3, int new_p4)
504 {
505  T& item = Base::At(id);
506  Unlink(Hash(item), id);
507 
508  internal::sort4_ext(new_p1, new_p2, new_p3, new_p4);
509  item.p1 = new_p1;
510  item.p2 = new_p2;
511  item.p3 = new_p3;
512 
513  // reinsert under new parent IDs
514  int new_idx = Hash(new_p1, new_p2, new_p3);
515  Insert(new_idx, id, item);
516 }
517 
518 template<typename T>
520 {
521  return (mask+1) * sizeof(int) + Base::MemoryUsage() + unused.MemoryUsage();
522 }
523 
524 template<typename T>
526 {
527  mfem::out << Base::MemoryUsage() << " + " << (mask+1) * sizeof(int)
528  << " + " << unused.MemoryUsage();
529 }
530 
531 } // namespace mfem
532 
533 #endif
Array< int > unused
Definition: hash.hpp:178
int Size() const
Return the logical size of the array.
Definition: array.hpp:124
int Hash(int p1, int p2, int p3) const
Definition: hash.hpp:184
int Hash(const Hashed4 &item) const
Definition: hash.hpp:191
const_iterator cbegin() const
Definition: array.hpp:580
void Unlink(int idx, int id)
Definition: hash.hpp:452
void Delete(int id)
Remove an item from the hash table.
Definition: hash.hpp:470
bool IdExists(int id) const
Return true if item &#39;id&#39; exists in (is used by) the container.
Definition: hash.hpp:110
HashTable(int block_size=16 *1024, int init_hash_size=32 *1024)
Definition: hash.hpp:209
int Size() const
Return the number of items actually stored.
Definition: array.hpp:483
int SearchList(int id, int p1, int p2) const
Definition: hash.hpp:385
void Copy(Array &copy) const
Create a copy of the internal array to the provided copy.
Definition: array.hpp:831
iterator & operator++()
Definition: hash.hpp:142
T * Get(int p1, int p2)
Get item whose parents are &#39;p1&#39;, &#39;p2&#39;... Create it if it doesn&#39;t exist.
Definition: hash.hpp:268
const_iterator(const base &it)
Definition: hash.hpp:156
void PrintMemoryDetail() const
Write details of the memory usage to the mfem output stream.
Definition: hash.hpp:525
int Hash(int p1, int p2) const
Definition: hash.hpp:181
int Size() const
Return the number of elements currently stored in the HashTable.
Definition: hash.hpp:100
void DeleteAll()
Remove all items.
Definition: hash.hpp:479
void CheckRehash()
Check table load factor and resize if necessary.
Definition: hash.hpp:409
iterator begin()
Definition: array.hpp:577
const_iterator & operator++()
Definition: hash.hpp:162
double b
Definition: lissajous.cpp:42
void Reparent(int id, int new_p1, int new_p2)
Make an item hashed under different parent IDs.
Definition: hash.hpp:487
T * Find(int p1, int p2)
Find item whose parents are p1, p2... Return NULL if it doesn&#39;t exist.
Definition: hash.hpp:343
iterator(const base &it)
Definition: hash.hpp:136
iterator end()
Definition: hash.hpp:170
int * table
Definition: hash.hpp:176
int NumIds() const
Return the total number of ids (used and unused) in the HashTable.
Definition: hash.hpp:103
int Hash(const Hashed2 &item) const
Definition: hash.hpp:188
BlockArray< T > Base
Definition: hash.hpp:73
int NumFreeIds() const
Return the number of free/unused ids in the HashTable.
Definition: hash.hpp:106
int next
Definition: hash.hpp:37
iterator begin()
Definition: hash.hpp:169
double a
Definition: lissajous.cpp:41
long MemoryUsage() const
Return total size of allocated memory (tables plus items), in bytes.
Definition: hash.hpp:519
const_iterator cbegin() const
Definition: hash.hpp:172
int GetId(int p1, int p2)
Get id of item whose parents are p1, p2... Create it if it doesn&#39;t exist.
Definition: hash.hpp:280
Base::iterator base
Definition: hash.hpp:133
Base::const_iterator base
Definition: hash.hpp:153
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
int FindId(int p1, int p2) const
Find id of item whose parents are p1, p2... Return -1 if it doesn&#39;t exist.
Definition: hash.hpp:371
T & At(int index)
Access item of the array.
Definition: array.hpp:467
int next
Definition: hash.hpp:28
void DoRehash()
Definition: hash.hpp:421
void Insert(int idx, int id, T &item)
Definition: hash.hpp:444
const_iterator cend() const
Definition: hash.hpp:173