diff --git a/.gitignore b/.gitignore index 180a08d..429c597 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .vs/ out/ CMakePresets.json +CMakeSettings.json diff --git a/README.md b/README.md index ee86604..857f536 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,12 @@ ## Оглавление - [Описание](#описание) - [Примеры использования](#примеры-использования) + - [Пример: добавление и поиск элемента](#пример-добавление-и-поиск-элемента) + - [Пример: удаление элемента](#пример-удаление-элемента) + - [Пример: добавление элемента с проверкой, что значение элемента не изменилось с прошлого обращения](#пример-добавление-элемента-с-проверкой-что-значение-элемента-не-изменилось-с-прошлого-обращения) + - [Пример: печать элементов хранилища](#пример-печать-элементов-хранилища) + - [Пример: получение дампа хранилища](#пример-получение-дампа-хранилища) + - [Пример: построение хранилища из дампа](#пример-построение-хранилища-из-дампа) - [Как добавить библиотеку в ваш проект](#как-добавить-библиотеку-в-ваш-проект) - [Дополнительно](#дополнительно) @@ -31,7 +37,7 @@ **Важно:** для типа-значения *value_type* должны быть определены операции равенства и не равенства (*operator==() / operator!=()*). -Добавление и поиск элемента: +### Пример: добавление и поиск элемента ```c++ // инициализация хранилища с максимальным размером 4 kvstor::storage_t stor{ 4 }; @@ -57,7 +63,7 @@ item 2 has value: second again ``` -Удаление элемента: +### Пример: удаление элемента ```c++ kvstor::storage_t stor{ 10 }; stor.push(1, "first"); @@ -75,7 +81,7 @@ item 2 does not exist ``` -Добавление элемента с проверкой, что значение элемента не изменилось с прошлого обращения: +### Пример: добавление элемента с проверкой, что значение элемента не изменилось с прошлого обращения ```c++ kvstor::storage_t stor{ 10 }; @@ -92,7 +98,7 @@ flag = stor.compare_exchange(1, "11", expected); ``` -Печать элементов хранилища: +### Пример: печать элементов хранилища ```c++ using stor_t = kvstor::storage_t; stor_t stor{ 4 }; @@ -124,6 +130,49 @@ std::cout << "}" << std::endl; Обратите внимание, что элементы идут в обратном порядке попадания в хранилище (наиболее новые сначала). +### Пример: получение дампа хранилища +```c++ + kvstor::storage_t stor{ 10 }; + stor.push(1, 10); + stor.push(2, 20); + stor.push(3, 30); + stor.push(4, 40); + + const std::vector> & 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> dump = { {5, 50}, {4, 40}, {3, 30}, {2, 20}, {1, 10}, }; + const kvstor::storage_t 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` diff --git a/include/kvstor.h b/include/kvstor.h index 4291d24..59f29bb 100644 --- a/include/kvstor.h +++ b/include/kvstor.h @@ -10,6 +10,7 @@ #include #include #include +#include namespace kvstor @@ -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> & dump_data, size_t max_size); storage_t(const storage_t &) = delete; storage_t(storage_t &&) = delete; ~storage_t() noexcept; @@ -67,6 +69,8 @@ namespace kvstor void erase(const key_t& key); void clear() noexcept; + std::vector> dump() const; + private: struct item_t { @@ -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> & dump_data); bool compare_with(typename index_t::iterator found, std::optional & expected); list_t m_data; @@ -118,6 +123,18 @@ namespace kvstor } + template + inline storage_t::storage_t + ( + const std::vector> & dump_data, + size_t max_size + ) + : storage_t(max_size) + { + build_from_dump(dump_data); + } + + template inline storage_t::~storage_t() noexcept { @@ -300,6 +317,23 @@ namespace kvstor } + template + std::vector> storage_t::dump() const + { + std::vector> 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 void storage_t::apply_new ( @@ -337,6 +371,27 @@ namespace kvstor } + template + void storage_t::build_from_dump + ( + const std::vector> & 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 bool storage_t::compare_with ( diff --git a/tests/kvstor_test.cpp b/tests/kvstor_test.cpp index 1f1c55a..0011940 100644 --- a/tests/kvstor_test.cpp +++ b/tests/kvstor_test.cpp @@ -231,7 +231,7 @@ TEST_CASE("kvstor::map()") const list_t expected_1 = { {4, 40}, {3, 30}, {2, 20}, {1, 10}, }; - kvstor::storage_t stor{ 10 }; + stor_t stor{ 10 }; stor.push(1, 10); stor.push(2, 20); stor.push(3, 30); @@ -254,6 +254,98 @@ TEST_CASE("kvstor::map()") } +TEST_CASE("kvstor::dump()") +{ + using arr_t = std::vector>; + using stor_t = kvstor::storage_t; + + 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>; + using stor_t = kvstor::storage_t; + + 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>; + using stor_t = kvstor::storage_t; + + 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;