Aleph-w  1.9
General library for algorithms and data structures
tpl_lhash.H
1 
2 /* Aleph-w
3 
4  / \ | | ___ _ __ | |__ __ __
5  / _ \ | |/ _ \ '_ \| '_ \ ____\ \ /\ / / Data structures & Algorithms
6  / ___ \| | __/ |_) | | | |_____\ V V / version 1.9b
7  /_/ \_\_|\___| .__/|_| |_| \_/\_/ https://github.com/lrleon/Aleph-w
8  |_|
9 
10  This file is part of Aleph-w library
11 
12  Copyright (c) 2002-2018 Leandro Rabindranath Leon & Alejandro Mujica
13 
14  This program is free software: you can redistribute it and/or modify
15  it under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  This program is distributed in the hope that it will be useful, but
20  WITHOUT ANY WARRANTY; without even the implied warranty of
21  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22  General Public License for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with this program. If not, see <https://www.gnu.org/licenses/>.
26 */
27 
28 # ifndef TPL_LHASH_H
29 # define TPL_LHASH_H
30 
31 # include <iostream>
32 # include <primes.H>
33 # include <dlink.H>
34 # include <htlist.H>
35 # include <tpl_dynArray.H>
36 # include <hashDry.H>
37 # include <hash-fct.H>
38 # include <tpl_dynArray.H>
39 # include <hash-dry.H>
40 
41 # ifdef N
42 # define NBACKUP N
43 # undef N
44 # endif
45 
46 # ifdef M
47 # define MBACKUP M
48 # undef M
49 # endif
50 
51 using namespace Primes;
52 
53 using namespace Aleph;
54 
55 namespace Aleph {
56 
76  template <typename Key, class BucketType, class Cmp>
77 class GenLhashTable : public HashStats<GenLhashTable<Key, BucketType, Cmp>>
78 {
79  friend class HashStats<GenLhashTable<Key, BucketType, Cmp>>;
80 
81 public:
82 
83  using Bucket = BucketType;
84 
85  using Hash_Fct = std::function<size_t(const Key &)>;
86 
87  using Hash_Fct_Ptr = size_t (*) (const Key &);
88 
89  using Key_Type = Key;
90 
91  using Item_Type = Key;
92 
93 protected:
94 
95  Hash_Fct hash_fct;
96 
97 private:
98 
99  using BucketList = Dnode<Key>;// Tipo lista cubetas
100  using BucketItor = typename Dnode<Key>::Iterator; // Iterador cubetas
101  using Node = Dnode<Key>; // Sinónimo nodo
102 
103  BucketList * table;
104 
105 protected:
106 
107  size_t len; // Tamaño de la tabla
108  Cmp cmp;
109  float lower_alpha;
110  float upper_alpha;
111 
112 private:
113 
114  size_t N; // Número de elementos de la tabla
115  size_t busy_slots_counter; // Cantidad de entradas ocupadas
116  bool remove_all_buckets; // liberar cubetas en destructor
117  bool with_resize;
118 
119 public:
120 
121  Cmp & get_compare() { return cmp; }
122 
123  const Cmp & get_compare() const { return cmp; }
124 
125  protected:
126 
127  GenLhashTable(size_t table_size, Hash_Fct __hash_fct, Cmp __cmp,
128  float __lower_alpha, float __upper_alpha,
129  bool __remove_all_buckets, bool __with_resize)
130  : hash_fct(__hash_fct), table(nullptr),
131  len(Primes::next_prime(table_size)), cmp(__cmp),
132  lower_alpha(__lower_alpha), upper_alpha(__upper_alpha),
133  N(0), busy_slots_counter(0), remove_all_buckets(__remove_all_buckets),
134  with_resize(__with_resize)
135  {
136  table = new BucketList [len];
137  }
138 
139  public:
140 
149  GenLhashTable(size_t table_size = Primes::DefaultPrime,
150  Hash_Fct_Ptr hash_fct = Aleph::dft_hash_fct<Key>,
151  Cmp cmp = Cmp(),
152  float lower_alpha = hash_default_lower_alpha,
153  float upper_alpha = hash_default_upper_alpha,
154  bool remove_all_buckets = true,
155  bool with_resize = true)
156  : GenLhashTable(table_size, Hash_Fct(hash_fct), cmp,
157  lower_alpha, upper_alpha, remove_all_buckets, with_resize)
158  {}
159 
160  void swap(GenLhashTable & other) noexcept
161  {
162  std::swap(hash_fct, other.hash_fct);
163  std::swap(table, other.table);
164  std::swap(len, other.len);
165  std::swap(cmp, other.cmp);
166  std::swap(N, other.N);
167  std::swap(busy_slots_counter, other.busy_slots_counter);
168  std::swap(remove_all_buckets, other.remove_all_buckets);
169  std::swap(with_resize, other.with_resize);
170  }
171 
173  void empty() noexcept
174  {
175  for (int i = 0; i < len; ++i)
176  for (BucketItor itor(table[i]); itor.has_curr(); /* nada */)
177  delete (Bucket*) itor.del_ne();
178  busy_slots_counter = N = 0;
179  }
180 
181 private:
182 
183  Bucket *
184  search_in_bucket_list(BucketList & list, const Key & key) const noexcept
185  {
186  for (BucketItor it(list); it.has_curr(); it.next_ne())
187  {
188  Bucket * bucket = static_cast<Bucket*>(it.get_curr_ne());
189  if (cmp (key, bucket->get_key()))
190  return bucket;
191  }
192 
193  return nullptr;
194  }
195 
196 public:
197 
198  Hash_Fct get_hash_fct() const noexcept { return hash_fct; }
199 
201  void set_hash_fct(Hash_Fct fct) noexcept
202  {
203  hash_fct = fct;
204  }
205 
206  void set_hash_fct(Hash_Fct_Ptr fct) noexcept
207  {
208  hash_fct = Hash_Fct(fct);
209  }
210 
212  float current_alpha() const noexcept { return 1.0*N/len; }
213 
216  Bucket * insert(Bucket * bucket)
217  {
218  const auto i = hash_fct(bucket->get_key()) % len;
219  auto & list = table[i];
220 
221  if (list.is_empty()) // ¿está vacía la lista table[i]?
222  ++busy_slots_counter; // sí ==> actualizar contador ocupación
223 
224  if (search_in_bucket_list(list, bucket->get_key()) != nullptr)
225  return nullptr;
226 
227  list.append(bucket); // insertar cubeta al final
228  ++N;
229 
230  if (with_resize and current_alpha() >= upper_alpha)
231  resize(next_prime(2*len));
232 
233  return bucket;
234  }
235 
236  // Busca bucket->key. Si la encuentra, retorna la dirección de bucket
237  // dentro de la tabla. De lo contrario inserta y retorna bucket
238  Bucket * search_or_insert(Bucket * bucket)
239  {
240  const auto i = hash_fct(bucket->get_key()) % len;
241  auto & list = table[i];
242 
243  if (list.is_empty()) // ¿está vacía la lista table[i]?
244  ++busy_slots_counter; // sí ==> actualizar contador ocupación
245 
246  auto * p = search_in_bucket_list(list, bucket->get_key());
247  if (p != nullptr)
248  return p;
249 
250  list.append(bucket); // insertar cubeta al final
251  ++N;
252 
253  if (with_resize and current_alpha() >= upper_alpha)
254  resize(next_prime(2*len));
255 
256  return bucket;
257  }
258 
261  Bucket * search(const Key & key) const noexcept
262  {
263  const auto i = hash_fct(key) % len;
264  return search_in_bucket_list(table[i], key);
265  }
266 
267 private:
268 
269  // Remove without perform resizing. This is strictly required inside
270  // the del() method of Iterator. In addtion dries the code
271  Bucket * remove_bucket(Bucket * bucket) noexcept
272  { // guarda próxima colisión
273  Bucket * next = static_cast<Bucket*>(bucket->get_next());
274  bucket->del(); // eliminar de su lista de colisiones
275  if (next->is_empty()) // ¿la lista devino vacía?
276  --busy_slots_counter;// sí ==> actualizar contador listas ocupadas
277 
278  --N;
279 
280  return bucket;
281  }
282 
283 public:
284 
288  Bucket * remove(Bucket * bucket) noexcept
289  { // guarda próxima colisión
290  remove_bucket(bucket);
291 
292  if (with_resize and current_alpha() < lower_alpha)
293  resize(next_prime(len/2));
294 
295  return bucket;
296  }
297 
301  size_t resize(size_t new_size)
302  {
303  assert(len > 0);
304 
305  if (new_size == len or new_size == 0)
306  return len;
307 
308  BucketList * new_table = new BucketList [new_size];
309  BucketList * old_table = table; // guardar estado tabla actual
310  const size_t old_size = len;
311  table = new_table; // tabla vacía con nuevo arreglo
312  len = new_size;
313  busy_slots_counter = N = 0;
314 
315  for (int i = 0; i < old_size; ++i) // reinsertar cubetas
316  // recorrer lista colisiones en old_table[i]
317  for (BucketItor it(old_table[i]); it.has_curr(); /* Nada */)
318  insert((Bucket*) it.del_ne()); // eliminar e insertar en table[]
319 
320  delete [] old_table; // liberar memoria antigua tabla
321 
322  return len;
323  }
324 
328  virtual ~GenLhashTable()
329  {
330  if (remove_all_buckets)
331  empty();
332 
333  delete [] table;
334  }
335 
338  Bucket * search_next(Bucket * bucket) const noexcept
339  {
340  assert(bucket != nullptr);
341 
342  const auto i = hash_fct(bucket->get_key()) % len;
343 
344  BucketItor itor(table[i]);
345  itor.set(bucket);
346 
347  while (true)
348  {
349  itor.next_ne();
350 
351  if (not itor.has_curr())
352  return nullptr;
353 
354  Bucket * node = static_cast<Bucket*>(itor.get_curr_ne());
355 
356  if (cmp(bucket->get_key(), node->get_key()))
357  return node;
358  }
359  }
360 
362  const size_t & capacity() const noexcept { return len; }
363 
365  const size_t & size() const noexcept { return N; }
366 
368  const size_t &
369  get_num_busy_slots() const noexcept { return busy_slots_counter; }
370 
371  bool is_empty() const noexcept { return N == 0; }
372 
379  class Iterator
380  {
381  private:
382 
383  long curr_index = -1; // índice en table
384  long curr_pos = 0;
385  BucketItor curr_itor; // Iterador sobre table[curr_index]
386  GenLhashTable * hash_table = nullptr;
387 
388  // Avanza a próxima entrada de table
389  void locate_next_available_entry_ne() noexcept
390  {
391  while (true)
392  {
393  if (curr_index == hash_table->len - 1)
394  { // quedar en estado overflow
395  curr_index = hash_table->len;
396  return;
397  }
398 
399  ++curr_index;
400 
401  if (not hash_table->table[curr_index].is_empty())
402  {
403  BucketItor itor(hash_table->table[curr_index]);
404  curr_itor = itor;
405  return;
406  }
407  }
408  }
409 
410  void locate_next_available_entry()
411  {
412  while (true)
413  {
414  if (curr_index == hash_table->len)
415  throw std::overflow_error ("hash table iterator overflow");
416 
417  if (curr_index == hash_table->len - 1)
418  { // quedar en estado overflow
419  curr_index = hash_table->len;
420  return;
421  }
422 
423  ++curr_index;
424 
425  if (not hash_table->table[curr_index].is_empty())
426  {
427  BucketItor itor(hash_table->table[curr_index]);
428  curr_itor = itor;
429  return;
430  }
431  }
432  }
433 
434  void locate_next_available_bucket_ne() noexcept
435  {
436  curr_itor.next_ne();
437  if (not curr_itor.has_curr())
438  locate_next_available_entry_ne();
439  curr_pos++;
440  }
441 
442  void locate_next_available_bucket()
443  {
444  curr_itor.next();
445  if (not curr_itor.has_curr())
446  locate_next_available_entry();
447  curr_pos++;
448  }
449 
450  void locate_prev_available_entry_ne() noexcept
451  {
452  while (true)
453  {
454  if (curr_index == 0)
455  { // quedar en estado underflow
456  curr_index = -1;
457  return;
458  }
459 
460  --curr_index;
461 
462  if (not hash_table->table[curr_index].is_empty())
463  {
464  BucketItor itor(hash_table->table[curr_index]);
465  curr_itor = itor;
466  curr_itor.reset_last();
467  return;
468  }
469  }
470  }
471 
472  void locate_prev_available_entry()
473  {
474  while (true)
475  {
476  if (curr_index == -1)
477  throw std::underflow_error ("hash table iterator underflow");
478 
479  if (curr_index == 0)
480  { // quedar en estado underflow
481  curr_index = -1;
482  return;
483  }
484 
485  --curr_index;
486 
487  if (not hash_table->table[curr_index].is_empty())
488  {
489  BucketItor itor(hash_table->table[curr_index]);
490  curr_itor = itor;
491  curr_itor.reset_last();
492  return;
493  }
494  }
495  }
496 
497  void locate_prev_available_bucket_ne() noexcept
498  {
499  curr_itor.prev_ne();
500  if (not curr_itor.has_curr())
501  locate_prev_available_entry_ne();
502  curr_pos--;
503  }
504 
505  void locate_prev_available_bucket()
506  {
507  curr_itor.prev();
508  if (not curr_itor.has_curr())
509  locate_prev_available_entry();
510  curr_pos--;
511  }
512 
513 public:
514 
516  using Set_Type = GenLhashTable;
517 
519  using Item_Type = Bucket *;
520 
522  Iterator(const GenLhashTable & table) noexcept
523  : hash_table(&const_cast<GenLhashTable&>(table))
524  {
525  try
526  {
527  locate_next_available_entry();
528  }
529  catch (std::overflow_error)
530  { /* nothing to do */ }
531  }
532 
534  Iterator() noexcept { /* Empty */ }
535 
537  void reset_first() noexcept
538  {
539  curr_index = -1;
540  curr_pos = 1;
541  locate_next_available_entry();
542  }
543 
545  void reset_last() noexcept
546  {
547  curr_index = hash_table->len;
548  curr_pos = hash_table->N - 1;
549  locate_prev_available_entry();
550  }
551 
552  void end() noexcept
553  {
554  put_itor_at_the_end(*this);
555  }
556 
558  bool has_curr() const noexcept
559  {
560  return curr_index >= 0 and curr_index < hash_table->len;
561  }
562 
563  Bucket * get_curr_ne() noexcept
564  {
565  return static_cast<Bucket*>(curr_itor.get_curr_ne());
566  }
567 
569  Bucket * get_curr()
570  {
571  if (curr_index == -1)
572  throw std::underflow_error ("hash table iterator underflow");
573 
574  if (curr_index == hash_table->len)
575  throw std::overflow_error ("hash table iterator overflow");
576 
577  return static_cast<Bucket*>(curr_itor.get_curr());
578  }
579 
580  long get_pos() const noexcept { return curr_pos; }
581 
583  void next_ne() noexcept
584  {
585  locate_next_available_bucket_ne();
586  }
587 
588  void next()
589  {
590  if (curr_index == hash_table->len)
591  throw std::overflow_error ("hash table iterator overflow");
592  next_ne();
593  }
594 
596  void prev_ne() noexcept
597  {
598  locate_prev_available_bucket_ne();
599  }
600 
601  void prev()
602  {
603  if (curr_index == -1)
604  throw std::underflow_error ("hash table iterator underflow");
605  locate_prev_available_bucket();
606  }
607 
608  Bucket * del()
609  {
610  Bucket * ret_val = get_curr();
611  next();
612  hash_table->remove_bucket(ret_val);
613  return ret_val;
614  }
615  };
616 };
617 
625 template <typename Key> using LhashBucket = Dnode<Key>;
626 
627 
635 template <typename Key>
636 struct LhashBucketVtl : public Dnode<Key>
637 {
638  using Base = Dnode<Key>;
639  using Base::Base;
640 
642  virtual ~LhashBucketVtl() {}
643 };
644 
659  template <typename Key, class Cmp = Aleph::equal_to<Key>>
660 struct LhashTable : public GenLhashTable<Key, LhashBucket<Key>, Cmp>
661 {
663  using Base::Base;
664 };
665 
680  template <typename Key, class Cmp = Aleph::equal_to<Key> >
681 struct LhashTableVtl : public GenLhashTable<Key, LhashBucketVtl<Key>, Cmp>
682 {
684  using Base::Base;
685 };
686 
687 
688 } // end namespace Aleph
689 
690 # ifdef NBACKUP
691 # define N NBACKUP
692 # undef NBACKUP
693 # endif
694 
695 # ifdef MBACKUP
696 # define M MBACKUP
697 # undef MBACKUP
698 # endif
699 
700 # endif/* TPL_LHASH_H */
701 
Bucket * insert(Bucket *bucket)
Definition: tpl_lhash.H:216
virtual ~GenLhashTable()
Definition: tpl_lhash.H:328
const size_t & get_num_busy_slots() const noexcept
Retorna la cantidad de entradas del arreglo que están ocupadas.
Definition: tpl_lhash.H:369
Iterator() noexcept
Instancia un iterador vacío.
Definition: tpl_lhash.H:534
Iterator(const GenLhashTable &table) noexcept
Instancia un iterador sobre la tabla hash table.
Definition: tpl_lhash.H:522
void reset_last() noexcept
Reinicia el iterador sobre la última cubeta.
Definition: tpl_lhash.H:545
T & get_key() noexcept
Definition: tpl_dnode.H:187
virtual ~LhashBucketVtl()
destructor virtual.
Definition: tpl_lhash.H:642
Dnode< T > *& get_next() const noexcept
Return the next node to this
Definition: tpl_dnode.H:53
void set_hash_fct(Hash_Fct fct) noexcept
Set the internal hash function.
Definition: tpl_lhash.H:201
Definition: tpl_lhash.H:636
bool has_curr() const noexcept
Retorna true si el iterador tiene cubeta actual.
Definition: tpl_lhash.H:558
Definition: ah-comb.H:35
void empty() noexcept
Vacía la tabla hash; libera memoria de todas las cubetas.
Definition: tpl_lhash.H:173
void prev_ne() noexcept
Retrocede el iterador una cubeta.
Definition: tpl_lhash.H:596
Definition: tpl_lhash.H:77
Bucket * Item_Type
El tipo de elemento que retorna get_curr().
Definition: tpl_lhash.H:519
float current_alpha() const noexcept
return the current table load
Definition: tpl_lhash.H:212
void reset_first() noexcept
Reinicia el iterador sobre la primera cubeta.
Definition: tpl_lhash.H:537
const size_t & size() const noexcept
Retorna el número de elementos que contiene la tabla.
Definition: tpl_lhash.H:365
size_t resize(size_t new_size)
Definition: tpl_lhash.H:301
Definition: tpl_lhash.H:681
Bucket * search_next(Bucket *bucket) const noexcept
Definition: tpl_lhash.H:338
GenLhashTable(size_t table_size=Primes::DefaultPrime, Hash_Fct_Ptr hash_fct=Aleph::dft_hash_fct< Key >, Cmp cmp=Cmp(), float lower_alpha=hash_default_lower_alpha, float upper_alpha=hash_default_upper_alpha, bool remove_all_buckets=true, bool with_resize=true)
Definition: tpl_lhash.H:149
Bucket * search(const Key &key) const noexcept
Definition: tpl_lhash.H:261
Definition: tpl_lhash.H:660
Definition: primes.H:33
void next_ne() noexcept
Avanza el iterador una cubeta.
Definition: tpl_lhash.H:583
Bucket * get_curr()
Retorna la cubeta actual.
Definition: tpl_lhash.H:569
Definition: List.H:49
const size_t & capacity() const noexcept
Retorna el tamaño de la tabla.
Definition: tpl_lhash.H:362
Definition: tpl_lhash.H:379

Leandro Rabindranath León