00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044 # ifndef TPL_HASH_CACHE_H
00045 # define TPL_HASH_CACHE_H
00046
00047 # include <memory>
00048 # include <aleph.H>
00049 # include <tpl_dnode.H>
00050 # include <tpl_lhash.H>
00051
00069 template <typename Key, typename Data, class Cmp = Aleph::equal_to<Key> >
00070 class Hash_Cache
00071 {
00072
00073 public:
00074
00085 class Cache_Entry : public LhashTable<Key, Cmp>::Bucket
00086 {
00087
00088 friend class Hash_Cache<Key, Data>;
00089
00090 Data data;
00091
00092 Dlink dlink_lru;
00093 Dlink* link_lru() { return &dlink_lru; }
00094 bool locked;
00095 bool is_in_hash_table;
00096
00097 void lock()
00098 {
00099 if (locked)
00100 throw std::runtime_error("Cache_Entry is already locked");
00101
00102 locked = true;
00103 }
00104
00105 void unlock()
00106 {
00107 if (not locked)
00108 throw std::runtime_error("Cache_Entry is not locked");
00109
00110 locked = false;
00111 }
00112 Dlink dlink_inside;
00113
00114 Dlink * link_inside() { return &dlink_inside; }
00115
00116 LINKNAME_TO_TYPE(Cache_Entry, dlink_inside);
00117
00118 public:
00119 Cache_Entry() : locked(false), is_in_hash_table(false) { }
00120
00121
00123 Data & get_data() { return data; }
00125 const bool & is_locked() const { return locked; }
00126
00128 const bool & is_in_table() const { return is_in_hash_table; }
00129 };
00130
00131 private:
00132
00133 LhashTable<Key, Cmp> hash_table;
00134 LINKNAME_TO_TYPE(Cache_Entry, dlink_lru);
00135 Dlink lru_list;
00136 size_t num_lru;
00137 Dlink inside_list;
00138
00139
00140 size_t cache_size;
00141
00142 Dlink locked_list;
00143 size_t num_locked;
00144 typedef Dnode<Cache_Entry*> Chunk_Descriptor;
00145
00146 Chunk_Descriptor chunk_list;
00147 void insert_entry_to_lru_list(Cache_Entry * cache_entry)
00148 {
00149 num_lru++;
00150 lru_list.insert(cache_entry->link_lru());
00151 }
00152
00153 void remove_entry_from_lru_list(Cache_Entry * cache_entry)
00154 {
00155 num_lru--;
00156 cache_entry->link_lru()->del();
00157 }
00158
00159 void insert_entry_to_locked_list(Cache_Entry * cache_entry)
00160 {
00161 num_locked++;
00162 locked_list.insert(cache_entry->link_lru());
00163 }
00164
00165 void remove_entry_from_locked_list(Cache_Entry * cache_entry)
00166 {
00167 num_locked--;
00168 cache_entry->link_lru()->del();
00169 }
00170
00171 void move_to_inside_front(Cache_Entry * cache_entry)
00172 {
00173 cache_entry->link_inside()->del();
00174 inside_list.insert(cache_entry->link_inside());
00175 }
00176
00177 void do_mru(Cache_Entry * cache_entry)
00178 {
00179 cache_entry->link_lru()->del();
00180 lru_list.insert(cache_entry->link_lru());
00181 }
00182 void do_lru(Cache_Entry * cache_entry)
00183 {
00184 cache_entry->link_lru()->del();
00185 lru_list.append(cache_entry->link_lru());
00186 }
00187 void remove_entry_from_hash_table(Cache_Entry * cache_entry)
00188 {
00189 cache_entry->link_inside()->del();
00190
00191 hash_table.remove(cache_entry);
00192 cache_entry->is_in_hash_table = false;
00193 do_lru(cache_entry);
00194 }
00195
00196 Cache_Entry * get_lru_entry()
00197 {
00198
00199 if (lru_list.is_empty())
00200 throw std::underflow_error("All entries are locked");
00201
00202
00203 Dlink * lru_entry_link = lru_list.get_prev();
00204 Cache_Entry * cache_entry = dlink_lru_to_Cache_Entry(lru_entry_link);
00205
00206
00207 if (cache_entry->is_in_hash_table)
00208 remove_entry_from_hash_table(cache_entry);
00209
00210 do_mru(cache_entry);
00211
00212 return cache_entry;
00213 }
00214
00215 public:
00216
00235 Hash_Cache(size_t (*hash_fct)(const Key&),
00236 const size_t & __hash_size,
00237 const size_t & __cache_size)
00238
00239 throw (std::exception, std::bad_alloc)
00240
00241 : hash_table(hash_fct, __hash_size, false),
00242 num_lru(0), cache_size(__cache_size), num_locked(0)
00243 {
00244
00245 Cache_Entry * entries_array = new Cache_Entry [cache_size];
00246
00247 try
00248 {
00249
00250
00251
00252 std::auto_ptr<Chunk_Descriptor>
00253 chunk_descriptor (new Chunk_Descriptor (entries_array));
00254
00255 chunk_list.insert(chunk_descriptor.get());
00256
00257
00258 for (int i = 0; i < cache_size; i++)
00259 insert_entry_to_lru_list(&entries_array[i]);
00260
00261 chunk_descriptor.release();
00262
00263 }
00264 catch (...)
00265 {
00266 delete [] entries_array;
00267 throw;
00268 }
00269
00270 }
00271
00272 virtual ~Hash_Cache()
00273 {
00274
00275 while (not chunk_list.is_empty())
00276 {
00277 Chunk_Descriptor * current_chunk = chunk_list.remove_next();
00278
00279 delete [] current_chunk->get_data();
00280 delete current_chunk;
00281 }
00282 }
00283
00301 Cache_Entry * insert(const Key & key, const Data & data)
00302 {
00303 Cache_Entry * cache_entry = get_lru_entry();
00304
00305
00306 cache_entry->get_key() = key;
00307 cache_entry->get_data() = data;
00308
00309 inside_list.insert(cache_entry->link_inside());
00310
00311
00312 hash_table.insert(cache_entry);
00313 cache_entry->is_in_hash_table = true;
00314
00315 return cache_entry;
00316 }
00326 Cache_Entry * search(const Key & key)
00327 {
00328
00329 Cache_Entry * cache_entry = static_cast<Cache_Entry*>(hash_table.search(key));
00330
00331 if (cache_entry != NULL)
00332 {
00333 do_mru(cache_entry);
00334 move_to_inside_front(cache_entry);
00335 }
00336
00337 return cache_entry;
00338 }
00339
00350 Cache_Entry * search_next(Cache_Entry * cache_entry)
00351 {
00352 Cache_Entry *next_entry =
00353 static_cast<Cache_Entry*>(hash_table.search_next(cache_entry));
00354
00355 if (next_entry != NULL)
00356 {
00357 do_mru(next_entry);
00358 move_to_inside_front(cache_entry);
00359 }
00360
00361 return next_entry;
00362 }
00363
00367 void lock_entry(Cache_Entry * cache_entry)
00368
00369 throw(std::exception, std::runtime_error, std::domain_error)
00370
00371 {
00372
00373 if (cache_entry->is_locked())
00374 throw std::runtime_error("Cache_Entry is already locked");
00375
00376 if (not cache_entry->is_in_table())
00377 throw std::domain_error("Cache_Entry is not in the cache");
00378
00379 remove_entry_from_lru_list(cache_entry);
00380 insert_entry_to_locked_list(cache_entry);
00381
00382 cache_entry->lock();
00383 }
00386 void unlock_entry(Cache_Entry * cache_entry)
00387
00388 throw(std::exception, std::runtime_error)
00389
00390 {
00391
00392 if (not cache_entry->is_locked())
00393 throw std::runtime_error("Cache_Entry is not locked");
00394
00395 remove_entry_from_locked_list(cache_entry);
00396 insert_entry_to_lru_list(cache_entry);
00397
00398 cache_entry->unlock();
00399 }
00403 void remove(Cache_Entry * cache_entry)
00404
00405 throw(std::exception, std::runtime_error, std::domain_error)
00406
00407 {
00408
00409 if (cache_entry->is_locked())
00410 throw std::runtime_error("Cache_Entry is already locked");
00411
00412 if (not cache_entry->is_in_table())
00413 throw std::domain_error("Cache_Entry is not in the cache");
00414
00415 remove_entry_from_hash_table(cache_entry);
00416 }
00421 void expand(const size_t & plus_size)
00422
00423 throw(std::exception, std::range_error, std::bad_alloc)
00424
00425 {
00426
00427 if (plus_size == 0)
00428 throw std::range_error ("bad plus_size");
00429
00430 const size_t new_cache_size = cache_size + plus_size;
00431
00433 Cache_Entry * entries_array = new Cache_Entry [plus_size];
00434
00435 try
00436 {
00437
00438
00439
00440 std::auto_ptr<Chunk_Descriptor>
00441 chunk_descriptor (new Chunk_Descriptor (entries_array));
00442
00443
00444
00445 const float curr_hash_ratio = 1.0*cache_size/hash_table.capacity();
00446 const size_t new_hash_capacity = new_cache_size/curr_hash_ratio;
00447 hash_table.resize(new_hash_capacity);
00448
00449
00450 for (int i = 0; i < plus_size; i++)
00451 insert_entry_to_lru_list(&entries_array[i]);
00452
00453 chunk_list.insert(chunk_descriptor.release());
00454
00455 cache_size = new_cache_size;
00456
00457 }
00458 catch (...)
00459 {
00460 delete [] entries_array;
00461 throw;
00462 }
00463
00464 }
00465
00466
00468 const size_t & capacity() const { return cache_size; }
00469
00471 const size_t & size() const { return hash_table.size(); }
00472
00476 const size_t & get_num_locked() const { return num_locked; }
00477
00481 const size_t & get_num_busy_slots() const
00482 {
00483 return hash_table.get_num_busy_slots();
00484 }
00485
00487 const size_t & get_hash_capacity() const
00488 {
00489 return hash_table.capacity();
00490 }
00491
00495 class Iterator : public Dlink::Iterator
00496 {
00497
00498 public:
00499
00501 Iterator(Hash_Cache & cache)
00502 : Dlink::Iterator(&cache.inside_list) { }
00503
00505 Cache_Entry * get_current()
00506 {
00507 Cache_Entry * ret_val =
00508 Cache_Entry::dlink_inside_to_Cache_Entry(Dlink::Iterator::get_current());
00509
00510 return ret_val;
00511 }
00512 };
00513 };
00514
00515 # endif // TPL_HASH_CACHE_H
00516