Skip to content

Commit

Permalink
Dump & restore from dump (#3)
Browse files Browse the repository at this point in the history
* add dump method & restore from dump constructor
* add description to readme; improve tests
  • Loading branch information
khva authored Apr 9, 2023
1 parent 20f4d95 commit 3a54bb2
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 5 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.vs/
out/
CMakePresets.json
CMakeSettings.json
57 changes: 53 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@
## Оглавление
- [Описание](#описание)
- [Примеры использования](#примеры-использования)
- [Пример: добавление и поиск элемента](#пример-добавление-и-поиск-элемента)
- [Пример: удаление элемента](#пример-удаление-элемента)
- [Пример: добавление элемента с проверкой, что значение элемента не изменилось с прошлого обращения](#пример-добавление-элемента-с-проверкой-что-значение-элемента-не-изменилось-с-прошлого-обращения)
- [Пример: печать элементов хранилища](#пример-печать-элементов-хранилища)
- [Пример: получение дампа хранилища](#пример-получение-дампа-хранилища)
- [Пример: построение хранилища из дампа](#пример-построение-хранилища-из-дампа)
- [Как добавить библиотеку в ваш проект](#как-добавить-библиотеку-в-ваш-проект)
- [Дополнительно](#дополнительно)

Expand All @@ -31,7 +37,7 @@

**Важно:** для типа-значения *value_type* должны быть определены операции равенства и не равенства (*operator==() / operator!=()*).

Добавление и поиск элемента:
### Пример: добавление и поиск элемента
```c++
// инициализация хранилища с максимальным размером 4
kvstor::storage_t<int, std::string> stor{ 4 };
Expand All @@ -57,7 +63,7 @@ item 2 has value: second again
```
Удаление элемента:
### Пример: удаление элемента
```c++
kvstor::storage_t<int, std::string> stor{ 10 };
stor.push(1, "first");
Expand All @@ -75,7 +81,7 @@ item 2 does not exist
```


Добавление элемента с проверкой, что значение элемента не изменилось с прошлого обращения:
### Пример: добавление элемента с проверкой, что значение элемента не изменилось с прошлого обращения

```c++
kvstor::storage_t<int, std::string> stor{ 10 };
Expand All @@ -92,7 +98,7 @@ flag = stor.compare_exchange(1, "11", expected);
```
Печать элементов хранилища:
### Пример: печать элементов хранилища
```c++
using stor_t = kvstor::storage_t<long, std::string>;
stor_t stor{ 4 };
Expand Down Expand Up @@ -124,6 +130,49 @@ std::cout << "}" << std::endl;
Обратите внимание, что элементы идут в обратном порядке попадания в хранилище (наиболее новые сначала).


### Пример: получение дампа хранилища
```c++
kvstor::storage_t<int, int> stor{ 10 };
stor.push(1, 10);
stor.push(2, 20);
stor.push(3, 30);
stor.push(4, 40);

const std::vector<std::pair<int, int>> & dump = stor.dump();
for (const auto & [key, value] : dump)
std::cout << key << "\t" << value << std::endl;
```
В результате выполнения будет выведено:
```
4 40
3 30
2 20
1 10
```
### Пример: построение хранилища из дампа
```c++
const std::vector<std::pair<int, int>> dump = { {5, 50}, {4, 40}, {3, 30}, {2, 20}, {1, 10}, };
const kvstor::storage_t<int, int> stor{ dump, 3 };
auto print = [](int key, int value)
{
std::cout << key << "\t" << value << std::endl;
};
stor.map(print);
```
В результате выполнения будет выведено:
```
5 50
4 40
3 30
```
Максимальный размер хранилища ограничен 3 элементами, поэтому будет добавлено только 3 первых элемента дампа.



## Как добавить библиотеку в ваш проект
Весь код библиотеки содержится в одном файле `include/kvstor.h`. Наиболее простой способ добавления библиотеки в ваш проект:
- скопировать файл `kvstor.h` в удобное для вас место, например: `third_party/kvstor/kvstor.h`
Expand Down
55 changes: 55 additions & 0 deletions include/kvstor.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <mutex>
#include <optional>
#include <unordered_map>
#include <vector>


namespace kvstor
Expand Down Expand Up @@ -40,6 +41,7 @@ namespace kvstor
using kequal_t = typename traits_type::kequal_t;

explicit storage_t(size_t max_size) noexcept;
storage_t(const std::vector<std::pair<key_t, value_t>> & dump_data, size_t max_size);
storage_t(const storage_t &) = delete;
storage_t(storage_t &&) = delete;
~storage_t() noexcept;
Expand Down Expand Up @@ -67,6 +69,8 @@ namespace kvstor
void erase(const key_t& key);
void clear() noexcept;

std::vector<std::pair<key_t, value_t>> dump() const;

private:
struct item_t
{
Expand Down Expand Up @@ -95,6 +99,7 @@ namespace kvstor

void apply_new(const key_t & key, value_t && value, typename index_t::iterator found);
void fix_size();
void build_from_dump(const std::vector<std::pair<key_t, value_t>> & dump_data);
bool compare_with(typename index_t::iterator found, std::optional<value_t> & expected);

list_t m_data;
Expand All @@ -118,6 +123,18 @@ namespace kvstor
}


template <class key_type, class value_type, class traits_type>
inline storage_t<key_type, value_type, traits_type>::storage_t
(
const std::vector<std::pair<key_t, value_t>> & dump_data,
size_t max_size
)
: storage_t(max_size)
{
build_from_dump(dump_data);
}


template <class key_type, class value_type, class traits_type>
inline storage_t<key_type, value_type, traits_type>::~storage_t() noexcept
{
Expand Down Expand Up @@ -300,6 +317,23 @@ namespace kvstor
}


template <class key_type, class value_type, class traits_type>
std::vector<std::pair<key_type, value_type>> storage_t<key_type, value_type, traits_type>::dump() const
{
std::vector<std::pair<key_t, value_t>> dump_data;
dump_data.reserve(size());

auto do_dump = [&dump_data](key_t key, const value_t& value)
{
dump_data.emplace_back(key, value);
};

map(do_dump);

return dump_data;
}


template <class key_type, class value_type, class traits_type>
void storage_t<key_type, value_type, traits_type>::apply_new
(
Expand Down Expand Up @@ -337,6 +371,27 @@ namespace kvstor
}


template <class key_type, class value_type, class traits_type>
void storage_t<key_type, value_type, traits_type>::build_from_dump
(
const std::vector<std::pair<key_t, value_t>> & dump_data
)
{
const size_t dump_size = dump_data.size();
const size_t offset = dump_size < m_max_size ? 0 : dump_size - m_max_size;

for (auto it = dump_data.rbegin() + offset; it < dump_data.rend(); ++it)
{
const key_type & key = it->first;
value_t value = it->second;
const auto found = m_index.find(key);

apply_new(key, std::move(value), found);
fix_size();
}
}


template <class key_type, class value_type, class traits_type>
bool storage_t<key_type, value_type, traits_type>::compare_with
(
Expand Down
94 changes: 93 additions & 1 deletion tests/kvstor_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ TEST_CASE("kvstor::map()")

const list_t expected_1 = { {4, 40}, {3, 30}, {2, 20}, {1, 10}, };

kvstor::storage_t<int, int> stor{ 10 };
stor_t stor{ 10 };
stor.push(1, 10);
stor.push(2, 20);
stor.push(3, 30);
Expand All @@ -254,6 +254,98 @@ TEST_CASE("kvstor::map()")
}


TEST_CASE("kvstor::dump()")
{
using arr_t = std::vector<std::pair<int, int>>;
using stor_t = kvstor::storage_t<int, int>;

const arr_t expected = { {4, 40}, {3, 30}, {2, 20}, {1, 10}, };
stor_t stor{ 10 };

const arr_t empty_dump = stor.dump();
REQUIRE(empty_dump.empty());

stor.push(1, 10);
stor.push(2, 20);
stor.push(3, 30);
stor.push(4, 40);

const arr_t dump_data = stor.dump();
REQUIRE(dump_data == expected);
}


TEST_CASE("kvstor::dump & build a zero storage")
{
using arr_t = std::vector<std::pair<int, int>>;
using stor_t = kvstor::storage_t<int, int>;

stor_t stor_0{ 0 };

const arr_t empty_dump = stor_0.dump();
REQUIRE(empty_dump.empty());

stor_0.push(1, 10);
stor_0.push(2, 20);
stor_0.push(3, 30);

const arr_t dump_data = stor_0.dump();
REQUIRE(dump_data.empty());

const arr_t dum_data = { {4, 40}, {3, 30}, {2, 20}, {1, 10}, };
const stor_t stor_0_new(dump_data, 0);
REQUIRE(stor_0_new.empty());
}


TEST_CASE("kvstor::build_from_dump()")
{
using arr_t = std::vector<std::pair<int, int>>;
using stor_t = kvstor::storage_t<int, int>;

const arr_t dump_data = { {4, 40}, {3, 30}, {2, 20}, {1, 10}, };

const stor_t stor_10{ dump_data, 10 };
REQUIRE(stor_10.size() == dump_data.size());

bool is_equal = true;
size_t index = 0;
auto compare = [&dump_data, &index, &is_equal](int key, const int& value)
{
const auto& item = dump_data.at(index);

if (item.first != key || item.second != value)
is_equal = false;

++index;
};

stor_10.map(compare);
REQUIRE(is_equal);

const stor_t stor_4{ dump_data, 4 };
REQUIRE(stor_4.size() == dump_data.size());

is_equal = true;
index = 0;

stor_4.map(compare);
REQUIRE(is_equal);

const stor_t stor_2{ dump_data, 2 };
REQUIRE(stor_2.size() == 2);

is_equal = true;
index = 0;

stor_2.map(compare);
REQUIRE(is_equal);

const stor_t stor_10_empty{ arr_t{}, 10 };
REQUIRE(stor_10_empty.empty());
}


TEST_CASE("kvstor thread-safe")
{
constexpr size_t max_count = 20000;
Expand Down

0 comments on commit 3a54bb2

Please sign in to comment.