MFEM  v4.1.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  void PrintMemoryDetail() const;
127 
128  class iterator : public Base::iterator
129  {
130  protected:
131  friend class HashTable;
132  typedef typename Base::iterator base;
133 
134  iterator() { }
135  iterator(const base &it) : base(it)
136  {
137  while (base::good() && (*this)->next == -2) { base::next(); }
138  }
139 
140  public:
142  {
143  while (base::next(), base::good() && (*this)->next == -2) { }
144  return *this;
145  }
146  };
147 
149  {
150  protected:
151  friend class HashTable;
152  typedef typename Base::const_iterator base;
153 
155  const_iterator(const base &it) : base(it)
156  {
157  while (base::good() && (*this)->next == -2) { base::next(); }
158  }
159 
160  public:
162  {
163  while (base::next(), base::good() && (*this)->next == -2) { }
164  return *this;
165  }
166  };
167 
168  iterator begin() { return iterator(Base::begin()); }
169  iterator end() { return iterator(); }
170 
171  const_iterator cbegin() const { return const_iterator(Base::cbegin()); }
172  const_iterator cend() const { return const_iterator(); }
173 
174 protected:
175  int* table;
176  int mask;
178 
179  // hash functions (NOTE: the constants are arbitrary)
180  inline int Hash(int p1, int p2) const
181  { return (984120265*p1 + 125965121*p2) & mask; }
182 
183  inline int Hash(int p1, int p2, int p3) const
184  { return (984120265*p1 + 125965121*p2 + 495698413*p3) & mask; }
185 
186  // Delete() and Reparent() use one of these:
187  inline int Hash(const Hashed2& item) const
188  { return Hash(item.p1, item.p2); }
189 
190  inline int Hash(const Hashed4& item) const
191  { return Hash(item.p1, item.p2, item.p3); }
192 
193  int SearchList(int id, int p1, int p2) const;
194  int SearchList(int id, int p1, int p2, int p3) const;
195 
196  inline void Insert(int idx, int id, T &item);
197  void Unlink(int idx, int id);
198 
199  /// Check table load factor and resize if necessary
200  inline void CheckRehash();
201  void DoRehash();
202 };
203 
204 
205 // implementation
206 
207 template<typename T>
208 HashTable<T>::HashTable(int block_size, int init_hash_size)
209  : Base(block_size)
210 {
211  mask = init_hash_size-1;
212  MFEM_VERIFY(!(init_hash_size & mask), "init_size must be a power of two.");
213 
214  table = new int[init_hash_size];
215  for (int i = 0; i < init_hash_size; i++) { table[i] = -1; }
216 }
217 
218 template<typename T>
220  : Base(other), mask(other.mask)
221 {
222  int size = mask+1;
223  table = new int[size];
224  memcpy(table, other.table, size*sizeof(int));
225  other.unused.Copy(unused);
226 }
227 
228 template<typename T>
230 {
231  delete [] table;
232 }
233 
234 namespace internal
235 {
236 
237 inline void sort3(int &a, int &b, int &c)
238 {
239  if (a > b) { std::swap(a, b); }
240  if (a > c) { std::swap(a, c); }
241  if (b > c) { std::swap(b, c); }
242 }
243 
244 inline void sort4(int &a, int &b, int &c, int &d)
245 {
246  if (a > b) { std::swap(a, b); }
247  if (a > c) { std::swap(a, c); }
248  if (a > d) { std::swap(a, d); }
249  sort3(b, c, d);
250 }
251 
252 inline void sort4_ext(int &a, int &b, int &c, int &d)
253 {
254  if (d < 0) // support optional last index
255  {
256  sort3(a, b, c);
257  }
258  else
259  {
260  sort4(a, b, c, d);
261  }
262 }
263 
264 } // internal
265 
266 template<typename T>
267 inline T* HashTable<T>::Get(int p1, int p2)
268 {
269  return &(Base::At(GetId(p1, p2)));
270 }
271 
272 template<typename T>
273 inline T* HashTable<T>::Get(int p1, int p2, int p3, int p4)
274 {
275  return &(Base::At(GetId(p1, p2, p3, p4)));
276 }
277 
278 template<typename T>
279 int HashTable<T>::GetId(int p1, int p2)
280 {
281  // search for the item in the hashtable
282  if (p1 > p2) { std::swap(p1, p2); }
283  int idx = Hash(p1, p2);
284  int id = SearchList(table[idx], p1, p2);
285  if (id >= 0) { return id; }
286 
287  // not found - use an unused item or create a new one
288  int new_id;
289  if (unused.Size())
290  {
291  new_id = unused.Last();
292  unused.DeleteLast();
293  }
294  else
295  {
296  new_id = Base::Append();
297  }
298  T& item = Base::At(new_id);
299  item.p1 = p1;
300  item.p2 = p2;
301 
302  // insert into hashtable
303  Insert(idx, new_id, item);
304  CheckRehash();
305 
306  return new_id;
307 }
308 
309 template<typename T>
310 int HashTable<T>::GetId(int p1, int p2, int p3, int p4)
311 {
312  // search for the item in the hashtable
313  internal::sort4_ext(p1, p2, p3, p4);
314  int idx = Hash(p1, p2, p3);
315  int id = SearchList(table[idx], p1, p2, p3);
316  if (id >= 0) { return id; }
317 
318  // not found - use an unused item or create a new one
319  int new_id;
320  if (unused.Size())
321  {
322  new_id = unused.Last();
323  unused.DeleteLast();
324  }
325  else
326  {
327  new_id = Base::Append();
328  }
329  T& item = Base::At(new_id);
330  item.p1 = p1;
331  item.p2 = p2;
332  item.p3 = p3;
333 
334  // insert into hashtable
335  Insert(idx, new_id, item);
336  CheckRehash();
337 
338  return new_id;
339 }
340 
341 template<typename T>
342 inline T* HashTable<T>::Find(int p1, int p2)
343 {
344  int id = FindId(p1, p2);
345  return (id >= 0) ? &(Base::At(id)) : NULL;
346 }
347 
348 template<typename T>
349 inline T* HashTable<T>::Find(int p1, int p2, int p3, int p4)
350 {
351  int id = FindId(p1, p2, p3, p4);
352  return (id >= 0) ? &(Base::At(id)) : NULL;
353 }
354 
355 template<typename T>
356 inline const T* HashTable<T>::Find(int p1, int p2) const
357 {
358  int id = FindId(p1, p2);
359  return (id >= 0) ? &(Base::At(id)) : NULL;
360 }
361 
362 template<typename T>
363 inline const T* HashTable<T>::Find(int p1, int p2, int p3, int p4) const
364 {
365  int id = FindId(p1, p2, p3, p4);
366  return (id >= 0) ? &(Base::At(id)) : NULL;
367 }
368 
369 template<typename T>
370 int HashTable<T>::FindId(int p1, int p2) const
371 {
372  if (p1 > p2) { std::swap(p1, p2); }
373  return SearchList(table[Hash(p1, p2)], p1, p2);
374 }
375 
376 template<typename T>
377 int HashTable<T>::FindId(int p1, int p2, int p3, int p4) const
378 {
379  internal::sort4_ext(p1, p2, p3, p4);
380  return SearchList(table[Hash(p1, p2, p3)], p1, p2, p3);
381 }
382 
383 template<typename T>
384 int HashTable<T>::SearchList(int id, int p1, int p2) const
385 {
386  while (id >= 0)
387  {
388  const T& item = Base::At(id);
389  if (item.p1 == p1 && item.p2 == p2) { return id; }
390  id = item.next;
391  }
392  return -1;
393 }
394 
395 template<typename T>
396 int HashTable<T>::SearchList(int id, int p1, int p2, int p3) const
397 {
398  while (id >= 0)
399  {
400  const T& item = Base::At(id);
401  if (item.p1 == p1 && item.p2 == p2 && item.p3 == p3) { return id; }
402  id = item.next;
403  }
404  return -1;
405 }
406 
407 template<typename T>
409 {
410  const int fill_factor = 2;
411 
412  // is the table overfull?
413  if (Base::Size() > (mask+1) * fill_factor)
414  {
415  DoRehash();
416  }
417 }
418 
419 template<typename T>
421 {
422  delete [] table;
423 
424  // double the table size
425  int new_table_size = 2*(mask+1);
426  table = new int[new_table_size];
427  for (int i = 0; i < new_table_size; i++) { table[i] = -1; }
428  mask = new_table_size-1;
429 
430 #if defined(MFEM_DEBUG) && !defined(MFEM_USE_MPI)
431  mfem::out << _MFEM_FUNC_NAME << ": rehashing to size " << new_table_size
432  << std::endl;
433 #endif
434 
435  // reinsert all items
436  for (iterator it = begin(); it != end(); ++it)
437  {
438  Insert(Hash(*it), it.index(), *it);
439  }
440 }
441 
442 template<typename T>
443 inline void HashTable<T>::Insert(int idx, int id, T &item)
444 {
445  // add item at the beginning of the linked list
446  item.next = table[idx];
447  table[idx] = id;
448 }
449 
450 template<typename T>
451 void HashTable<T>::Unlink(int idx, int id)
452 {
453  // remove item from the linked list
454  int* p_id = table + idx;
455  while (*p_id >= 0)
456  {
457  T& item = Base::At(*p_id);
458  if (*p_id == id)
459  {
460  *p_id = item.next;
461  return;
462  }
463  p_id = &(item.next);
464  }
465  MFEM_ABORT("HashTable<>::Unlink: item not found!");
466 }
467 
468 template<typename T>
470 {
471  T& item = Base::At(id);
472  Unlink(Hash(item), id);
473  item.next = -2; // mark item as unused
474  unused.Append(id); // add its id to the unused ids
475 }
476 
477 template<typename T>
479 {
480  Base::DeleteAll();
481  for (int i = 0; i <= mask; i++) { table[i] = -1; }
482  unused.DeleteAll();
483 }
484 
485 template<typename T>
486 void HashTable<T>::Reparent(int id, int new_p1, int new_p2)
487 {
488  T& item = Base::At(id);
489  Unlink(Hash(item), id);
490 
491  if (new_p1 > new_p2) { std::swap(new_p1, new_p2); }
492  item.p1 = new_p1;
493  item.p2 = new_p2;
494 
495  // reinsert under new parent IDs
496  int new_idx = Hash(new_p1, new_p2);
497  Insert(new_idx, id, item);
498 }
499 
500 template<typename T>
502  int new_p1, int new_p2, int new_p3, int new_p4)
503 {
504  T& item = Base::At(id);
505  Unlink(Hash(item), id);
506 
507  internal::sort4_ext(new_p1, new_p2, new_p3, new_p4);
508  item.p1 = new_p1;
509  item.p2 = new_p2;
510  item.p3 = new_p3;
511 
512  // reinsert under new parent IDs
513  int new_idx = Hash(new_p1, new_p2, new_p3);
514  Insert(new_idx, id, item);
515 }
516 
517 template<typename T>
519 {
520  return (mask+1) * sizeof(int) + Base::MemoryUsage() + unused.MemoryUsage();
521 }
522 
523 template<typename T>
525 {
526  mfem::out << Base::MemoryUsage() << " + " << (mask+1) * sizeof(int)
527  << " + " << unused.MemoryUsage();
528 }
529 
530 } // namespace mfem
531 
532 #endif
Array< int > unused
Definition: hash.hpp:177
int Size() const
Logical size of the array.
Definition: array.hpp:124
int Hash(int p1, int p2, int p3) const
Definition: hash.hpp:183
int Hash(const Hashed4 &item) const
Definition: hash.hpp:190
const_iterator cbegin() const
Definition: array.hpp:561
void Unlink(int idx, int id)
Definition: hash.hpp:451
void Delete(int id)
Remove an item from the hash table.
Definition: hash.hpp:469
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:208
int Size() const
Return the number of items actually stored.
Definition: array.hpp:464
int SearchList(int id, int p1, int p2) const
Definition: hash.hpp:384
void Copy(Array &copy) const
Create a copy of the current array.
Definition: array.hpp:812
iterator & operator++()
Definition: hash.hpp:141
T * Get(int p1, int p2)
Get item whose parents are p1, p2... Create it if it doesn&#39;t exist.
Definition: hash.hpp:267
const_iterator(const base &it)
Definition: hash.hpp:155
void PrintMemoryDetail() const
Definition: hash.hpp:524
int Hash(int p1, int p2) const
Definition: hash.hpp:180
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:478
void CheckRehash()
Check table load factor and resize if necessary.
Definition: hash.hpp:408
iterator begin()
Definition: array.hpp:558
const_iterator & operator++()
Definition: hash.hpp:161
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:486
T * Find(int p1, int p2)
Find item whose parents are p1, p2... Return NULL if it doesn&#39;t exist.
Definition: hash.hpp:342
iterator(const base &it)
Definition: hash.hpp:135
iterator end()
Definition: hash.hpp:169
int * table
Definition: hash.hpp:175
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:187
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:168
double a
Definition: lissajous.cpp:41
long MemoryUsage() const
Return total size of allocated memory (tables plus items), in bytes.
Definition: hash.hpp:518
const_iterator cbegin() const
Definition: hash.hpp:171
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:279
Base::iterator base
Definition: hash.hpp:132
Base::const_iterator base
Definition: hash.hpp:152
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:370
T & At(int index)
Access item of the array.
Definition: array.hpp:448
int next
Definition: hash.hpp:28
void DoRehash()
Definition: hash.hpp:420
void Insert(int idx, int id, T &item)
Definition: hash.hpp:443
const_iterator cend() const
Definition: hash.hpp:172