From 4039239c165fa11ad8f3d6ee311484d1b90d4c3a Mon Sep 17 00:00:00 2001 From: Green Sky Date: Sun, 14 Apr 2024 12:33:17 +0200 Subject: [PATCH] give the message serializer its own repo --- .gitignore | 26 ++++ CMakeLists.txt | 72 +++++++++++ external/CMakeLists.txt | 37 ++++++ src/CMakeLists.txt | 19 +++ .../message3/message_serializer.cpp | 120 ++++++++++++++++++ .../message3/message_serializer.hpp | 87 +++++++++++++ 6 files changed, 361 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 external/CMakeLists.txt create mode 100644 src/CMakeLists.txt create mode 100644 src/solanaceae/message3/message_serializer.cpp create mode 100644 src/solanaceae/message3/message_serializer.hpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..56f48bf --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +.vs/ +*.o +*.swp +~* +*~ +.idea/ +cmake-build-debug/ +cmake-build-debugandtest/ +cmake-build-release/ +*.stackdump +*.coredump +compile_commands.json +/build* +/result* +.clangd +.cache + +.DS_Store +.AppleDouble +.LSOverride + +CMakeLists.txt.user* +CMakeCache.txt + +*.tox +imgui.ini diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..95f8214 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,72 @@ +cmake_minimum_required(VERSION 3.24 FATAL_ERROR) + +# cmake setup begin +project(solanaceae_message_serializer) + +if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + set(SOLANACEAE_MESSAGE_SERIALIZER_STANDALONE ON) +else() + set(SOLANACEAE_MESSAGE_SERIALIZER_STANDALONE OFF) +endif() +message("II SOLANACEAE_MESSAGE_SERIALIZER_STANDALONE " ${SOLANACEAE_MESSAGE_SERIALIZER_STANDALONE}) + +#option(SOLANACEAE_MESSAGE_SERIALIZER_BUILD_PLUGINS "Build the solanaceae_message_serializer plugins" ${SOLANACEAE_MESSAGE_SERIALIZER_STANDALONE}) + +if (SOLANACEAE_MESSAGE_SERIALIZER_STANDALONE) + set(CMAKE_POSITION_INDEPENDENT_CODE ON) + + # defaulting to debug mode, if not specified + if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Debug") + endif() + + # setup my vim ycm :D + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + + # more paths + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") +endif() + +# external libs +add_subdirectory(./external EXCLUDE_FROM_ALL) # before increasing warn levels, sad :( + +if (SOLANACEAE_MESSAGE_SERIALIZER_STANDALONE) + set(CMAKE_CXX_EXTENSIONS OFF) + + # bump up warning levels appropriately for clang, gcc & msvc + if (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU" OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") + add_compile_options( + -Wall -Wextra # Reasonable and standard + -Wpedantic # Warn if non-standard C++ is used + -Wunused # Warn on anything being unused + #-Wconversion # Warn on type conversions that may lose data + #-Wsign-conversion # Warn on sign conversions + -Wshadow # Warn if a variable declaration shadows one from a parent context + ) + + if (NOT WIN32) + #link_libraries(-fsanitize=address) + #link_libraries(-fsanitize=address,undefined) + #link_libraries(-fsanitize-address-use-after-scope) + #link_libraries(-fsanitize=undefined) + endif() + elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") + if (CMAKE_CXX_FLAGS MATCHES "/W[0-4]") + string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") + endif() + endif() + +endif() + +# cmake setup end + +add_subdirectory(./src) + +#if (SOLANACEAE_MESSAGE_SERIALIZER_BUILD_PLUGINS) + #add_subdirectory(./plugins) +#endif() + diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt new file mode 100644 index 0000000..3f94048 --- /dev/null +++ b/external/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.24 FATAL_ERROR) + +include(FetchContent) + +if (NOT TARGET solanaceae_util) + FetchContent_Declare(solanaceae_util + GIT_REPOSITORY https://github.com/Green-Sky/solanaceae_util.git + GIT_TAG master + ) + FetchContent_MakeAvailable(solanaceae_util) +endif() + +if (NOT TARGET solanaceae_contact) + FetchContent_Declare(solanaceae_contact + GIT_REPOSITORY https://github.com/Green-Sky/solanaceae_contact.git + GIT_TAG master + ) + FetchContent_MakeAvailable(solanaceae_contact) +endif() + +if (NOT TARGET solanaceae_message3) + FetchContent_Declare(solanaceae_message3 + GIT_REPOSITORY https://github.com/Green-Sky/solanaceae_message3.git + GIT_TAG master + ) + FetchContent_MakeAvailable(solanaceae_message3) +endif() + +if (NOT TARGET nlohmann_json::nlohmann_json) + FetchContent_Declare(json + URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz + URL_HASH SHA256=d6c65aca6b1ed68e7a182f4757257b107ae403032760ed6ef121c9d55e81757d + EXCLUDE_FROM_ALL + ) + FetchContent_MakeAvailable(json) +endif() + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..a215f10 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.9...3.24 FATAL_ERROR) + +project(solanaceae) + +add_library(solanaceae_message_serializer + ./solanaceae/message3/message_serializer.hpp + ./solanaceae/message3/message_serializer.cpp +) + +target_include_directories(solanaceae_message_serializer PUBLIC .) +target_compile_features(solanaceae_message_serializer PUBLIC cxx_std_17) +target_link_libraries(solanaceae_message_serializer PUBLIC + solanaceae_util + solanaceae_message3 + nlohmann_json::nlohmann_json +) + +######################################## + diff --git a/src/solanaceae/message3/message_serializer.cpp b/src/solanaceae/message3/message_serializer.cpp new file mode 100644 index 0000000..253b62b --- /dev/null +++ b/src/solanaceae/message3/message_serializer.cpp @@ -0,0 +1,120 @@ +#include "./message_serializer.hpp" + +#include +#include + +#include + +#include + +static Contact3 findContactByID(Contact3Registry& cr, const std::vector& id) { + // TODO: id lookup table, this is very inefficent + for (const auto& [c_it, id_it] : cr.view().each()) { + if (id == id_it.data) { + return c_it; + } + } + + return entt::null; +} + +template<> +bool MessageSerializerNJ::component_get_json(MessageSerializerNJ& msc, const Handle h, nlohmann::json& j) { + const Contact3 c = h.get().c; + if (!msc.cr.valid(c)) { + // while this is invalid registry state, it is valid serialization + j = nullptr; + std::cerr << "MSC warning: encountered invalid contact\n"; + return true; + } + + if (!msc.cr.all_of(c)) { + // unlucky, this contact is purely ephemeral + j = nullptr; + std::cerr << "MSC warning: encountered contact without ID\n"; + return true; + } + + j = nlohmann::json::binary(msc.cr.get(c).data); + + return true; +} + +template<> +bool MessageSerializerNJ::component_emplace_or_replace_json(MessageSerializerNJ& msc, Handle h, const nlohmann::json& j) { + if (j.is_null()) { + std::cerr << "MSC warning: encountered null contact\n"; + h.emplace_or_replace(); + return true; + } + + std::vector id; + if (j.is_binary()) { + id = j.get_binary(); + } else { + j["bytes"].get_to(id); + } + + Contact3 other_c = findContactByID(msc.cr, id); + if (!msc.cr.valid(other_c)) { + // create sparse contact with id only + other_c = msc.cr.create(); + msc.cr.emplace_or_replace(other_c, id); + } + + h.emplace_or_replace(other_c); + + // TODO: should we return false if the contact is unknown?? + return true; +} + +template<> +bool MessageSerializerNJ::component_get_json(MessageSerializerNJ& msc, const Handle h, nlohmann::json& j) { + const Contact3 c = h.get().c; + if (!msc.cr.valid(c)) { + // while this is invalid registry state, it is valid serialization + j = nullptr; + std::cerr << "MSC warning: encountered invalid contact\n"; + return true; + } + + if (!msc.cr.all_of(c)) { + // unlucky, this contact is purely ephemeral + j = nullptr; + std::cerr << "MSC warning: encountered contact without ID\n"; + return true; + } + + j = nlohmann::json::binary(msc.cr.get(c).data); + + return true; +} + +template<> +bool MessageSerializerNJ::component_emplace_or_replace_json(MessageSerializerNJ& msc, Handle h, const nlohmann::json& j) { + if (j.is_null()) { + std::cerr << "MSC warning: encountered null contact\n"; + h.emplace_or_replace(); + return true; + } + + std::vector id; + if (j.is_binary()) { + id = j.get_binary(); + } else { + j["bytes"].get_to(id); + } + + Contact3 other_c = findContactByID(msc.cr, id); + if (!msc.cr.valid(other_c)) { + // create sparse contact with id only + other_c = msc.cr.create(); + msc.cr.emplace_or_replace(other_c, id); + } + + h.emplace_or_replace(other_c); + + // TODO: should we return false if the contact is unknown?? + return true; +} + diff --git a/src/solanaceae/message3/message_serializer.hpp b/src/solanaceae/message3/message_serializer.hpp new file mode 100644 index 0000000..65d342d --- /dev/null +++ b/src/solanaceae/message3/message_serializer.hpp @@ -0,0 +1,87 @@ +#pragma once + +#include +#include + +#include + +#include + +struct MessageSerializerNJ { + using Registry = Message3Registry; + using Handle = Message3Handle; + + Contact3Registry& cr; + + // nlohmann + // json/msgpack + using serialize_fn = bool(*)(MessageSerializerNJ& msc, const Handle h, nlohmann::json& out); + entt::dense_map _serl_json; + + using deserialize_fn = bool(*)(MessageSerializerNJ& msc, Handle h, const nlohmann::json& in); + entt::dense_map _deserl_json; + + template + static bool component_get_json(MessageSerializerNJ&, const Handle h, nlohmann::json& j) { + if (h.template all_of()) { + if constexpr (!std::is_empty_v) { + j = h.template get(); + } + return true; + } + + return false; + } + + template + static bool component_emplace_or_replace_json(MessageSerializerNJ&, Handle h, const nlohmann::json& j) { + if constexpr (std::is_empty_v) { + h.template emplace_or_replace(); // assert empty json? + } else { + h.template emplace_or_replace(static_cast(j)); + } + return true; + } + + void registerSerializer(serialize_fn fn, const entt::type_info& type_info) { + _serl_json[type_info.hash()] = fn; + } + + template + void registerSerializer( + serialize_fn fn = component_get_json, + const entt::type_info& type_info = entt::type_id() + ) { + registerSerializer(fn, type_info); + } + + void registerDeserializer(deserialize_fn fn, const entt::type_info& type_info) { + _deserl_json[type_info.hash()] = fn; + } + + template + void registerDeserializer( + deserialize_fn fn = component_emplace_or_replace_json, + const entt::type_info& type_info = entt::type_id() + ) { + registerDeserializer(fn, type_info); + } + + // TODO: deregister +}; + +// fwd +namespace Message::Components { +struct ContactFrom; +struct ContactTo; +} + +// make specializations known +template<> +bool MessageSerializerNJ::component_get_json(MessageSerializerNJ& msc, const Handle h, nlohmann::json& j); +template<> +bool MessageSerializerNJ::component_emplace_or_replace_json(MessageSerializerNJ& msc, Handle h, const nlohmann::json& j); +template<> +bool MessageSerializerNJ::component_get_json(MessageSerializerNJ& msc, const Handle h, nlohmann::json& j); +template<> +bool MessageSerializerNJ::component_emplace_or_replace_json(MessageSerializerNJ& msc, Handle h, const nlohmann::json& j);