diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4bc6c63..9acd1e3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,6 +19,18 @@ target_link_libraries(fragment_store PUBLIC ######################################## +add_library(message_fragment_store + ./fragment_store/message_fragment_store.hpp + ./fragment_store/message_fragment_store.cpp +) +target_compile_features(message_fragment_store PRIVATE cxx_std_20) +target_link_libraries(message_fragment_store PUBLIC + fragment_store + solanaceae_message3 +) + +######################################## + add_executable(fragment_store_test fragment_store/test_fragstore.cpp ) @@ -112,6 +124,7 @@ target_link_libraries(tomato PUBLIC solanaceae_tox_messages fragment_store + message_fragment_store SDL3::SDL3 diff --git a/src/fragment_store/message_fragment_store.cpp b/src/fragment_store/message_fragment_store.cpp new file mode 100644 index 0000000..2f1324c --- /dev/null +++ b/src/fragment_store/message_fragment_store.cpp @@ -0,0 +1,131 @@ +#include "./message_fragment_store.hpp" + +#include + +#include + +#include +#include + +static bool serl_json_msg_ts_range(void* comp, nlohmann::json& out) { + if (comp == nullptr) { + return false; + } + + out = nlohmann::json::object(); + + auto& r_comp = *reinterpret_cast(comp); + + out["begin"] = r_comp.begin; + out["end"] = r_comp.end; + + return true; +} + +void MessageFragmentStore::handleMessage(const Message3Handle& m) { + if (!static_cast(m)) { + return; // huh? + } + + if (!m.all_of()) { + return; // we only handle msg with ts + } + + const auto msg_ts = m.get().ts; + + if (!m.all_of()) { + // missing fuid + // find closesed non-sealed off fragment + + std::vector fragment_uid; + + // first search for fragment where the ts falls into the range + for (const auto& [tsrage, fid] : _fuid_open) { + if (tsrage.ts_begin <= msg_ts && tsrage.ts_end >= msg_ts) { + fragment_uid = fid; + // TODO: check conditions for open here + // TODO: mark msg (and frag?) dirty + } + } + + // if it did not fit into an existing fragment, we next look for fragments that could be extended + if (fragment_uid.empty()) { + for (const auto& [tsrage, fid] : _fuid_open) { + const int64_t frag_range = int64_t(tsrage.ts_end) - int64_t(tsrage.ts_begin); + //constexpr static int64_t max_frag_ts_extent {1000*60*60}; + constexpr static int64_t max_frag_ts_extent {1000*60*3}; // 3min for testing + const int64_t possible_extention = max_frag_ts_extent - frag_range; + + // which direction + if ((tsrage.ts_begin - possible_extention) <= msg_ts) { + fragment_uid = fid; + auto fh = _fs.fragmentHandle(_fs.getFragmentByID(fid)); + assert(static_cast(fh)); + + // assuming ts range exists + auto& fts_comp = fh.get(); + fts_comp.begin = msg_ts; // extend into the past + + // TODO: check conditions for open here + // TODO: mark msg (and frag?) dirty + } else if ((tsrage.ts_end + possible_extention) >= msg_ts) { + fragment_uid = fid; + auto fh = _fs.fragmentHandle(_fs.getFragmentByID(fid)); + assert(static_cast(fh)); + + // assuming ts range exists + auto& fts_comp = fh.get(); + fts_comp.end = msg_ts; // extend into the future + + // TODO: check conditions for open here + // TODO: mark msg (and frag?) dirty + } + } + } + + // if its still not found, we need a new fragment + if (fragment_uid.empty()) { + } + + // if this is still empty, something is very wrong and we exit here + if (fragment_uid.empty()) { + return; + } + + m.emplace(fragment_uid); + } + + // TODO: do we use fid? + + // on new message: assign fuid + // on new and update: mark as fragment dirty +} + +MessageFragmentStore::MessageFragmentStore( + RegistryMessageModel& rmm, + FragmentStore& fs +) : _rmm(rmm), _fs(fs) { + _rmm.subscribe(this, RegistryMessageModel_Event::message_construct); + _rmm.subscribe(this, RegistryMessageModel_Event::message_updated); + _rmm.subscribe(this, RegistryMessageModel_Event::message_destroy); + + _fs._sc.registerSerializerJson(serl_json_msg_ts_range); +} + +MessageFragmentStore::~MessageFragmentStore(void) { +} + +float MessageFragmentStore::tick(float time_delta) { + return 1000.f*60.f*60.f; +} + +bool MessageFragmentStore::onEvent(const Message::Events::MessageConstruct& e) { + handleMessage(e.e); + return false; +} + +bool MessageFragmentStore::onEvent(const Message::Events::MessageUpdated& e) { + handleMessage(e.e); + return false; +} + diff --git a/src/fragment_store/message_fragment_store.hpp b/src/fragment_store/message_fragment_store.hpp new file mode 100644 index 0000000..aca4d50 --- /dev/null +++ b/src/fragment_store/message_fragment_store.hpp @@ -0,0 +1,66 @@ +#pragma once + +#include "./meta_components.hpp" +#include "./fragment_store_i.hpp" +#include "./fragment_store.hpp" + +#include +#include +#include + +#include + +#include + +namespace Message::Components { + + using FUID = FragComp::ID; + + struct FID { + FragmentID fid {entt::null}; + }; + +} // Message::Components + +namespace Fragment::Components { + struct MessagesTSRange { + // timestamp range within the fragment + uint64_t begin {0}; + uint64_t end {0}; + }; +} // Fragment::Components + +// handles fragments for messages +// on new message: assign fuid +// on new and update: mark as fragment dirty +// on delete: mark as fragment dirty? +class MessageFragmentStore : public RegistryMessageModelEventI { + protected: + RegistryMessageModel& _rmm; + FragmentStore& _fs; + + void handleMessage(const Message3Handle& m); + + struct TSRange final { + uint64_t ts_begin {0}; + uint64_t ts_end {0}; + }; + // only contains fragments with <1024 messages and <28h tsrage + std::map> _fuid_open; + + std::map> _fuid_save_queue; + + public: + MessageFragmentStore( + RegistryMessageModel& rmm, + FragmentStore& fs + ); + virtual ~MessageFragmentStore(void); + + float tick(float time_delta); + + protected: // rmm + bool onEvent(const Message::Events::MessageConstruct& e) override; + bool onEvent(const Message::Events::MessageUpdated& e) override; +}; + diff --git a/src/main_screen.cpp b/src/main_screen.cpp index 9db04a3..9e00875 100644 --- a/src/main_screen.cpp +++ b/src/main_screen.cpp @@ -14,6 +14,7 @@ MainScreen::MainScreen(SDL_Renderer* renderer_, std::string save_path, std::stri renderer(renderer_), rmm(cr), mts(rmm), + mfs(rmm, fs), tc(save_path, save_password), tpi(tc.getTox()), ad(tc), diff --git a/src/main_screen.hpp b/src/main_screen.hpp index aabcb51..39baf7e 100644 --- a/src/main_screen.hpp +++ b/src/main_screen.hpp @@ -7,6 +7,7 @@ #include #include #include +#include "./fragment_store/message_fragment_store.hpp" #include #include #include "./tox_private_impl.hpp" @@ -50,6 +51,7 @@ struct MainScreen final : public Screen { Contact3Registry cr; RegistryMessageModel rmm; MessageTimeSort mts; + MessageFragmentStore mfs; ToxEventLogger tel{std::cout}; ToxClient tc;