Aleph-w  1.9
General library for algorithms and data structures
tpl_hash_cache.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_HASH_CACHE_H
29 # define TPL_HASH_CACHE_H
30 
31 # include <memory>
32 # include <aleph.H>
33 # include <tpl_dnode.H>
34 # include <tpl_lhash.H>
35 
36 namespace Aleph {
37 
38 
56  template <typename Key, typename Data, class Cmp = Aleph::equal_to<Key>>
57  class Hash_Cache
58  {
59 
60  public:
61 
72  class Cache_Entry : public LhashTable<Key, Cmp>::Bucket
73  {
74  friend class Hash_Cache<Key, Data>;
75 
76  Data data;
77  Dlink dlink_lru; // enlace a la cola lru
78  Dlink* link_lru() { return &dlink_lru; }
79  bool locked; // indica si la entrada está trancada
80  bool is_in_hash_table; // indica si entrada está contenida
81  // dentro de tabla hash
82  void lock()
83  {
84  if (locked)
85  throw std::runtime_error("Cache_Entry is already locked");
86 
87  locked = true;
88  }
89 
90  void unlock()
91  {
92  if (not locked)
93  throw std::runtime_error("Cache_Entry is not locked");
94 
95  locked = false;
96  }
97 
98  Dlink dlink_inside;
99 
100  Dlink * link_inside() { return &dlink_inside; }
101 
102  LINKNAME_TO_TYPE(Cache_Entry, dlink_inside);
103 
104  public:
105 
106  Cache_Entry()
107  : data(), locked(false), is_in_hash_table(false) { /* empty */ }
108 
110  Data & get_data() { return data; }
111 
113  const bool & is_locked() const { return locked; }
114 
116  const bool & is_in_table() const { return is_in_hash_table; }
117  }; // fin class Cache_Entry
118 
119  private:
120 
121  LhashTable<Key, Cmp> hash_table;
122  LINKNAME_TO_TYPE(Cache_Entry, dlink_lru);
123  Dlink lru_list; // cabecera de la lista lru
124  size_t num_lru; // número de elementos en lista lru
125  Dlink inside_list; // lista de cubetas apartadas y metidas en tabla
126  size_t cache_size; // máx número de entradas que puede tener cache
127  Dlink locked_list; // lista de entradas trancadas
128  size_t num_locked; // número de elementos trancados
130  Chunk_Descriptor chunk_list;
131 
132  void insert_entry_to_lru_list(Cache_Entry * cache_entry)
133  {
134  num_lru++;
135  lru_list.insert(cache_entry->link_lru());
136  }
137  void remove_entry_from_lru_list(Cache_Entry * cache_entry)
138  {
139  num_lru--;
140  cache_entry->link_lru()->del();
141  }
142 
143  void insert_entry_to_locked_list(Cache_Entry * cache_entry)
144  {
145  num_locked++;
146  locked_list.insert(cache_entry->link_lru());
147  }
148 
149  void remove_entry_from_locked_list(Cache_Entry * cache_entry)
150  {
151  num_locked--;
152  cache_entry->link_lru()->del();
153  }
154 
155  void move_to_inside_front(Cache_Entry * cache_entry)
156  {
157  cache_entry->link_inside()->del();
158  inside_list.insert(cache_entry->link_inside());
159  }
160 
161  void do_mru(Cache_Entry * cache_entry)
162  {
163  cache_entry->link_lru()->del(); // elimine de posición actual
164  lru_list.insert(cache_entry->link_lru()); // llévela a trasero
165  }
166 
167  void do_lru(Cache_Entry * cache_entry)
168  {
169  cache_entry->link_lru()->del(); // elimine de posición actual
170  lru_list.append(cache_entry->link_lru()); // llévela al frente
171  }
172 
173  void remove_entry_from_hash_table(Cache_Entry * cache_entry)
174  {
175  cache_entry->link_inside()->del();
176  hash_table.remove(cache_entry);
177  cache_entry->is_in_hash_table = false;
178  do_lru(cache_entry);
179  }
180 
181  Cache_Entry * get_lru_entry()
182  {
183  if (lru_list.is_empty()) // ¿existe una entrada disponible?
184  throw std::underflow_error("All entries are locked"); // no ==> ¡excepción!
185 
186  // obtenga entrada más antigua; menos recientemente accedida
187  Dlink * lru_entry_link = lru_list.get_prev();
188  Cache_Entry * cache_entry = dlink_lru_to_Cache_Entry(lru_entry_link);
189 
190  // si cache_entry contenida en tabla ==> eliminarlo
191  if (cache_entry->is_in_hash_table)
192  remove_entry_from_hash_table(cache_entry);
193 
194  do_mru(cache_entry); // entrada deviene más recientemente accedida
195 
196  return cache_entry;
197  }
198 
199  public:
200 
219  Hash_Cache(size_t (*hash_fct)(const Key&),
220  size_t __hash_size, const size_t & __cache_size)
221  : hash_table(__hash_size, hash_fct),
222  num_lru(0), cache_size(__cache_size), num_locked(0)
223  {
224  // apartar entradas del cache
225  Cache_Entry * entries_array = new Cache_Entry [cache_size];
226 
227  try
228  { // apartar el descriptor del arreglo
229  std::unique_ptr<Chunk_Descriptor>
230  chunk_descriptor (new Chunk_Descriptor (entries_array));
231  chunk_list.insert(chunk_descriptor.get());
232 
233  // insertar cada Cache_Entry en lista lru
234  for (int i = 0; i < cache_size; i++)
235  insert_entry_to_lru_list(&entries_array[i]);
236 
237  chunk_descriptor.release();
238 
239  }
240  catch (...)
241  {
242  delete [] entries_array;
243  throw;
244  }
245 
246  }
247 
248  virtual ~Hash_Cache()
249  { // recorrer lista de bloques y liberarlos
250  while (not chunk_list.is_empty())
251  {
252  Chunk_Descriptor * current_chunk = chunk_list.remove_next();
253 
254  delete [] current_chunk->get_data();
255  delete current_chunk;
256  }
257  }
258 
276  Cache_Entry * insert(const Key & key, const Data & data)
277  {
278  Cache_Entry * cache_entry = get_lru_entry(); // entrada más antigua
279  cache_entry->get_key() = key; // escribirle el par
280  cache_entry->get_data() = data;
281  inside_list.insert(cache_entry->link_inside());
282  hash_table.insert(cache_entry);
283  cache_entry->is_in_hash_table = true;
284  return cache_entry;
285  }
295  Cache_Entry * search(const Key & key)
296  { // buscar en la tabla hash
297  Cache_Entry * cache_entry = (Cache_Entry*) hash_table.search(key);
298  if (cache_entry != nullptr) // ¿fue encontrada la clave?
299  { // sí ==> hacerla la más recientemente usada
300  do_mru(cache_entry);
301  move_to_inside_front(cache_entry);
302  }
303  return cache_entry;
304  }
305 
317  {
318  Cache_Entry *next_entry =
319  static_cast<Cache_Entry*>(hash_table.search_next(cache_entry));
320  if (next_entry != nullptr)
321  {
322  do_mru(next_entry);
323  move_to_inside_front(cache_entry);
324  }
325  return next_entry;
326  }
327 
331  void lock_entry(Cache_Entry * cache_entry)
332  {
333 
334  if (cache_entry->is_locked())
335  throw std::runtime_error("Cache_Entry is already locked");
336 
337  if (not cache_entry->is_in_table())
338  throw std::domain_error("Cache_Entry is not in the cache");
339 
340  remove_entry_from_lru_list(cache_entry);
341  insert_entry_to_locked_list(cache_entry);
342  cache_entry->lock();
343  }
344 
347  void unlock_entry(Cache_Entry * cache_entry)
348  {
349  if (not cache_entry->is_locked())
350  throw std::runtime_error("Cache_Entry is not locked");
351 
352  remove_entry_from_locked_list(cache_entry);
353  insert_entry_to_lru_list(cache_entry);
354  cache_entry->unlock();
355  }
356 
360  void remove(Cache_Entry * cache_entry)
361  {
362  if (cache_entry->is_locked())
363  throw std::runtime_error("Cache_Entry is already locked");
364  if (not cache_entry->is_in_table())
365  throw std::domain_error("Cache_Entry is not in the cache");
366 
367  remove_entry_from_hash_table(cache_entry);
368  }
369 
374  void expand(const size_t & plus_size)
375  {
376  if (plus_size == 0)
377  throw std::range_error ("bad plus_size");
378 
379  const size_t new_cache_size = cache_size + plus_size;
380 
381  // apartar plus_size nuevas entradas
382  Cache_Entry * entries_array = new Cache_Entry [plus_size];
383 
384  try
385  {
386  std::unique_ptr<Chunk_Descriptor> // apartar el descriptor
387  chunk_descriptor (new Chunk_Descriptor (entries_array));
388 
389  // Calcular nuevo tamaño de tabla y relocalizar sus entradas
390  const float curr_hash_ratio = 1.0*cache_size/hash_table.capacity();
391  const size_t new_hash_capacity = new_cache_size/curr_hash_ratio;
392 
393  hash_table.resize(new_hash_capacity);
394 
395  // meter nuevas entradas en lru_list
396  for (int i = 0; i < plus_size; i++)
397  insert_entry_to_lru_list(&entries_array[i]);
398 
399  chunk_list.insert(chunk_descriptor.release());
400  cache_size = new_cache_size;
401 
402  }
403  catch (...)
404  {
405  delete [] entries_array;
406  throw;
407  }
408 
409  }
410 
412  const size_t & capacity() const { return cache_size; }
413 
415  const size_t & size() const { return hash_table.size(); }
416 
420  const size_t & get_num_locked() const { return num_locked; }
421 
425  const size_t & get_num_busy_slots() const
426  {
427  return hash_table.get_num_busy_slots();
428  }
429 
431  const size_t & get_hash_capacity() const
432  {
433  return hash_table.capacity();
434  }
435 
439  class Iterator : public Dlink::Iterator
440  {
441  public:
446 
448  Iterator(Hash_Cache & cache) : Dlink::Iterator(&cache.inside_list) {}
449 
452  {
454  return Cache_Entry::dlink_inside_to_Cache_Entry(dl);
455  }
456  };
457  };
458 
459 
460 } // end namespace Aleph
461 
462 # endif // TPL_HASH_CACHE_H
463 
Hash_Cache(size_t(*hash_fct)(const Key &), size_t __hash_size, const size_t &__cache_size)
Definition: tpl_hash_cache.H:219
Bucket * insert(Bucket *bucket)
Definition: tpl_lhash.H:216
Cache_Entry * get_curr()
Retorna el Cache_Entry actual.
Definition: tpl_hash_cache.H:451
void expand(const size_t &plus_size)
Definition: tpl_hash_cache.H:374
Cache_Entry * search(const Key &key)
Definition: tpl_hash_cache.H:295
Definition: tpl_hash_cache.H:439
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
#define LINKNAME_TO_TYPE(type_name, link_name)
Definition: dlink.H:937
const bool & is_in_table() const
Retorna true si la entrada está dentro de la tabla.
Definition: tpl_hash_cache.H:116
Bucket * remove(Bucket *bucket) noexcept
Definition: tpl_lhash.H:288
const size_t & get_num_locked() const
Definition: tpl_hash_cache.H:420
const size_t & size() const
Retorna en número de datos que están contenidos en el cache.
Definition: tpl_hash_cache.H:415
void unlock_entry(Cache_Entry *cache_entry)
Definition: tpl_hash_cache.H:347
Cache_Entry * Item_Type
El tipo de elemento que retorna get_curr().
Definition: tpl_hash_cache.H:445
Data & get_data()
Retorna una referencia al dato asociado a la clave.
Definition: tpl_hash_cache.H:110
Definition: ah-comb.H:35
Definition: tpl_hash_cache.H:57
Iterator(Hash_Cache &cache)
Instancia un iterador sobre cache.
Definition: tpl_hash_cache.H:448
T & get_data() noexcept
Return a modifiable reference to the data contained in the node.
Definition: tpl_dnode.H:178
Cache_Entry * insert(const Key &key, const Data &data)
Definition: tpl_hash_cache.H:276
Cache_Entry * search_next(Cache_Entry *cache_entry)
Definition: tpl_hash_cache.H:316
Dnode< T > * remove_next() noexcept
Remove the next node to this; return its address.
Definition: tpl_dnode.H:62
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
void lock_entry(Cache_Entry *cache_entry)
Definition: tpl_hash_cache.H:331
Definition: tpl_hash_cache.H:72
const size_t & get_num_busy_slots() const
Definition: tpl_hash_cache.H:425
const bool & is_locked() const
Retorna true si la entrada está trancada.
Definition: tpl_hash_cache.H:113
Bucket * search_next(Bucket *bucket) const noexcept
Definition: tpl_lhash.H:338
Bucket * search(const Key &key) const noexcept
Definition: tpl_lhash.H:261
Definition: tpl_lhash.H:660
const size_t & get_hash_capacity() const
Retorna el tamaño de la tabla hash.
Definition: tpl_hash_cache.H:431
const size_t & capacity() const
Retorna el tamaño de cache.
Definition: tpl_hash_cache.H:412
const size_t & capacity() const noexcept
Retorna el tamaño de la tabla.
Definition: tpl_lhash.H:362
Hash_Cache Set_Type
El tipo de conjunto sobre el cual se itera.
Definition: tpl_hash_cache.H:443

Leandro Rabindranath León