Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 163 additions & 5 deletions src/storage/SimpleLRU.cpp
Original file line number Diff line number Diff line change
@@ -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<const std::string>(new_node->key), std::reference_wrapper<lru_node>(*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
83 changes: 48 additions & 35 deletions src/storage/SimpleLRU.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<lru_node> prev;
std::unique_ptr<lru_node> 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_node> _lru_head;

// Index of nodes from list above, allows fast random access to elements by lru_node#key
std::map<std::reference_wrapper<std::string>, std::reference_wrapper<lru_node>, std::less<std::string>> _lru_index;
// LRU cache node
using lru_node = struct lru_node {
const std::string key;
std::string value;
lru_node* prev;
std::unique_ptr<lru_node> 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_node> _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<const std::string>, std::reference_wrapper<lru_node>, std::less<std::string>> _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
Expand Down
11 changes: 11 additions & 0 deletions test/storage/StorageTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}