diff --git a/libmemdb/.gitignore b/libmemdb/.gitignore new file mode 100644 index 0000000..f152158 --- /dev/null +++ b/libmemdb/.gitignore @@ -0,0 +1,69 @@ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Linker files +*.ilk + +# Debugger Files +*.pdb + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll +*.so.* + + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# Build directories +build/ +Build/ +build-*/ + +# CMake generated files +CMakeFiles/ +CMakeCache.txt +cmake_install.cmake +#Makefile +install_manifest.txt +compile_commands.json + +# Temporary files +*.tmp +*.log +*.bak +*.swp + +# vcpkg +vcpkg_installed/ + +# debug information files +*.dwo + +# test output & cache +Testing/ +.cache/ diff --git a/libmemdb/Makefile b/libmemdb/Makefile new file mode 100644 index 0000000..43ecf59 --- /dev/null +++ b/libmemdb/Makefile @@ -0,0 +1,25 @@ +VERSION := 1.0.0 +FRAMEWORK_NAME := us +LIB_NAME := libus_memdb +INSTL_TDIR := /lib +INCLU_TDIR := /usr/include +INCLU_SDIR := inc +CPP := g++ +INCS := -I inc +CPPFLAGS := --std=c++20 -O3 -pipe -Wall -Werror +LIBS := +BUILD_TDIR := build +$(shell mkdir -p $(BUILD_TDIR)) + + + +install: + @echo "install UniServe_libmemdb..." + @mkdir -vp $(INCLU_TDIR)/${FRAMEWORK_NAME} + @cp -vf $(INCLU_SDIR)/$(FRAMEWORK_NAME)/memdb.hpp $(INCLU_TDIR)/$(FRAMEWORK_NAME)/ + @echo "done." + +uninstall: + @echo "uninstall UniServe_libmemdb..." + @rm -vf $(INCLU_TDIR)/$(FRAMEWORK_NAME)/memdb.hpp + @echo "done." \ No newline at end of file diff --git a/libmemdb/README.md b/libmemdb/README.md new file mode 100644 index 0000000..f6805f9 --- /dev/null +++ b/libmemdb/README.md @@ -0,0 +1,5 @@ +# UniServe_libmemdb +UniServe Framework memdb Library + +# Dependancy +libmemdb-dev diff --git a/libmemdb/_set.sh b/libmemdb/_set.sh new file mode 100644 index 0000000..d3c371b --- /dev/null +++ b/libmemdb/_set.sh @@ -0,0 +1,3 @@ +#!/bin/bash +cd "$(dirname "$0")" || exit +make install \ No newline at end of file diff --git a/libmemdb/inc/us/memdb.hpp b/libmemdb/inc/us/memdb.hpp new file mode 100644 index 0000000..cc8b48a --- /dev/null +++ b/libmemdb/inc/us/memdb.hpp @@ -0,0 +1,156 @@ +#pragma once +#include +#include +#include +#include +#include +#include + + + +namespace shufeng2012 { + + + +template + +class Memdb { + + +struct memdb_column { + T key; + U value; + std::chrono::system_clock::time_point timestamp; +}; + +struct index_keys { + T key; + std::list ::iterator item; +}; + +struct index_timestamp { + std::list ::iterator item; + + bool operator<(const index_timestamp& a) const { + return this -> item -> timestamp < a.item -> timestamp; + } +}; + + +private: + std::list db; + std::unordered_map keys; + std::multiset time_touch; + size_t __size = 0; + int MAX_TIME = 60 * 10; // 10分钟 + void flush(); + + + +public: + using key_t = T; + using value_t = U; + static constexpr bool fail = false; + static constexpr bool succ = true; + + + + Memdb() = default; + Memdb(int max_time) : MAX_TIME(std::max(0, max_time)) {} + Memdb& operator=(const Memdb& to_be_cp_memdb) = default; + ~Memdb() = default; + + + size_t size() const { + return this -> __size; + } + + const std::unordered_map & get_keys() const { + return this -> keys; + } + + const std::list & get_db() const { + return this -> db; + } + + const std::multiset ::index_timestamp>& get_time_touch() const { + return this -> time_touch; + } + + + bool insert(key_t to_be_ins_k, value_t ins_v) { + this -> flush(); + if (this -> keys.find(to_be_ins_k) != this -> keys.end()) { + return this -> fail; + } + + std::chrono::system_clock::time_point now_time = std::chrono::system_clock::now(); + + Memdb::memdb_column new_column; + new_column.key = to_be_ins_k; + new_column.value = ins_v; + new_column.timestamp = now_time + std::chrono::seconds(MAX_TIME); + this -> db.push_back(new_column); + + Memdb::index_keys new_key; + new_key.key = to_be_ins_k; + new_key.item = std::prev(this -> db.end()); + this -> keys[to_be_ins_k] = new_key; + + Memdb::index_timestamp new_time_touch; + new_time_touch.item = std::prev(this -> db.end()); + this -> time_touch.insert(new_time_touch); + + this -> __size++; + + return this -> succ; + } + + bool remove(key_t to_be_del_k) { + this -> flush(); + if (this -> keys.find(to_be_del_k) == this -> keys.end()) { + return this -> fail; + } + + typename std::list ::iterator it = this -> keys[to_be_del_k].item; + this -> keys.erase(it -> key); + this -> time_touch.erase({it}); + this -> db.erase(it); + this -> __size--; + + return this -> succ; + } + + bool update(key_t to_be_udt_k, value_t udt_v, bool need_to_udt_timetouch=false) { + this -> flush(); + if (this -> keys.find(to_be_udt_k) == this -> keys.end()) { + return this -> fail; + } + + typename std::list ::iterator it = this -> keys[to_be_udt_k].item; + it -> value = udt_v; + if (need_to_udt_timetouch == true) { + std::chrono::system_clock::time_point now_time = std::chrono::system_clock::now(); + T copy_k = it -> key; + U copy_v = it -> value; + this -> remove(copy_k); + this -> insert(copy_k, copy_v); + } + + return this -> succ; + } + + std::optional search(key_t to_be_sch_k) { + this -> flush(); + if (this -> keys.find(to_be_sch_k) == this -> keys.end()) { + return std::nullopt; + } + + typename std::list ::iterator it = this -> keys[to_be_sch_k].item; + return it -> value; + } +}; + + + +} \ No newline at end of file diff --git a/memdb/.gitignore b/memdb/.gitignore deleted file mode 100644 index ec6b25a..0000000 --- a/memdb/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.bin -*.obj -build/ \ No newline at end of file diff --git a/memdb/Makefile b/memdb/Makefile deleted file mode 100644 index f51dc0b..0000000 --- a/memdb/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -test: src/blackbox_test.cpp src/main/memdb.cpp include/memdb.hpp - g++ src/blackbox_test.cpp -o test.bin -I include/ -std=c++20 -O3 -g \ No newline at end of file diff --git a/memdb/include/memdb.hpp b/memdb/include/memdb.hpp deleted file mode 100644 index 26c6ed0..0000000 --- a/memdb/include/memdb.hpp +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include - - - -namespace shufeng2012 { - -template - -class Memdb { - - -struct memdb_column { - T key; - U value; - std::chrono::system_clock::time_point timestamp; -}; - -struct index_keys { - T key; - std::list ::iterator item; -}; - -struct index_timestamp { - std::list ::iterator item; - - bool operator<(const index_timestamp& a) const; -}; - - -private: - std::list db; - std::unordered_map keys; - std::multiset time_touch; - size_t __size = 0; - int MAX_TIME = 60 * 10; // 10分钟 - void flush(); - - -public: - using key_t = T; - using value_t = U; - static constexpr bool fail = false; - static constexpr bool succ = true; - - Memdb(); - Memdb(int max_time); - Memdb& operator=(const Memdb& to_be_cp_memdb); - ~Memdb(); - - size_t size() const; - const std::unordered_map & get_keys() const; - const std::list & get_db() const; - const std::multiset ::index_timestamp>& get_time_touch() const; - - bool insert(key_t to_be_ins_k, value_t ins_v); - bool remove(key_t to_be_del_k); - bool update(key_t to_be_udt_k, value_t udt_v, bool need_to_udt_timetouch=false); - std::optional search(key_t to_be_sch_k); -}; -} \ No newline at end of file diff --git a/memdb/src/blackbox_test.cpp b/memdb/src/blackbox_test.cpp deleted file mode 100644 index 8a98e7e..0000000 --- a/memdb/src/blackbox_test.cpp +++ /dev/null @@ -1,291 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -// 使用 C++20 的时间字面量 -using namespace std::chrono_literals; -using namespace shufeng2012; - -#include "main/memdb.cpp" - -// ----- 简易测试框架 ----- -static int g_test_passed = 0; -static int g_test_failed = 0; - -#define TEST(name) \ - void test_##name(); \ - struct TestReg_##name { \ - TestReg_##name() { register_test(#name, test_##name); } \ - } reg_##name; \ - void test_##name() - -using TestFunc = std::function; -std::vector> tests; - -void register_test(const std::string& name, TestFunc fn) { - tests.push_back({name, fn}); -} - -// 断言宏(支持 C++20 的 constexpr 字符串,但这里不变) -#define EXPECT_TRUE(expr) \ - do { if (!(expr)) { std::cerr << " [FAIL] " #expr << " is false\n"; ++g_test_failed; return; } } while(0) - -#define EXPECT_FALSE(expr) EXPECT_TRUE(!(expr)) - -#define EXPECT_EQ(expected, actual) \ - do { if ((expected) != (actual)) { std::cerr << " [FAIL] Expected: " << (expected) << ", Actual: " << (actual) << "\n"; ++g_test_failed; return; } } while(0) - -#define EXPECT_OPTIONAL_EQ(expected, opt) \ - do { \ - if (!(opt).has_value()) { std::cerr << " [FAIL] optional has no value\n"; ++g_test_failed; return; } \ - if ((expected) != (opt).value()) { std::cerr << " [FAIL] Expected: " << (expected) << ", Actual: " << (opt).value() << "\n"; ++g_test_failed; return; } \ - } while(0) - -#define EXPECT_NO_VALUE(opt) \ - do { if ((opt).has_value()) { std::cerr << " [FAIL] optional should have no value, but has: " << (opt).value() << "\n"; ++g_test_failed; return; } } while(0) - -// ----- 测试用例 ----- - -// 1. 默认构造 -TEST(DefaultConstruction) { - Memdb db; - EXPECT_EQ(0u, db.size()); - EXPECT_TRUE(db.get_db().empty()); - EXPECT_TRUE(db.get_keys().empty()); - EXPECT_TRUE(db.get_time_touch().empty()); - std::cout << " [PASS]\n"; - ++g_test_passed; -} - -// 2. 插入与查询 -TEST(InsertAndSearch) { - Memdb db(10); - EXPECT_TRUE(db.insert(1, "one")); - EXPECT_EQ(1u, db.size()); - auto opt = db.search(1); - EXPECT_OPTIONAL_EQ("one", opt); - std::cout << " [PASS]\n"; - ++g_test_passed; -} - -// 3. 重复插入失败 -TEST(InsertDuplicateFails) { - Memdb db; - EXPECT_TRUE(db.insert(1, "one")); - EXPECT_FALSE(db.insert(1, "another")); - EXPECT_EQ(1u, db.size()); - auto opt = db.search(1); - EXPECT_OPTIONAL_EQ("one", opt); - std::cout << " [PASS]\n"; - ++g_test_passed; -} - -// 4. 查询不存在的键 -TEST(SearchNonExistent) { - Memdb db; - auto opt = db.search(42); - EXPECT_NO_VALUE(opt); - std::cout << " [PASS]\n"; - ++g_test_passed; -} - -// 5. 删除存在的键 -TEST(RemoveExisting) { - Memdb db; - db.insert(1, "one"); - EXPECT_TRUE(db.remove(1)); - EXPECT_EQ(0u, db.size()); - auto opt = db.search(1); - EXPECT_NO_VALUE(opt); - std::cout << " [PASS]\n"; - ++g_test_passed; -} - -// 6. 删除不存在的键 -TEST(RemoveNonExistentFails) { - Memdb db; - EXPECT_FALSE(db.remove(99)); - EXPECT_EQ(0u, db.size()); - std::cout << " [PASS]\n"; - ++g_test_passed; -} - -// 7. 删除后重新插入 -TEST(RemoveThenReinsert) { - Memdb db; - db.insert(1, "one"); - db.remove(1); - EXPECT_TRUE(db.insert(1, "new")); - EXPECT_EQ(1u, db.size()); - auto opt = db.search(1); - EXPECT_OPTIONAL_EQ("new", opt); - std::cout << " [PASS]\n"; - ++g_test_passed; -} - -// 8. 更新(不触摸) -TEST(UpdateWithoutTouch) { - Memdb db(10); - db.insert(1, "one"); - EXPECT_TRUE(db.update(1, "updated", false)); - EXPECT_EQ(1u, db.size()); - auto opt = db.search(1); - EXPECT_OPTIONAL_EQ("updated", opt); - std::cout << " [PASS]\n"; - ++g_test_passed; -} - -// 9. 更新(触摸)延长寿命 —— 使用 C++20 字面量 -TEST(UpdateWithTouchExtendsLifetime) { - const int TTL = 3; - Memdb db(TTL); - db.insert(1, "one"); - - // 等待 1.5 秒(剩余寿命约 1.5 秒) - std::this_thread::sleep_for(1500ms); - - // 触摸更新,重置寿命为从当前起 TTL 秒 - EXPECT_TRUE(db.update(1, "touched", true)); - - // 再等待 2.5 秒(从触摸起 2.5 < TTL,应存活) - std::this_thread::sleep_for(2500ms); - - db.search(999); // 触发 flush - auto opt = db.search(1); - EXPECT_TRUE(opt.has_value()); - EXPECT_OPTIONAL_EQ("touched", opt); - EXPECT_EQ(1u, db.size()); - - std::cout << " [PASS]\n"; - ++g_test_passed; -} - -// 10. 更新不存在的键 -TEST(UpdateNonExistentFails) { - Memdb db; - EXPECT_FALSE(db.update(2, "nope")); - EXPECT_EQ(0u, db.size()); - std::cout << " [PASS]\n"; - ++g_test_passed; -} - -// 11. 过期后自动清除 -TEST(ExpirationAfterTimeout) { - const int TTL = 2; - Memdb db(TTL); - db.insert(1, "expire"); - - // 等待 TTL + 1 秒(保证超过) - std::this_thread::sleep_for(std::chrono::seconds(TTL + 1)); - - db.search(999); // 触发 flush - EXPECT_EQ(0u, db.size()); - EXPECT_NO_VALUE(db.search(1)); - - std::cout << " [PASS]\n"; - ++g_test_passed; -} - -// 12. 未过期前可查询 -TEST(NotExpiredBeforeTimeout) { - const int TTL = 2; - Memdb db(TTL); - db.insert(1, "alive"); - std::this_thread::sleep_for(1s); - EXPECT_EQ(1u, db.size()); - auto opt = db.search(1); - EXPECT_OPTIONAL_EQ("alive", opt); - std::cout << " [PASS]\n"; - ++g_test_passed; -} - -// 13. 更新不触摸不延长寿命 -TEST(UpdateWithoutTouchDoesNotExtend) { - const int TTL = 3; - Memdb db(TTL); - db.insert(1, "one"); - - // 等待 1.5 秒(剩余 1.5 秒) - std::this_thread::sleep_for(1500ms); - - // 更新但不触摸,过期时间不变 - EXPECT_TRUE(db.update(1, "updated", false)); - - // 再等待 2 秒(总休眠 3.5 > TTL,应过期) - std::this_thread::sleep_for(2000ms); - - db.search(999); - EXPECT_EQ(0u, db.size()); - EXPECT_NO_VALUE(db.search(1)); - - std::cout << " [PASS]\n"; - ++g_test_passed; -} - -// 14. 生存时间为 0,立即过期 -TEST(ZeroLifetimeExpiresImmediately) { - Memdb db(0); - db.insert(1, "zero"); - std::this_thread::sleep_for(10ms); - db.search(999); - EXPECT_EQ(0u, db.size()); - EXPECT_NO_VALUE(db.search(1)); - std::cout << " [PASS]\n"; - ++g_test_passed; -} - -// 15. 混合操作一致性 -TEST(MixedInsertDeleteSize) { - Memdb db; - db.insert(1, "a"); - db.insert(2, "b"); - db.insert(3, "c"); - db.remove(2); - db.insert(4, "d"); - EXPECT_EQ(3u, db.size()); - EXPECT_TRUE(db.search(1).has_value()); - EXPECT_FALSE(db.search(2).has_value()); - EXPECT_TRUE(db.search(3).has_value()); - EXPECT_TRUE(db.search(4).has_value()); - std::cout << " [PASS]\n"; - ++g_test_passed; -} - -// 16. 批量过期 -TEST(BulkExpirationClearsSize) { - const int TTL = 1; - Memdb db(TTL); - for (int i = 0; i < 100; ++i) { - db.insert(i, std::to_string(i)); - } - EXPECT_EQ(100u, db.size()); - std::this_thread::sleep_for(std::chrono::seconds(TTL + 1)); - db.search(-1); - EXPECT_EQ(0u, db.size()); - for (int i = 0; i < 100; ++i) { - EXPECT_FALSE(db.search(i).has_value()); - } - std::cout << " [PASS]\n"; - ++g_test_passed; -} - -// ----- 程序入口 ----- -int main() { - std::cout << "Memdb Black-box Tests (C++20, no external libs)\n"; - std::cout << "================================================\n"; - - for (auto& [name, fn] : tests) { - std::cout << "Test: " << name << "\n"; - fn(); - } - - std::cout << "================================================\n"; - std::cout << "Passed: " << g_test_passed << ", Failed: " << g_test_failed << "\n"; - - return g_test_failed > 0 ? 1 : 0; -} \ No newline at end of file diff --git a/memdb/src/main.cpp b/memdb/src/main.cpp deleted file mode 100644 index 23e8a44..0000000 --- a/memdb/src/main.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include "main/memdb.cpp" -using namespace std; -Memdb testdb(10); -int main(){ - testdb.insert_("key1","value1"); - auto pack=testdb.search_("key1"); - cout<<(pack==nullopt?"未找到":pack.value())< - -bool Memdb ::index_timestamp::operator<(const index_timestamp& a) const { - return this -> item -> timestamp < a.item -> timestamp; -} - - -template - -Memdb ::Memdb() = default; - - -template - -Memdb ::Memdb(int max_time) : MAX_TIME(std::max(0, max_time)) {} - - -template - -Memdb & Memdb ::operator=(const Memdb & to_be_cp_memdb) = default; - - -template - -Memdb ::~Memdb() = default; - - -template - -size_t Memdb ::size() const { - return this -> __size; -} - - -template - -const std::unordered_map ::index_keys>& Memdb ::get_keys() const { - return this -> keys; -} - - -template - -const std::list ::memdb_column>& Memdb ::get_db() const { - return this -> db; -} - - -/* -内测版 -*/ - - -template - -void Memdb ::flush() { - if (this -> time_touch.empty() == true) { - return; - } - std::chrono::system_clock::time_point now_time = std::chrono::system_clock::now(); - typename std::list ::iterator it = this -> time_touch.begin() -> item; - - while (now_time >= it -> timestamp) { - this -> __size--; - key_t to_be_del_k = it -> key; - this -> db.erase(it); - this -> keys.erase(to_be_del_k); - this -> time_touch.erase(this -> time_touch.begin()); - - if (this -> time_touch.empty() == true) { - return; - } - it = this -> time_touch.begin() -> item; - now_time = std::chrono::system_clock::now(); - } -} - - -template - -const std::multiset ::index_timestamp>& Memdb ::get_time_touch() const { - return this -> time_touch; -} - - -template - -bool Memdb ::insert(key_t to_be_ins_k, value_t ins_v) { - this -> flush(); - if (this -> keys.find(to_be_ins_k) != this -> keys.end()) { - return this -> fail; - } - - std::chrono::system_clock::time_point now_time = std::chrono::system_clock::now(); - - Memdb::memdb_column new_column; - new_column.key = to_be_ins_k; - new_column.value = ins_v; - new_column.timestamp = now_time + std::chrono::seconds(MAX_TIME); - this -> db.push_back(new_column); - - Memdb::index_keys new_key; - new_key.key = to_be_ins_k; - new_key.item = std::prev(this -> db.end()); - this -> keys[to_be_ins_k] = new_key; - - Memdb::index_timestamp new_time_touch; - new_time_touch.item = std::prev(this -> db.end()); - this -> time_touch.insert(new_time_touch); - - this -> __size++; - - return this -> succ; -} - - -template - -bool Memdb ::remove(key_t to_be_del_k) { - //! 直接删除,不要lazy - this -> flush(); - if (this -> keys.find(to_be_del_k) == this -> keys.end()) { - return this -> fail; - } - - typename std::list ::iterator it = this -> keys[to_be_del_k].item; - // it -> be_del = true; - this -> keys.erase(it -> key); - this -> time_touch.erase({it}); - this -> db.erase(it); - this -> __size--; - - return this -> succ; -} - - -template - -bool Memdb ::update(key_t to_be_udt_k, value_t udt_v, bool need_to_udt_timetouch) { - //! 先删再加 - this -> flush(); - if (this -> keys.find(to_be_udt_k) == this -> keys.end()) { - return this -> fail; - } - - typename std::list ::iterator it = this -> keys[to_be_udt_k].item; - it -> value = udt_v; - if (need_to_udt_timetouch == true) { - std::chrono::system_clock::time_point now_time = std::chrono::system_clock::now(); - T copy_k = it -> key; - U copy_v = it -> value; - this -> remove(copy_k); - this -> insert(copy_k, copy_v); - } - - return this -> succ; -} - - -template - -std::optional Memdb ::search(key_t to_be_sch_k){ - this -> flush(); - if (this -> keys.find(to_be_sch_k) == this -> keys.end()) { - return std::nullopt; - } - - typename std::list ::iterator it = this -> keys[to_be_sch_k].item; - return it -> value; -} - -} \ No newline at end of file