diff --git a/src/storage/SimpleLRU.cpp b/src/storage/SimpleLRU.cpp index 0b895bcdf..db1e8672d 100644 --- a/src/storage/SimpleLRU.cpp +++ b/src/storage/SimpleLRU.cpp @@ -1,22 +1,180 @@ #include "SimpleLRU.h" +#include "iostream" namespace Afina { namespace Backend { +void SimpleLRU::LRU_delete() { + lru_node* deleted_node = _lru_tail; + _curr_size -= deleted_node->key.size() + deleted_node->value.size(); + _lru_index.erase(deleted_node->key); + if (deleted_node != _lru_head.get()) + { + _lru_tail = deleted_node->prev; + _lru_tail->next.reset(); + } + else //there is only one element in list + { + _lru_head.reset(); + } +} + +void SimpleLRU::LRU_move(lru_node* moved_node) { + if (_lru_head.get() == moved_node) //node is already a head + { + return; + } + if (!moved_node->next.get()) //move the last node + { + _lru_tail = moved_node->prev; + _lru_head.get()->prev = moved_node; + moved_node->next = std::move(_lru_head); + _lru_head = std::move(moved_node->prev->next); + } + else + { + auto p = std::move(_lru_head); + _lru_head = std::move(moved_node->prev->next); + moved_node->prev->next = std::move(moved_node->next); + p.get()->prev = moved_node; + moved_node->next = std::move(p); + moved_node->prev->next->prev = moved_node->prev; + } +} + +void SimpleLRU::PutIn(const std::string &key, const std::string &value, std::size_t node_size) { + while (node_size + _curr_size > _max_size) + { + LRU_delete(); + } + lru_node *new_node = new lru_node{key, value, nullptr, nullptr}; + //insert + if (_lru_head.get()) + { + _lru_head.get()->prev = new_node; + } + else + { + _lru_tail = new_node; + } + new_node->prev = nullptr; + new_node->next = std::move(_lru_head); + _lru_head.reset(new_node); + //end of insert + _lru_index.insert({std::reference_wrapper(new_node->key), std::reference_wrapper(*new_node)}); + _curr_size += node_size; +} + +void SimpleLRU::SetIn(lru_node &node, const std::string &value) { + int diff_size = value.size() - node.value.size(); //change size_t on int + LRU_move(&node); + while (_curr_size + diff_size > _max_size) + { + LRU_delete(); + } + node.value = value; + _curr_size += diff_size; +} + // See MapBasedGlobalLockImpl.h -bool SimpleLRU::Put(const std::string &key, const std::string &value) { return false; } +bool SimpleLRU::Put(const std::string &key, const std::string &value) { + std::size_t node_size = key.size() + value.size(); + if (node_size > _max_size) // can't guarantee invariant + { + return false; + } + auto node = _lru_index.find(key); + + if (node == _lru_index.end()) // there is no key + { + PutIn(key, value, node_size); + } + else + { + SetIn((node->second).get(), value); + } + return true; +} // See MapBasedGlobalLockImpl.h -bool SimpleLRU::PutIfAbsent(const std::string &key, const std::string &value) { return false; } +bool SimpleLRU::PutIfAbsent(const std::string &key, const std::string &value) { + std::size_t node_size = key.size() + value.size(); + if (node_size > _max_size) // can't guarantee invariant + { + return false; + } + auto node = _lru_index.find(key); + if (node == _lru_index.end()) // there is no key + { + PutIn(key, value, node_size); + return true; + } + return false; +} // See MapBasedGlobalLockImpl.h -bool SimpleLRU::Set(const std::string &key, const std::string &value) { return false; } +bool SimpleLRU::Set(const std::string &key, const std::string &value) { + int node_size = key.size() + value.size(); + if (node_size > _max_size) // can't guarantee invariant + { + return false; + } + auto node = _lru_index.find(key); + if (node != _lru_index.end()) // key exists + { + SetIn((node->second).get(), value); + return true; + } + return false; +} // See MapBasedGlobalLockImpl.h -bool SimpleLRU::Delete(const std::string &key) { return false; } +bool SimpleLRU::Delete(const std::string &key) { + auto p = _lru_index.find(key); + if (p == _lru_index.end()) + { + return false; + } + lru_node* node = &(p->second.get()); + _lru_index.erase(p); + _curr_size -= node->key.size() + node->value.size(); + if (node == _lru_head.get()) //deleting head + { + if (!node->next.get()) //one element in list + { + _lru_tail = nullptr; + _lru_head.reset(); + } + else + { + node->next.get()->prev = nullptr; + _lru_head = std::move(node->next); + } + } + else if (!node->next.get()) //deleting last element + { + _lru_tail = node->prev; + node->prev->next.reset(); + } + else + { + node->next.get()->prev = node->prev; + node->prev->next = std::move(node->next); + } + return true; +} // See MapBasedGlobalLockImpl.h -bool SimpleLRU::Get(const std::string &key, std::string &value) { return false; } +bool SimpleLRU::Get(const std::string &key, std::string &value) { + auto node = _lru_index.find(key); + if (node != _lru_index.end()) + { + value = node->second.get().value; + LRU_move(&node->second.get()); + return true; + } + return false; +} } // namespace Backend } // namespace Afina diff --git a/src/storage/SimpleLRU.h b/src/storage/SimpleLRU.h index 6e05435a8..4fc68622d 100644 --- a/src/storage/SimpleLRU.h +++ b/src/storage/SimpleLRU.h @@ -17,49 +17,62 @@ namespace Backend { */ class SimpleLRU : public Afina::Storage { public: - SimpleLRU(size_t max_size = 1024) : _max_size(max_size) {} + SimpleLRU(size_t max_size = 1024) : _max_size(max_size), _curr_size(0), _lru_tail(nullptr) {} - ~SimpleLRU() { - _lru_index.clear(); - _lru_head.reset(); // TODO: Here is stack overflow - } + ~SimpleLRU() { + _lru_index.clear(); + while ((_lru_tail != nullptr) && (_lru_tail != _lru_head.get())) + { + _lru_tail = _lru_tail->prev; + _lru_tail->next.reset(); + } + _lru_head.reset(); + } - // Implements Afina::Storage interface - bool Put(const std::string &key, const std::string &value) override; + // Implements Afina::Storage interface + bool Put(const std::string &key, const std::string &value) override; - // Implements Afina::Storage interface - bool PutIfAbsent(const std::string &key, const std::string &value) override; + // Implements Afina::Storage interface + bool PutIfAbsent(const std::string &key, const std::string &value) override; - // Implements Afina::Storage interface - bool Set(const std::string &key, const std::string &value) override; + // Implements Afina::Storage interface + bool Set(const std::string &key, const std::string &value) override; - // Implements Afina::Storage interface - bool Delete(const std::string &key) override; + // Implements Afina::Storage interface + bool Delete(const std::string &key) override; - // Implements Afina::Storage interface - bool Get(const std::string &key, std::string &value) override; + // Implements Afina::Storage interface + bool Get(const std::string &key, std::string &value) override; private: - // LRU cache node - using lru_node = struct lru_node { - std::string key; - std::string value; - std::unique_ptr prev; - std::unique_ptr next; - }; - - // Maximum number of bytes could be stored in this cache. - // i.e all (keys+values) must be less the _max_size - std::size_t _max_size; - - // Main storage of lru_nodes, elements in this list ordered descending by "freshness": in the head - // element that wasn't used for longest time. - // - // List owns all nodes - std::unique_ptr _lru_head; - - // Index of nodes from list above, allows fast random access to elements by lru_node#key - std::map, std::reference_wrapper, std::less> _lru_index; + // LRU cache node + using lru_node = struct lru_node { + const std::string key; + std::string value; + lru_node* prev; + std::unique_ptr next; + }; + + // Maximum number of bytes could be stored in this cache. + // i.e all (keys+values) must be less the _max_size + const std::size_t _max_size; + std::size_t _curr_size; + + // Main storage of lru_nodes, elements in this list ordered descending by "freshness": in the head + // element that wasn't used for longest time. + // + // List owns all nodes + std::unique_ptr _lru_head; + lru_node* _lru_tail; + + // Index of nodes from list above, allows fast random access to elements by lru_node#key + std::map, std::reference_wrapper, std::less> _lru_index; + + //help functions + void LRU_delete(); + void LRU_move(lru_node* moved_node); + void PutIn(const std::string &key, const std::string &value, std::size_t node_size); + void SetIn(lru_node &node, const std::string &value); }; } // namespace Backend diff --git a/test/storage/StorageTest.cpp b/test/storage/StorageTest.cpp index b3fae8d37..0717bc49e 100644 --- a/test/storage/StorageTest.cpp +++ b/test/storage/StorageTest.cpp @@ -195,3 +195,14 @@ TEST(StorageTest, MaxTest) { EXPECT_FALSE(storage.Get(key, res)); } } + +TEST(StorageTest, SHORT_NEW_VALUE){ + SimpleLRU storage(22); + std::string result; + EXPECT_TRUE(storage.Put("KEY", "VERY_VERY_BIG_VALUE")); + EXPECT_TRUE(storage.Put("KEY1", "VALUE")); + EXPECT_TRUE(storage.Put("KEY2", "VALUE")); + EXPECT_TRUE(storage.Put("KEY", "VALUE")); + EXPECT_TRUE(storage.Get("KEY", result)); + EXPECT_TRUE(storage.Get("KEY2", result)); +}