Aleph-w  1.5a.2
Biblioteca general de algoritmos y estructuras de datos
 Todo Clases Archivos Funciones Variables 'typedefs' Enumeraciones Amigas Grupos Páginas
tpl_hash_cache.H
1 
2 # ifndef TPL_HASH_CACHE_H
3 # define TPL_HASH_CACHE_H
4 
5 # include <memory>
6 # include <aleph.H>
7 # include <tpl_dnode.H>
8 # include <tpl_lhash.H>
9 
10 namespace Aleph {
11 
12 
30  template <typename Key, typename Data, class Cmp = Aleph::equal_to<Key>>
32 {
33 
34 public:
35 
46  class Cache_Entry : public LhashTable<Key, Cmp>::Bucket
47  {
48 
49  friend class Hash_Cache<Key, Data>;
50 
51  Data data;
52  Dlink dlink_lru; // enlace a la cola lru
53  Dlink* link_lru() { return &dlink_lru; }
54  bool locked; // indica si la entrada está trancada
55  bool is_in_hash_table; // indica si entrada está contenida dentro
56  // de tabla hash
57  void lock()
58  {
59  if (locked)
60  throw std::runtime_error("Cache_Entry is already locked");
61 
62  locked = true;
63  }
64 
65  void unlock()
66  {
67  if (not locked)
68  throw std::runtime_error("Cache_Entry is not locked");
69 
70  locked = false;
71  }
72  Dlink dlink_inside; // enlace a la lista de entradas que
73 
74  Dlink * link_inside() { return &dlink_inside; }
75 
76  LINKNAME_TO_TYPE(Cache_Entry, dlink_inside);
77 
78  public:
79  Cache_Entry() : locked(false), is_in_hash_table(false) { /* empty */ }
80 
81 
83  Data & get_data() { return data; }
85  const bool & is_locked() const { return locked; }
86 
88  const bool & is_in_table() const { return is_in_hash_table; }
89  }; // fin class Cache_Entry
90 
91 private:
92 
93  LhashTable<Key, Cmp> hash_table;
94  LINKNAME_TO_TYPE(Cache_Entry, dlink_lru);
95  Dlink lru_list; // cabecera de la lista lru
96  size_t num_lru; // número de elementos en lista lru
97  Dlink inside_list; // lista de cubetas apartadas y metidas en tabla
98  size_t cache_size; // máx número de entradas que puede tener cache
99  Dlink locked_list; // lista de entradas trancadas
100  size_t num_locked; // número de elementos trancados
101  typedef Dnode<Cache_Entry*> Chunk_Descriptor;
102  Chunk_Descriptor chunk_list;
103  void insert_entry_to_lru_list(Cache_Entry * cache_entry)
104  {
105  num_lru++;
106  lru_list.insert(cache_entry->link_lru());
107  }
108  void remove_entry_from_lru_list(Cache_Entry * cache_entry)
109  {
110  num_lru--;
111  cache_entry->link_lru()->del();
112  }
113 
114  void insert_entry_to_locked_list(Cache_Entry * cache_entry)
115  {
116  num_locked++;
117  locked_list.insert(cache_entry->link_lru());
118  }
119 
120  void remove_entry_from_locked_list(Cache_Entry * cache_entry)
121  {
122  num_locked--;
123  cache_entry->link_lru()->del();
124  }
125 
126  void move_to_inside_front(Cache_Entry * cache_entry)
127  {
128  cache_entry->link_inside()->del();
129  inside_list.insert(cache_entry->link_inside());
130  }
131 
132  void do_mru(Cache_Entry * cache_entry)
133  {
134  cache_entry->link_lru()->del(); // elimine de posición actual
135  lru_list.insert(cache_entry->link_lru()); // llévela a trasero
136  }
137  void do_lru(Cache_Entry * cache_entry)
138  {
139  cache_entry->link_lru()->del(); // elimine de posición actual
140  lru_list.append(cache_entry->link_lru()); // llévela al frente
141  }
142  void remove_entry_from_hash_table(Cache_Entry * cache_entry)
143  {
144  cache_entry->link_inside()->del();
145  hash_table.remove(cache_entry);
146  cache_entry->is_in_hash_table = false;
147  do_lru(cache_entry);
148  }
149  Cache_Entry * get_lru_entry()
150  {
151 
152  if (lru_list.is_empty()) // ¿existe una entrada disponible?
153  throw std::underflow_error("All entries are locked"); // no ==> ¡excepción!
154 
155  // obtenga entrada más antigua; menos recientemente accedida
156  Dlink * lru_entry_link = lru_list.get_prev();
157  Cache_Entry * cache_entry = dlink_lru_to_Cache_Entry(lru_entry_link);
158 
159  // si cache_entry contenida en tabla ==> eliminarlo
160  if (cache_entry->is_in_hash_table)
161  remove_entry_from_hash_table(cache_entry);
162 
163  do_mru(cache_entry); // entrada deviene más recientemente accedida
164 
165  return cache_entry;
166  }
167 
168 public:
169 
188  Hash_Cache(size_t (*hash_fct)(const Key&),
189  const size_t & __hash_size, const size_t & __cache_size)
190 
191  throw (std::exception, std::bad_alloc)
192 
193  : hash_table(hash_fct, __hash_size, false),
194  num_lru(0), cache_size(__cache_size), num_locked(0)
195  {
196  // apartar entradas del cache
197  Cache_Entry * entries_array = new Cache_Entry [cache_size];
198 
199  try
200  {
201 
202 
203  // apartar el descriptor del arreglo
204  std::unique_ptr<Chunk_Descriptor>
205  chunk_descriptor (new Chunk_Descriptor (entries_array));
206  chunk_list.insert(chunk_descriptor.get());
207 
208  // insertar cada Cache_Entry en lista lru
209  for (int i = 0; i < cache_size; i++)
210  insert_entry_to_lru_list(&entries_array[i]);
211 
212  chunk_descriptor.release();
213 
214  }
215  catch (...)
216  {
217  delete [] entries_array;
218  throw;
219  }
220 
221  }
222 
223  virtual ~Hash_Cache()
224  { // recorrer lista de bloques y liberarlos
225  while (not chunk_list.is_empty())
226  {
227  Chunk_Descriptor * current_chunk = chunk_list.remove_next();
228 
229  delete [] current_chunk->get_data();
230  delete current_chunk;
231  }
232  }
233 
251  Cache_Entry * insert(const Key & key, const Data & data)
252  {
253  Cache_Entry * cache_entry = get_lru_entry(); // entrada más antigua
254  cache_entry->get_key() = key; // escribirle el par
255  cache_entry->get_data() = data;
256  inside_list.insert(cache_entry->link_inside());
257  hash_table.insert(cache_entry);
258  cache_entry->is_in_hash_table = true;
259  return cache_entry;
260  }
270  Cache_Entry * search(const Key & key)
271  { // buscar en la tabla hash
272  Cache_Entry * cache_entry = (Cache_Entry*) hash_table.search(key);
273  if (cache_entry != NULL) // ¿fue encontrada la clave?
274  { // sí ==> hacerla la más recientemente usada
275  do_mru(cache_entry);
276  move_to_inside_front(cache_entry);
277  }
278  return cache_entry;
279  }
280 
292  {
293  Cache_Entry *next_entry =
294  static_cast<Cache_Entry*>(hash_table.search_next(cache_entry));
295  if (next_entry != NULL)
296  {
297  do_mru(next_entry);
298  move_to_inside_front(cache_entry);
299  }
300  return next_entry;
301  }
302 
306  void lock_entry(Cache_Entry * cache_entry)
307 
308  throw(std::exception, std::runtime_error, std::domain_error)
309 
310  {
311 
312  if (cache_entry->is_locked())
313  throw std::runtime_error("Cache_Entry is already locked");
314 
315  if (not cache_entry->is_in_table())
316  throw std::domain_error("Cache_Entry is not in the cache");
317 
318  remove_entry_from_lru_list(cache_entry);
319  insert_entry_to_locked_list(cache_entry);
320  cache_entry->lock();
321  }
324  void unlock_entry(Cache_Entry * cache_entry)
325 
326  throw(std::exception, std::runtime_error)
327 
328  {
329 
330  if (not cache_entry->is_locked())
331  throw std::runtime_error("Cache_Entry is not locked");
332 
333  remove_entry_from_locked_list(cache_entry);
334  insert_entry_to_lru_list(cache_entry);
335  cache_entry->unlock();
336  }
340  void remove(Cache_Entry * cache_entry)
341 
342  throw(std::exception, std::runtime_error, std::domain_error)
343 
344  {
345 
346  if (cache_entry->is_locked())
347  throw std::runtime_error("Cache_Entry is already locked");
348  if (not cache_entry->is_in_table())
349  throw std::domain_error("Cache_Entry is not in the cache");
350 
351  remove_entry_from_hash_table(cache_entry);
352  }
357  void expand(const size_t & plus_size)
358 
359  throw(std::exception, std::range_error, std::bad_alloc)
360 
361  {
362 
363  if (plus_size == 0)
364  throw std::range_error ("bad plus_size");
365 
366  const size_t new_cache_size = cache_size + plus_size;
367 
368  // apartar plus_size nuevas entradas
369  Cache_Entry * entries_array = new Cache_Entry [plus_size];
370 
371  try
372  {
373 
374  std::unique_ptr<Chunk_Descriptor> // apartar el descriptor
375  chunk_descriptor (new Chunk_Descriptor (entries_array));
376 
377  // Calcular nuevo tamaño de tabla y relocalizar sus entradas
378  const float curr_hash_ratio = 1.0*cache_size/hash_table.capacity();
379  const size_t new_hash_capacity = new_cache_size/curr_hash_ratio;
380 
381  hash_table.resize(new_hash_capacity);
382 
383  // meter nuevas entradas en lru_list
384  for (int i = 0; i < plus_size; i++)
385  insert_entry_to_lru_list(&entries_array[i]);
386 
387  chunk_list.insert(chunk_descriptor.release());
388  cache_size = new_cache_size;
389 
390  }
391  catch (...)
392  {
393  delete [] entries_array;
394  throw;
395  }
396 
397  }
398 
399 
401  const size_t & capacity() const { return cache_size; }
402 
404  const size_t & size() const { return hash_table.size(); }
405 
409  const size_t & get_num_locked() const { return num_locked; }
410 
414  const size_t & get_num_busy_slots() const
415  {
416  return hash_table.get_num_busy_slots();
417  }
418 
420  const size_t & get_hash_capacity() const
421  {
422  return hash_table.capacity();
423  }
424 
428  class Iterator : public Dlink::Iterator
429  {
430 
431  public:
436 
438  Iterator(Hash_Cache & cache) : Dlink::Iterator(&cache.inside_list) {}
439 
442  {
444  return Cache_Entry::dlink_inside_to_Cache_Entry(dl);
445  }
446  };
447 };
448 
449 
450 } // end namespace Aleph
451 
452 # endif // TPL_HASH_CACHE_H
453 
Cache_Entry * get_current()
Retorna el Cache_Entry actual.
Definition: tpl_hash_cache.H:441
const size_t & size() const
Retorna en número de datos que están contenidos en el cache.
Definition: tpl_hash_cache.H:404
void expand(const size_t &plus_size)
Definition: tpl_hash_cache.H:357
Cache_Entry * search(const Key &key)
Definition: tpl_hash_cache.H:270
Definition: tpl_hash_cache.H:428
#define LINKNAME_TO_TYPE(type_name, link_name)
Definition: dlink.H:741
Dnode< T > * remove_next()
Elimina sucesor a this y retorna su dirección.
Definition: tpl_dnode.H:37
const bool & is_locked() const
Retorna true si la entrada está trancada.
Definition: tpl_hash_cache.H:85
const size_t & get_num_busy_slots() const
Definition: tpl_hash_cache.H:414
void unlock_entry(Cache_Entry *cache_entry)
Definition: tpl_hash_cache.H:324
Cache_Entry * Item_Type
El tipo de elemento que retorna get_current().
Definition: tpl_hash_cache.H:435
Data & get_data()
Retorna una referencia al dato asociado a la clave.
Definition: tpl_hash_cache.H:83
const size_t & get_hash_capacity() const
Retorna el tamaño de la tabla hash.
Definition: tpl_hash_cache.H:420
Definition: tpl_hash_cache.H:31
Iterator(Hash_Cache &cache)
Instancia un iterador sobre cache.
Definition: tpl_hash_cache.H:438
Cache_Entry * insert(const Key &key, const Data &data)
Definition: tpl_hash_cache.H:251
Definition: tpl_lhash.H:531
const size_t & capacity() const
Retorna el tamaño de cache.
Definition: tpl_hash_cache.H:401
Cache_Entry * search_next(Cache_Entry *cache_entry)
Definition: tpl_hash_cache.H:291
const bool & is_in_table() const
Retorna true si la entrada está dentro de la tabla.
Definition: tpl_hash_cache.H:88
void lock_entry(Cache_Entry *cache_entry)
Definition: tpl_hash_cache.H:306
Definition: tpl_hash_cache.H:46
Hash_Cache(size_t(*hash_fct)(const Key &), const size_t &__hash_size, const size_t &__cache_size)
Definition: tpl_hash_cache.H:188
const size_t & get_num_locked() const
Definition: tpl_hash_cache.H:409
T & get_data()
Retorna referencia a dato contenido en el nodo.
Definition: tpl_dnode.H:126
Hash_Cache Set_Type
El tipo de conjunto sobre el cual se itera.
Definition: tpl_hash_cache.H:433

Leandro Rabindranath León