diff --git a/.clang-tidy b/.clang-tidy index d65343dd..c21f2118 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -23,6 +23,7 @@ Checks: - -misc-use-internal-linkage - -misc-use-anonymous-namespace - -misc-include-cleaner + - -misc-static-assert - -cppcoreguidelines-pro-type-vararg - -cppcoreguidelines-non-private-member-variables-in-classes - -cppcoreguidelines-special-member-functions @@ -32,6 +33,7 @@ Checks: - -cppcoreguidelines-pro-type-member-init - -cppcoreguidelines-owning-memory - -cppcoreguidelines-avoid-const-or-ref-data-members + - -cppcoreguidelines-avoid-c-arrays - -cppcoreguidelines-macro-usage - -cppcoreguidelines-pro-bounds-constant-array-index - -cppcoreguidelines-pro-bounds-array-to-pointer-decay diff --git a/.opencode/agents/architect.md b/.opencode/agents/architect.md deleted file mode 100644 index 46cc90a2..00000000 --- a/.opencode/agents/architect.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -mode: all -model: openai/gpt-5.5 -variant: high -description: Analyze requirements and produce C++ architecture and implementation instructions -permission: - read: allow - grep: allow - glob: allow - list: allow - edit: deny - task: - "*": deny - explorer: allow - bash: - "*": deny - "git log*": allow - "git diff*": allow ---- - -You are a C++ solution architect. Analyze and design; do not implement, build, or test. - -Discovery: -- Use @explorer for broad discovery when context is missing. -- Read files directly only to verify APIs, invariants, ownership/lifetime, or details needed for precise instructions. -- Do not repeat broad exploration already done by @explorer. - -Goal: -- Produce instructions precise enough for @coder to implement with a cheaper model and no architecture decisions. - -Required output: -1. Risk: High/Medium/Low, approval requirement, and reason. Low: comments, typos, tests only, or isolated implementation bugfix without API/behavior impact. Medium: behavior changes, cross-file refactors, async/task logic, build configuration, dependency configuration, or public headers without ABI concern. High: persistence/state format, crypto/security, ownership/lifetime, public API/ABI, threading/concurrency, cross-platform behavior, or dependency version changes. -2. Blocking Questions: none, or questions blocking implementation. -3. Decision: final design choice, short and direct. -4. Acceptance Criteria: observable behavior required. -5. Coder Packet: - - Files to Modify: exact edit allowlist and add/modify/delete intent. - - Implementation Steps: ordered concrete steps with files, symbols, and signatures where needed. - - Invariants: what must remain true. - - Do NOT Change: only non-obvious protected boundaries, not the rest of the project. - - Escalate If: when @coder must stop and return. - -Rules: -- Do not include build/test commands; @builder and @tester own validation. -- Request user approval for all Medium and High risk changes before implementation. diff --git a/.opencode/agents/builder.md b/.opencode/agents/builder.md deleted file mode 100644 index e878e926..00000000 --- a/.opencode/agents/builder.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -mode: subagent -model: openai/gpt-5.4-mini-fast -variant: low -description: Validate project build and analyze compiler logs -permission: - read: allow - edit: deny - bash: - "*": deny - "ninja*": allow - "cmake*": allow -temperature: 0.1 -steps: 5 ---- - -You are a C++ build validation specialist. - -Responsibilities: -- Run the requested CMake/Ninja build: full build, specific target, or configured build command. -- If the build succeeds, report the command, build directory, and success. -- If the build fails, analyze the full build log and report root-cause errors only. - -Failure analysis rules: -- Collapse cascaded diagnostics into the real underlying issue. -- Group independent failures by file, target, or symbol. -- For each issue, report location, root cause, and brief supporting diagnostic. - -Output: -- End every report with exactly one marker: Build validation: SUCCESS or Build validation: FAILURE. - -Rules: -- Do not edit files. -- Do not fix issues yourself. diff --git a/.opencode/agents/code-reviewer.md b/.opencode/agents/code-reviewer.md deleted file mode 100644 index 54731c60..00000000 --- a/.opencode/agents/code-reviewer.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -mode: all -model: openai/gpt-5.5 -variant: high -description: Deep review of current task changes when final or high-risk validation is needed -permission: - read: allow - glob: allow - grep: allow - edit: deny - bash: - "*": deny - "git log*": allow - "git diff*": allow -temperature: 0.1 ---- - -You are a strict C++ deep code reviewer for final/high-risk validation. - -Scope: -- Review only current task files from @coder's changed-file list. -- Use actual diffs and relevant surrounding code only as needed. -- Ignore unrelated dirty worktree changes, generated artifacts, temp files, and logs. - -Source of truth: -- User request, architect Decision, Acceptance Criteria, Coder Packet, Invariants, Do NOT Change, @sanity-reviewer result, and @coder Intentional tradeoffs. - -Review focus: -- Correctness, UB, lifetime/ownership, async/task usage, persistence, CMake target propagation, cross-platform desktop/IoT behavior, performance, and security. - -Design/tradeoff rules: -- Do not redesign the solution. -- Do not reject intentional performance/API ergonomics tradeoffs only because a safer alternative exists. -- Treat tradeoffs as Findings only for concrete correctness, security, lifetime, ownership, requirement, or invariant violations. -- If an intentional API/performance tradeoff can be misused but matches accepted architecture, report it as Design Risk instead of asking for a coder patch. - -Clang-tidy/suppression review: -- Review suppressions, NOLINT, disabled checks, and unresolved clang-tidy findings. -- Treat unjustified suppressions as Findings; justification must be performance, API ergonomics, intended behavior, or project conventions. - -Rules: -- Never edit files. -- Mark repeated or design-level issues as Block. -- When blocking on design-level issues, state that architect revision is required rather than recommending local coder patching. - -Output: Reviewed files, Findings, Design risks, Suppression review, Missing tests, Risk assessment, Approve or Block. diff --git a/.opencode/agents/coder.md b/.opencode/agents/coder.md deleted file mode 100644 index 6a5371e2..00000000 --- a/.opencode/agents/coder.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -mode: subagent -model: openai/gpt-5.4-mini -variant: low -description: Write c++ code -permission: - edit: allow - grep: allow - bash: - "*": deny - "rm *": ask - "rm *.txt": ask - "rm *.cpp": allow - "rm *.h": allow - "rm *.hpp": allow - "rm *.cmake": allow - "clang-format *": allow - external_directory: deny - repo_clone: deny ---- - -You are a focused C++ implementation agent. Implement only @architect's Coder Packet; do not design or infer missing architecture from prose. - -Coder Packet contract: -- Files to Modify is the edit allowlist. Touch only those files unless @architect revises the packet. -- Execute Implementation Steps in numbered order. -- Preserve Invariants and never touch Do NOT Change items. -- Stop and report that revised @architect instructions are required if required files/APIs/steps are missing, contradictory, incompatible with code, match Escalate If, change approved design, or repeat the same failed issue. - -Implementation rules: -- Follow AGENTS.md, preserve existing patterns, avoid unrelated refactors, keep changes minimal. -- Follow code-style related to .clang-format and run clang-format after each change. -- Do not run or request build/test validation. - -Clang-tidy rules: -- Fix @sanity-reviewer clang-tidy findings as much as possible. -- Leave a finding unresolved only when fixing it harms performance, API ergonomics, intended behavior, or project conventions. -- For every unresolved finding, add an appropriate suppression or report why unresolved. -- Explain each suppression/unresolved finding briefly. -- Report suppressions or intentional performance/API ergonomics tradeoffs under: Intentional tradeoffs. - -Output: -- Report what changed and let @team-lead coordinate validation. -- Changed files: added, modified, deleted, or renamed files you changed only, with summary per file. -- Intentional tradeoffs: performance/API ergonomics/suppression decisions, or none. -- Verification: always state not run by coder. -- Notes: blockers, pre-existing unrelated worktree changes noticed, or none. diff --git a/.opencode/agents/explorer.md b/.opencode/agents/explorer.md deleted file mode 100644 index ef794aae..00000000 --- a/.opencode/agents/explorer.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -mode: subagent -model: openai/gpt-5.4-mini-fast -variant: low -description: Read-only codebase exploration before architecture or implementation work -permission: - edit: deny - bash: deny - grep: allow - glob: allow - list: allow - read: allow - external_directory: deny -temperature: 0.1 ---- - -You are a read-only C++ codebase explorer. - -Responsibilities: -- Find relevant files, APIs, existing patterns, build targets, tests, and constraints for the requested change. -- Return concise facts with file paths and symbols that @architect and @coder can rely on. - -Rules: -- Do not design the solution. -- Do not edit files. -- Do not build or test. diff --git a/.opencode/agents/sanity-reviewer.md b/.opencode/agents/sanity-reviewer.md deleted file mode 100644 index fc5f5671..00000000 --- a/.opencode/agents/sanity-reviewer.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -mode: subagent -model: openai/gpt-5.4-mini -variant: low -description: Fast check that implementation matches the task and proposed architecture -permission: - read: allow - glob: allow - grep: allow - edit: deny - bash: - "*": deny - "clang-tidy*": allow - "git log*": allow - "git diff*": allow -temperature: 0.1 ---- - -You are a fast implementation sanity reviewer. - -Scope: -- Review only @coder's changed-file list for the current task. -- Use actual diffs for those files. -- Ignore unrelated worktree changes unless listed by @coder or explicitly assigned to this task. - -Checks: -- Verify changes match the user request and architect instructions. -- Find missing requested behavior, unrelated changes inside reviewed files, architecture mismatches, and incomplete implementation. -- Run clang-tidy on changed C++ source/header files using project .clang-tidy. -- Use AGENTS.md/project instructions to find build dir or compile_commands.json; prefer clang-tidy -p . -- If clang-tidy cannot run because compile database/build configuration is missing or stale, report Clang-tidy infrastructure blocked, not a coder issue. -- Ignore or separately report unrelated existing clang-tidy findings outside the reviewed changed files. -- Order clang-tidy findings from critical correctness to style/readability, grouping cascades by root cause. - -Rules: -- Do not perform deep C++ design review. -- Do not review generated artifacts, temp files, logs, or unrelated files. -- Never edit files. - -Output: Reviewed files, Matches task, Matches architecture, Clang-tidy findings, Blocking mismatches, Approve or Block. diff --git a/.opencode/agents/team-lead.md b/.opencode/agents/team-lead.md deleted file mode 100644 index e7c22a81..00000000 --- a/.opencode/agents/team-lead.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -mode: primary -model: openai/gpt-5.5-fast -variant: medium -description: The main agent to rule the others on the way to work on code. -permission: - edit: deny - bash: deny - task: allow -temperature: 0.1 ---- - -You are team-manager. Coordinate agents; do not edit code, build, or test directly. - -Agents: -- @explorer: read-only facts. -- @architect: design, Risk, Blocking Questions, Decision, Acceptance Criteria, Coder Packet. -- @coder: implements only the Coder Packet. -- @sanity-reviewer: task/architecture match plus clang-tidy on changed files. -- @builder: CMake/Ninja build validation. -- @tester: unit and smoke tests. -- @code-reviewer: deep review only when needed. - -Handoff rules: -- Run workflow stages sequentially, each only after the previous stage successfully finishes. -- Do not invoke @coder until @architect produced a complete Coder Packet with no Blocking Questions. -- If the Coder Packet is vague, incomplete, or missing files/steps, send it back to @architect before coding. -- You may do lightweight read-only inspection for routing, but prefer @explorer for repository context. - -Fast workflow: -1. Analyze request; use @explorer for unfamiliar areas, multi-file changes, public API, ownership/lifetime, async/task logic, or unknown target files. Skip @explorer only for trivial localized requests with explicit files. -2. Ask @architect for Risk, Blocking Questions, Decision, Acceptance Criteria, and Coder Packet. -3. Require user approval for medium/high-risk changes. -4. Ask @coder to implement the complete Coder Packet. -5. Ask @sanity-reviewer to check @coder Changed files; ignore unrelated worktree changes. -6. Sanity implementation/clang-tidy block -> @coder. Sanity architecture block -> @architect. -7. Ask @builder to validate build. Build failure -> @coder. -8. If build succeeds, ask @tester to run tests. Test failure -> @coder. -9. For small fixes, user-review loops, and tuning, stop after @tester success and report to user. - -Deep review policy: -- Do not run @code-reviewer during fast iteration unless user asks for deep/final review. -- For non-final fast iteration, if a change qualifies for deep review, ask the user whether to run @code-reviewer now or defer it to final validation. -- Run @code-reviewer after build/test success for final validation, High risk changes, public API, persistence, async/task flow, crypto/security, CMake, ownership/lifetime, or cross-platform behavior. If @sanity-reviewer reported an architecture mismatch during the task, run @code-reviewer after the revised implementation passes build/test. -- Pass @code-reviewer: user request, architect Decision/Acceptance Criteria/Coder Packet, @coder Changed files, @sanity-reviewer result, and @coder Intentional tradeoffs. -- Deep review block -> @architect, then continue with @coder -> @sanity-reviewer -> @builder -> @tester, and optional final @code-reviewer. - -Loop control: -- Max three fast fix cycles per task: @coder -> @sanity-reviewer -> @builder -> @tester. -- Any return to @coder after implementation counts as one fix cycle. -- Escalate to @architect after three failed cycles, repeated @code-reviewer issue, ambiguous coder instructions, approved-design change, or design/API/ownership/lifetime/CMake/requirement mismatch. -- If @architect changes an approved medium/high-risk design, request user approval again before invoking @coder. diff --git a/.opencode/agents/tester.md b/.opencode/agents/tester.md deleted file mode 100644 index bfd75818..00000000 --- a/.opencode/agents/tester.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -mode: subagent -model: openai/gpt-5.4-mini-fast -variant: low -description: Run tests and analyze results -permission: - edit: deny - read: allow - grep: allow - glob: allow - bash: - "*": deny - "timeout *aether-client-cpp-cloud*": allow - "rm -rf *state": allow - "*aether-client-cpp-cloud*": allow - "ninja test": allow - "ctest *": allow -temperature: 0.1 -steps: 5 ---- - -You are a fast test runner and test result reporter. - -Responsibilities: -- Run unit tests. -- Separately run smoke tests. -- Report what passed and what failed. - -Smoke tests: -- Before running smoke tests, inspect project instructions such as AGENTS.md to identify what this project defines as smoke tests, where they must be run from, and whether cleanup is required. - -Failure reports: -- Report failing command, relevant output, exit status if available, and a short likely cause. - -Rules: -- Do not edit files. -- Do not design new tests unless explicitly asked. diff --git a/aether/CMakeLists.txt b/aether/CMakeLists.txt index e3bf7de7..96a67142 100644 --- a/aether/CMakeLists.txt +++ b/aether/CMakeLists.txt @@ -167,7 +167,6 @@ list(APPEND aether_srcs list(APPEND aether_srcs "api_protocol/protocol_context.cpp" "api_protocol/api_pack_parser.cpp" - "api_protocol/child_data.cpp" "api_protocol/return_result_api.cpp" ) diff --git a/aether/ae_actions/telemetry.cpp b/aether/ae_actions/telemetry.cpp index 2209f389..c2e26cad 100644 --- a/aether/ae_actions/telemetry.cpp +++ b/aether/ae_actions/telemetry.cpp @@ -19,11 +19,12 @@ #if defined TELEMETRY_ENABLED # include "aether-miscpp/format/format.h" +# include "aether-miscpp/serialization/binary_archive.h" +# include "aether-tele/traps/statistics_trap.h" + # include "aether/aether.h" # include "aether/tele_statistics.h" - -# include "aether/mstream.h" -# include "aether/mstream_buffers.h" +# include "aether/tiered_int_serializer.h" // IWYU pragma: keep # include "aether/ae_actions/ae_actions_tele.h" @@ -93,10 +94,10 @@ std::optional Telemetry::CollectTelemetry( (11 + res.cpp.lib_version.size() + res.cpp.os.size() + res.cpp.compiler.size()); res.cpp.blob.reserve(blob_max_size); - auto vector_writer = - LimitVectorWriter<>{res.cpp.blob, res.cpp.blob.capacity()}; - auto os = omstream{vector_writer}; - os << *statistics_trap; + + auto archive = seri::BinaryArchive{seri::LimitedVectorBuffer<>{res.cpp.blob}}; + archive.Save(*statistics_trap); + // TODO: should we reset stored statistics? return res; diff --git a/aether/api_protocol/api_class_impl.h b/aether/api_protocol/api_class_impl.h index fa047fab..3ff4a54e 100644 --- a/aether/api_protocol/api_class_impl.h +++ b/aether/api_protocol/api_class_impl.h @@ -167,7 +167,9 @@ struct ExtApi; template struct ExtApi { using type = T; - using Field = reflect::reflect_internal::FieldPtr; + using Field = reflect::MetaMember; + + static constexpr auto field = Field{"ext", ptr}; }; /** @@ -214,8 +216,8 @@ struct LoadSelector> { template struct LoadSelector> { static bool Load(Api* api, MessageId message_id, ApiParser& parser) { - using Field = typename ExtApi::Field; - auto&& ext_api = Field::get(*api); + using ExtApi = ExtApi; + auto&& ext_api = ExtApi::field.get(*api); return LoadFactoryImpl(&ext_api, message_id, parser); } }; diff --git a/aether/api_protocol/api_context.h b/aether/api_protocol/api_context.h index ac6ad004..cf862d36 100644 --- a/aether/api_protocol/api_context.h +++ b/aether/api_protocol/api_context.h @@ -17,15 +17,44 @@ #ifndef AETHER_API_PROTOCOL_API_CONTEXT_H_ #define AETHER_API_PROTOCOL_API_CONTEXT_H_ -#include #include +#include -#include "aether/memory.h" #include "aether/common.h" -#include "aether/api_protocol/packet_builder.h" +#include "aether/memory.h" + #include "aether/api_protocol/api_pack_parser.h" namespace ae { +class IPackMessage { + public: + virtual ~IPackMessage() = default; + + virtual void Pack(ApiPacker& packer) && = 0; +}; + +template +class PackMessage final : public IPackMessage { + public: + template + explicit PackMessage(UApiClass&& api_class, U&& api_message) + : api_class_{ + std::forward(api_class), + }, api_message_{std::forward(api_message)} {} + + void Pack(ApiPacker& packer) && override { + api_class_.Pack(std::move(api_message_), packer); + } + + private: + TApiClass api_class_; + TApiMessage api_message_; +}; + +template +PackMessage(TApiClass&& api_class, TApiMessages&&... api_messages) + -> PackMessage...>; + /** * \brief Stack of packed messages to generate one packet */ @@ -52,31 +81,6 @@ class PacketStack { std::vector> packets_; }; -/** - * \brief Used as a child data for sub api packet. - * Provide it's own specialization for operator<< to message_ostream. - */ -class ChildPacketStack { - public: - ChildPacketStack() : packet_stack_{make_unique()} {} - - AE_CLASS_MOVE_ONLY(ChildPacketStack) - - PacketStack& operator*() { return *packet_stack_; } - - friend message_ostream& operator<<( - message_ostream& os, ChildPacketStack const& child_packet_stack) { - auto data = std::vector{}; - auto packer = ApiPacker{os.ob_.packer.Context(), data}; - std::move(*child_packet_stack.packet_stack_).Pack(packer); - os << data; - return os; - } - - private: - std::unique_ptr packet_stack_; -}; - /** * \brief Api method call context. * Wrapper for api class. Pointer to TApi return through proxy object which diff --git a/aether/api_protocol/api_message.h b/aether/api_protocol/api_message.h index abc44505..ab5dee9e 100644 --- a/aether/api_protocol/api_message.h +++ b/aether/api_protocol/api_message.h @@ -17,14 +17,16 @@ #ifndef AETHER_API_PROTOCOL_API_MESSAGE_H_ #define AETHER_API_PROTOCOL_API_MESSAGE_H_ +#include +#include #include #include -#include #include -#include "aether/mstream.h" -#include "aether/mstream_buffers.h" +#include "aether-miscpp/serialization/serialization.h" + +#include "aether/vector_buffer.h" namespace ae { @@ -35,71 +37,58 @@ using PackedSize = TieredInt; class ApiParser; class ApiPacker; -struct MessageBufferWriter : VectorWriter { - MessageBufferWriter(std::vector& data, ApiPacker& p) - : VectorWriter(data), packer{p} {} - - ApiPacker& packer; -}; -struct MessageBufferReader : VectorReader { - MessageBufferReader(std::vector const& data, ApiParser& p) - : VectorReader(data), parser{p} {} - ApiParser& parser; -}; - -using message_ostream = ae::omstream; -using message_istream = ae::imstream; - -// Base for all messages -template -struct Message { - Message() = default; - Message(Message const&) = default; - void Load(message_istream& ms) { ms >> static_cast(*this); } - void Save(message_ostream& ms) const { ms << static_cast(*this); } -}; +using MessageBuffer = VectorBuffer; /** * \brief A message formed from template parameters */ template -struct GenericMessage : Message> { +struct GenericMessage { explicit GenericMessage() = default; explicit GenericMessage(Ts... args) : fields{std::forward(args)...} {} - template - friend imstream& operator>>(imstream& is, GenericMessage& mesassge) { - std::apply([&](auto&... args) { ((is >> args), ...); }, mesassge.fields); - return is; - } - - template - friend omstream& operator<<(omstream& os, - GenericMessage const& mesassge) { - std::apply([&](auto const&... args) { ((os << args), ...); }, - mesassge.fields); - return os; - } - - std::tuple fields; + [[no_unique_address]] std::tuple fields; }; template <> -struct GenericMessage<> : Message> { +struct GenericMessage<> { explicit GenericMessage() = default; +}; - template - friend imstream& operator>>(imstream& is, - GenericMessage& /* mesassge */) { - return is; +namespace seri { +template +struct Serializer> { + SeriResult Seri(A& archive, Meta const> meta) const { + if constexpr (sizeof...(Ts) > 0) { + return std::apply( + [&](auto&... args) { + auto res = SeriResult{Ok{good}}; + auto b = ((res = archive.Save(Meta{.value = args, .name = "field"}), + !!res) && + ...); + (void)b; + return res; + }, + meta.value.fields); + } + return Ok{seri::good}; } - template - friend omstream& operator<<(omstream& os, - GenericMessage const& /* mesassge */) { - return os; + SeriResult Deseri(A& archive, Meta> meta) const { + if constexpr (sizeof...(Ts) > 0) { + return std::apply( + [&](auto&... args) { + auto res = SeriResult{Ok{good}}; + auto b = ((res = archive.Load(Meta{args}), !!res) && ...); + (void)b; + return res; + }, + meta.value.fields); + } + return Ok{seri::good}; } }; +} // namespace seri } // namespace ae diff --git a/aether/api_protocol/api_method.h b/aether/api_protocol/api_method.h index 418502b5..4e186969 100644 --- a/aether/api_protocol/api_method.h +++ b/aether/api_protocol/api_method.h @@ -17,10 +17,10 @@ #ifndef AETHER_API_PROTOCOL_API_METHOD_H_ #define AETHER_API_PROTOCOL_API_METHOD_H_ -#include "aether/api_protocol/api_message.h" #include "aether/api_protocol/api_context.h" -#include "aether/api_protocol/api_promise.h" +#include "aether/api_protocol/api_message.h" #include "aether/api_protocol/api_pack_parser.h" +#include "aether/api_protocol/api_promise.h" #include "aether/api_protocol/protocol_context.h" namespace ae { @@ -97,44 +97,6 @@ struct Method(Args...), ArgProc> { ProtocolContext* protocol_context_; ArgProc arg_proc_; }; - -/** - * \brief Specialization for method call with sub api. - * A GenericMessage generated directly from list of args and subapi method call. - * returns SubContext with access to Api class. - */ -template -struct Method(Args...), ArgProc> { - explicit Method(ProtocolContext& protocol_context, Api& api, - ArgProc arg_proc = {}) - : protocol_context_{&protocol_context}, - api_{&api}, - arg_proc_{std::move(arg_proc)} {} - - SubContext operator()(Args... args) { - auto child_stack = ChildPacketStack{}; - SubContext context{*api_, *child_stack}; - - auto* packet_stack = protocol_context_->packet_stack(); - assert(packet_stack != nullptr && - "Method::operator() requires active ApiContext"); - packet_stack->Push( - *this, arg_proc_(std::forward(args)..., std::move(child_stack))); - return context; - } - - template - void Pack(GenericMessage&& message, ApiPacker& packer) { - packer.Pack(MessageCode, std::move(message)); - } - - private: - ProtocolContext* protocol_context_; - Api* api_; - ArgProc arg_proc_; -}; - } // namespace ae #endif // AETHER_API_PROTOCOL_API_METHOD_H_ diff --git a/aether/api_protocol/api_pack_parser.cpp b/aether/api_protocol/api_pack_parser.cpp index bc3f9222..895a33e8 100644 --- a/aether/api_protocol/api_pack_parser.cpp +++ b/aether/api_protocol/api_pack_parser.cpp @@ -16,36 +16,31 @@ #include "aether/api_protocol/api_pack_parser.h" -#include "aether/api_protocol/child_data.h" - namespace ae { ApiParser::ApiParser(ProtocolContext& protocol_context, std::vector const& data) - : protocol_context_{protocol_context}, buffer_reader_{data, *this} { + : protocol_context_{protocol_context}, + archive{MessageBuffer{ + const_cast&>(data), // NOLINT(*const-cast) + }} { protocol_context_.PushParser(*this); } -ApiParser::ApiParser(ProtocolContext& protocol_context_, - ChildData const& child_data) - : ApiParser{protocol_context_, child_data.PackData()} {} - ApiParser::~ApiParser() { protocol_context_.PopParser(); } ProtocolContext& ApiParser::Context() { return protocol_context_; } void ApiParser::Cancel() { - buffer_reader_.offset_ = buffer_reader_.data_.size(); + archive.buffer().read_offset = archive.buffer().buff.size(); } ApiPacker::ApiPacker(ProtocolContext& protocol_context_, std::vector& data) - : protocol_context_{protocol_context_}, buffer_writer_{data, *this} { + : protocol_context_{protocol_context_}, archive{MessageBuffer{data}} { protocol_context_.PushPacker(*this); } ApiPacker::~ApiPacker() { protocol_context_.PopPacker(); } -MessageBufferWriter& ApiPacker::Buffer() { return buffer_writer_; } - ProtocolContext& ApiPacker::Context() { return protocol_context_; } } // namespace ae diff --git a/aether/api_protocol/api_pack_parser.h b/aether/api_protocol/api_pack_parser.h index 473d070f..a2b95c7c 100644 --- a/aether/api_protocol/api_pack_parser.h +++ b/aether/api_protocol/api_pack_parser.h @@ -17,18 +17,18 @@ #ifndef AETHER_API_PROTOCOL_API_PACK_PARSER_H_ #define AETHER_API_PROTOCOL_API_PACK_PARSER_H_ -#include -#include -#include #include #include +#include +#include +#include + +#include "aether-miscpp/serialization/binary_archive.h" #include "aether/api_protocol/api_message.h" #include "aether/api_protocol/protocol_context.h" namespace ae { -class ChildData; - class ApiParser; class ApiPacker; @@ -37,14 +37,17 @@ class ApiParser { public: ApiParser(ProtocolContext& protocol_context_, std::vector const& data); - ApiParser(ProtocolContext& protocol_context_, ChildData const& child_data); ~ApiParser(); template void Parse(TApiClass& api_class) { - while (buffer_reader_.offset_ < buffer_reader_.data_.size()) { + while (archive.buffer().read_offset < archive.buffer().buff.size()) { MessageId message_id{std::numeric_limits::max()}; - istream_ >> message_id; + if (auto res = archive.Load(message_id); res.IsErr()) { + // message_id didn't loaded + assert(false && "message_id didn't loaded"); + return; + } api_class.LoadFactory(message_id, *this); } } @@ -52,14 +55,21 @@ class ApiParser { template void Load(TApiClass& api_class) { Message msg{}; - msg.Load(istream_); + if (auto res = archive.Load(msg); res.IsErr()) { + // message_id didn't loaded + assert(false && "Message didn't loaded"); + return; + } api_class.Execute(std::move(msg), *this); } template T Extract() { T result{}; - istream_ >> result; + if (auto res = archive.Load(result); res.IsErr()) { + // message_id didn't loaded + assert(false && "result didn't extracted"); + } return result; } @@ -69,8 +79,7 @@ class ApiParser { private: ProtocolContext& protocol_context_; - MessageBufferReader buffer_reader_; - message_istream istream_{buffer_reader_}; + seri::BinaryArchive archive; }; // Packing API messages to raw data buffer @@ -81,19 +90,24 @@ class ApiPacker { ~ApiPacker(); template - void Pack(MessageId message_id, Message&& msg) { - ostream_ << message_id; - std::forward(msg).Save(ostream_); + void Pack(MessageId message_id, Message const& msg) { + auto res = archive.Save(message_id); + if (!res) { + assert(false && "Message id didn't saved"); + return; + } + res = archive.Save(msg); + if (!res) { + assert(false && "Message didn't saved"); + return; + } } - MessageBufferWriter& Buffer(); - ProtocolContext& Context(); private: ProtocolContext& protocol_context_; - MessageBufferWriter buffer_writer_; - message_ostream ostream_{buffer_writer_}; + seri::BinaryArchive archive; }; } // namespace ae diff --git a/aether/api_protocol/api_promise.h b/aether/api_protocol/api_promise.h index c14c23df..0a7a81e8 100644 --- a/aether/api_protocol/api_promise.h +++ b/aether/api_protocol/api_promise.h @@ -23,10 +23,12 @@ #include "aether-miscpp/meta/ignore_t.h" #include "aether-miscpp/types/result.h" + +#include "aether/events/events.h" + #include "aether/api_protocol/api_pack_parser.h" #include "aether/api_protocol/protocol_context.h" #include "aether/api_protocol/request_id.h" -#include "aether/events/events.h" namespace ae { namespace api_promise_internal { diff --git a/aether/api_protocol/api_protocol.h b/aether/api_protocol/api_protocol.h index 55a21e83..c091c43e 100644 --- a/aether/api_protocol/api_protocol.h +++ b/aether/api_protocol/api_protocol.h @@ -18,17 +18,17 @@ #define AETHER_API_PROTOCOL_API_PROTOCOL_H_ // IWYU pragma: begin_exports -#include "aether/api_protocol/sub_api.h" #include "aether/api_protocol/api_class.h" -#include "aether/api_protocol/request_id.h" -#include "aether/api_protocol/api_method.h" -#include "aether/api_protocol/api_message.h" -#include "aether/api_protocol/api_context.h" #include "aether/api_protocol/api_class_impl.h" +#include "aether/api_protocol/api_context.h" +#include "aether/api_protocol/api_message.h" +#include "aether/api_protocol/api_method.h" #include "aether/api_protocol/api_pack_parser.h" -#include "aether/api_protocol/protocol_context.h" #include "aether/api_protocol/api_promise.h" +#include "aether/api_protocol/protocol_context.h" +#include "aether/api_protocol/request_id.h" #include "aether/api_protocol/return_result_api.h" +#include "aether/api_protocol/sub_api.h" // IWYU pragma: end_exports #endif // AETHER_API_PROTOCOL_API_PROTOCOL_H_ */ diff --git a/aether/api_protocol/child_data.cpp b/aether/api_protocol/child_data.cpp deleted file mode 100644 index 418ce009..00000000 --- a/aether/api_protocol/child_data.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2024 Aethernet Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "aether/api_protocol/child_data.h" - -namespace ae { -ChildData::ChildData() = default; - -ChildData::ChildData(std::vector data) - : pack_data_{std::move(data)} {} -ChildData::ChildData(std::unique_ptr pack_message) - : pack_data_{std::move(pack_message)} {} - -std::vector ChildData::PackData( - ProtocolContext& protocol_context) && { - if (pack_data_.index() == 0) { - return DataPackMessage(protocol_context, - std::move(*std::get<0>(pack_data_))); - } - return std::get<1>(pack_data_); -} - -std::vector const& ChildData::PackData() const { - if (pack_data_.index() == 0) { - assert(false); - } - return std::get<1>(pack_data_); -} - -std::vector ChildData::DataPackMessage( - ProtocolContext& protocol_context, IPackMessage&& pack_message) { - std::vector res; - auto packer = ApiPacker{protocol_context, res}; - std::move(pack_message).Pack(packer); - return res; -} - -} // namespace ae diff --git a/aether/api_protocol/child_data.h b/aether/api_protocol/child_data.h deleted file mode 100644 index a5a109f2..00000000 --- a/aether/api_protocol/child_data.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2024 Aethernet Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef AETHER_API_PROTOCOL_CHILD_DATA_H_ -#define AETHER_API_PROTOCOL_CHILD_DATA_H_ - -#include -#include -#include -#include -#include - -#include "aether/api_protocol/api_message.h" -#include "aether/api_protocol/packet_builder.h" -#include "aether/api_protocol/protocol_context.h" - -namespace ae { -class ChildData { - public: - ChildData(); - - ChildData(std::vector data); - ChildData(std::unique_ptr pack_message); - - template - ChildData(PackMessage pack_message) - : ChildData{std::make_unique>( - std::move(pack_message))} {} - - ChildData(PacketBuilder&& packet_builder) - : ChildData{std::move(packet_builder).Pack()} {} - - ChildData(ChildData&& other) : pack_data_{std::move(other.pack_data_)} {} - ChildData(ChildData const& other) : ChildData{other.PackData()} {} - - ChildData& operator=(ChildData const& other) { - if (this != &other) { - pack_data_ = other.PackData(); - } - return *this; - } - - ChildData& operator=(ChildData&& other) { - if (this != &other) { - pack_data_ = std::move(other.pack_data_); - } - return *this; - } - - std::vector PackData(ProtocolContext& protocol_context) &&; - std::vector const& PackData() const; - - void clear() { pack_data_ = std::vector{}; } - - private: - static std::vector DataPackMessage( - ProtocolContext& protocol_context, IPackMessage&& pack_message); - - std::variant, std::vector> - pack_data_; -}; - -template -imstream& operator>>(imstream& is, ChildData& ch_d) { - std::vector data; - is >> data; - ch_d = ChildData{data}; - return is; -} - -inline message_ostream& operator<<(message_ostream& os, ChildData const& ch_d) { - os << std::move(const_cast(ch_d)) - .PackData(os.ob_.packer.Context()); - return os; -} - -} // namespace ae - -#endif // AETHER_API_PROTOCOL_CHILD_DATA_H_ diff --git a/aether/api_protocol/packet_builder.h b/aether/api_protocol/packet_builder.h deleted file mode 100644 index 6eefb2fa..00000000 --- a/aether/api_protocol/packet_builder.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2024 Aethernet Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef AETHER_API_PROTOCOL_PACKET_BUILDER_H_ -#define AETHER_API_PROTOCOL_PACKET_BUILDER_H_ - -#include -#include -#include -#include -#include - -#include "aether/api_protocol/api_pack_parser.h" - -namespace ae { - -class IPackMessage { - public: - virtual ~IPackMessage() = default; - - virtual void Pack(ApiPacker& packer) && = 0; -}; - -template -class PackMessage : public IPackMessage { - public: - template - explicit PackMessage(UApiClass&& api_class, UApiMessages&&... api_messages) - : api_class_{ - std::forward(api_class), - }, api_messages_{std::forward(api_messages)...} {} - - void Pack(ApiPacker& packer) && override { - std::apply( - [this, packer{&packer}](auto&&... messages) { - (api_class_.Pack(std::forward(messages), *packer), - ...); - }, - std::move(api_messages_)); - } - - private: - TApiClass api_class_; - std::tuple api_messages_; -}; - -template -PackMessage(TApiClass&& api_class, TApiMessages&&... api_messages) - -> PackMessage...>; - -class PacketBuilder { - public: - template - explicit PacketBuilder(ProtocolContext& protocol_context, - TPackMessages&&... pack_messages) - : protocol_context_{protocol_context} { - (pack_messages_.push_back(std::make_unique>( - std::forward(pack_messages))), - ...); - } - - template - void Push(TApiClass&& api_class, TApiMessages&&... api_messages) { - Push(PackMessage{std::forward(api_class), - std::forward(api_messages)...}); - } - - std::vector Pack() && { - auto data = std::vector{}; - - ApiPacker packer{protocol_context_, data}; - - for (auto& pack_message : pack_messages_) { - std::move(*pack_message).Pack(packer); - } - return data; - } - - operator std::vector() && { return std::move(*this).Pack(); } - - private: - template - void Push(TPackMessage&& pack_message) { - pack_messages_.push_back(std::make_unique>( - std::forward(pack_message))); - } - - ProtocolContext& protocol_context_; - std::vector> pack_messages_; -}; - -} // namespace ae - -#endif // AETHER_API_PROTOCOL_PACKET_BUILDER_H_ diff --git a/aether/api_protocol/protocol_context.h b/aether/api_protocol/protocol_context.h index a75e1427..e4d5741e 100644 --- a/aether/api_protocol/protocol_context.h +++ b/aether/api_protocol/protocol_context.h @@ -18,7 +18,6 @@ #define AETHER_API_PROTOCOL_PROTOCOL_CONTEXT_H_ #include -#include #include #include diff --git a/aether/api_protocol/return_result_api.cpp b/aether/api_protocol/return_result_api.cpp index 76435593..da48662e 100644 --- a/aether/api_protocol/return_result_api.cpp +++ b/aether/api_protocol/return_result_api.cpp @@ -29,9 +29,4 @@ void ReturnResultApi::SendErrorImpl(RequestId request_id, std::uint32_t error_code) { protocol_context().SetSendErrorResponse(request_id, error_type, error_code); } - -void ReturnResultApi::Pack(ae::SendResult&& result, ApiPacker& packer) { - packer.Pack(kSendResult, std::move(result)); -} - } // namespace ae diff --git a/aether/api_protocol/return_result_api.h b/aether/api_protocol/return_result_api.h index e3304b72..b4820960 100644 --- a/aether/api_protocol/return_result_api.h +++ b/aether/api_protocol/return_result_api.h @@ -19,12 +19,20 @@ #include -#include "aether/api_protocol/api_method.h" -#include "aether/api_protocol/send_result.h" +#include "aether-miscpp/reflect/reflect.h" + #include "aether/api_protocol/api_class_impl.h" +#include "aether/api_protocol/api_method.h" namespace ae { class ReturnResultApi : public ApiClass { + template + struct SendResultMessage { + AE_REFLECT_MEMBERS(req_id, data); + RequestId req_id; + T data; + }; + public: static constexpr MessageId kSendResult = 0; static constexpr MessageId kSendError = 1; @@ -40,7 +48,7 @@ class ReturnResultApi : public ApiClass { void SendResult(RequestId req_id, T&& data) { auto* packet_stack = protocol_context().packet_stack(); assert(packet_stack); - packet_stack->Push(*this, ::ae::SendResult{req_id, std::forward(data)}); + packet_stack->Push(*this, SendResultMessage{req_id, std::forward(data)}); } void SendError(RequestId req_id, std::uint8_t error_type, @@ -51,7 +59,10 @@ class ReturnResultApi : public ApiClass { AE_METHODS(RegMethod, RegMethod); - void Pack(ae::SendResult&& result, ApiPacker& packer); + template + void Pack(SendResultMessage&& result, ApiPacker& packer) const { + packer.Pack(kSendResult, std::move(result)); + } private: Method -#include -#include - -#include "aether/mstream.h" -#include "aether/mstream_buffers.h" -#include "aether/api_protocol/request_id.h" -#include "aether/api_protocol/api_message.h" - -namespace ae { -struct SendResult : public Message { - static constexpr std::uint32_t kMessageId = 0; - - SendResult() = default; - - // Construct from T data - template - SendResult(RequestId request_id, T const& t) - : request_id{std::move(request_id)} { - VectorWriter mbw{child_data}; - auto os = omstream{mbw}; - os << t; - } - - // convert to T data - template - auto Get() const { - VectorReader mbr{child_data}; - auto is = imstream{mbr}; - T t{}; - is >> t; - return t; - } - - template - friend imstream& operator>>(imstream& is, SendResult& sr) { - is >> sr.request_id; - // child data will be parsed from the stream - return is; - } - - template - friend omstream& operator<<(omstream& os, SendResult const& sr) { - os << sr.request_id; - // write data as is - os.write(sr.child_data.data(), sr.child_data.size()); - return os; - } - - RequestId request_id; - // for serialize only - std::vector child_data; -}; -} // namespace ae - -#endif // AETHER_API_PROTOCOL_SEND_RESULT_H_ */ diff --git a/aether/crypto/sign.h b/aether/crypto/sign.h index 48987085..989e21c1 100644 --- a/aether/crypto/sign.h +++ b/aether/crypto/sign.h @@ -32,15 +32,15 @@ # include # endif -# include "aether-miscpp/reflect/reflect.h" # include "aether-miscpp/format/format.h" +# include "aether-miscpp/reflect/reflect.h" # include "aether/types/variant_type.h" namespace ae { enum class SignatureMethod : std::uint8_t { - kEd25519 = 0, - kHydroSignature, + kEd25519 = 1, + kHydroSignature = 2, }; # if AE_SIGNATURE == AE_ED25519 @@ -85,7 +85,6 @@ struct Sign : public VariantType(*this)); } - }; template <> diff --git a/aether/domain_storage/file_system_std_storage.cpp b/aether/domain_storage/file_system_std_storage.cpp index 3ce2071e..cbb66f59 100644 --- a/aether/domain_storage/file_system_std_storage.cpp +++ b/aether/domain_storage/file_system_std_storage.cpp @@ -32,7 +32,7 @@ namespace ae { class FstreamStorageWriter final : public IDomainStorageWriter { public: FstreamStorageWriter(DomainQuery q, std::ofstream&& f) - : query{std::move(q)}, file{std::move(f)}, written_size{0} {} + : query{std::move(q)}, file{std::move(f)} {} ~FstreamStorageWriter() override { file.close(); @@ -42,16 +42,25 @@ class FstreamStorageWriter final : public IDomainStorageWriter { written_size); } - void write(void const* data, std::size_t size) override { - file.write(reinterpret_cast(data), - static_cast(size)); - written_size += size; + seri::SeriResult Write(seri::SizeWriteTag data) override { + auto const u_size = static_cast(data.size); + return Write(seri::DataTag{u_size}); + } + + seri::SeriResult Write(seri::DataWriteTag data) override { + file.write(reinterpret_cast(data.data), + static_cast(data.size)); + if (file.fail()) { + return Error{seri::write_error}; + } + written_size += data.size; + return Ok{seri::good}; } private: DomainQuery query; std::ofstream file; - std::size_t written_size; + std::size_t written_size{}; }; class FstreamStorageReader final : public IDomainStorageReader { @@ -59,13 +68,28 @@ class FstreamStorageReader final : public IDomainStorageReader { explicit FstreamStorageReader(std::ifstream&& f) : file{std::move(f)} {} ~FstreamStorageReader() override { file.close(); } - void read(void* data, std::size_t size) override { - file.read(reinterpret_cast(data), - static_cast(size)); + seri::SeriResult Read(seri::SizeReadTag data) override { + std::uint32_t u_size{}; + TRY_RESULT(Read(seri::DataTag{u_size})); + data.size = static_cast(u_size); + return Ok{seri::good}; } - ReadResult result() const override { return ReadResult::kYes; } - void result(ReadResult) override {} + seri::SeriResult Read(seri::DataReadTag data) override { + if (file.eof()) { + return Error{seri::read_eof}; + } + + file.read(reinterpret_cast(data.data), + static_cast(data.size)); + if (file.bad()) { + return Error{seri::read_error}; + } + if (file.gcount() != static_cast(data.size)) { + return Error{file.eof() ? seri::read_eof : seri::read_error}; + } + return Ok{seri::good}; + } private: std::ifstream file; diff --git a/aether/domain_storage/file_system_std_storage.h b/aether/domain_storage/file_system_std_storage.h index 652f1565..bb863a27 100644 --- a/aether/domain_storage/file_system_std_storage.h +++ b/aether/domain_storage/file_system_std_storage.h @@ -17,7 +17,7 @@ #ifndef AETHER_DOMAIN_STORAGE_FILE_SYSTEM_STD_STORAGE_H_ #define AETHER_DOMAIN_STORAGE_FILE_SYSTEM_STD_STORAGE_H_ -#include +#include #if (defined(__linux__) || defined(__unix__) || defined(__APPLE__) || \ defined(__FreeBSD__) || defined(_WIN64) || defined(_WIN32)) diff --git a/aether/domain_storage/ram_domain_storage.cpp b/aether/domain_storage/ram_domain_storage.cpp index 0c4c7d50..788f6871 100644 --- a/aether/domain_storage/ram_domain_storage.cpp +++ b/aether/domain_storage/ram_domain_storage.cpp @@ -19,47 +19,66 @@ #if defined AE_FILE_SYSTEM_RAM_ENABLED # include +# include +# include +# include # include "aether/domain_storage/domain_storage_tele.h" -# include "aether/mstream_buffers.h" namespace ae { class RamDomainStorageWriter final : public IDomainStorageWriter { public: RamDomainStorageWriter(DomainQuery q, RamDomainStorage& s) - : query{std::move(q)}, storage{&s}, vector_writer{data_buffer} { + : query{std::move(q)}, storage{&s} { assert(!storage->write_lock); } ~RamDomainStorageWriter() override { storage->SaveData(query, std::move(data_buffer)); } - void write(void const* data, std::size_t size) override { - vector_writer.write(data, size); + seri::SeriResult Write(seri::SizeWriteTag data) override { + auto const u_size = static_cast(data.size); + return Write(seri::DataTag{u_size}); + } + + seri::SeriResult Write(seri::DataWriteTag data) override { + data_buffer.insert(std::end(data_buffer), + static_cast(data.data), + static_cast(data.data) + data.size); + return Ok{seri::good}; } DomainQuery query; RamDomainStorage* storage; ObjectData data_buffer; - VectorWriter vector_writer; }; class RamDomainStorageReader final : public IDomainStorageReader { public: RamDomainStorageReader(ObjectData const& d, RamDomainStorage& s) - : storage{&s}, data_buffer{&d}, reader{*data_buffer} { + : storage{&s}, data_buffer{d.data(), d.size()} { storage->write_lock = true; } ~RamDomainStorageReader() override { storage->write_lock = false; } - void read(void* data, std::size_t size) override { reader.read(data, size); } + seri::SeriResult Read(seri::SizeReadTag data) override { + std::uint32_t u_size{}; + TRY_RESULT(Read(seri::DataTag{u_size})); + data.size = static_cast(u_size); + return Ok{seri::good}; + } - ReadResult result() const override { return ReadResult::kYes; } - void result(ReadResult) override {} + seri::SeriResult Read(seri::DataReadTag data) override { + if (data_buffer.size() < data.size) { + return Error{seri::read_eof}; + } + std::memcpy(data.data, data_buffer.data(), data.size); + data_buffer = data_buffer.subspan(data.size); + return Ok{seri::good}; + } RamDomainStorage* storage; - ObjectData const* data_buffer; - VectorReader reader; + std::span data_buffer; }; RamDomainStorage::RamDomainStorage() = default; diff --git a/aether/domain_storage/ram_domain_storage.h b/aether/domain_storage/ram_domain_storage.h index 4078b17b..893c3d60 100644 --- a/aether/domain_storage/ram_domain_storage.h +++ b/aether/domain_storage/ram_domain_storage.h @@ -19,8 +19,8 @@ #define AE_FILE_SYSTEM_RAM_ENABLED 0 -#include #include +#include #include #include "aether/obj/idomain_storage.h" diff --git a/aether/domain_storage/static_domain_storage.cpp b/aether/domain_storage/static_domain_storage.cpp index 0ef974eb..75ef29e2 100644 --- a/aether/domain_storage/static_domain_storage.cpp +++ b/aether/domain_storage/static_domain_storage.cpp @@ -17,24 +17,27 @@ #include "aether/domain_storage/static_domain_storage.h" #include -#include -#include +#include +#include namespace ae { StaticDomainStorageReader::StaticDomainStorageReader( Span const& d) - : data{&d}, offset{} {} + : data_buffer(d.data(), d.size()) {} -void StaticDomainStorageReader::read(void* out, std::size_t size) { - assert((offset + size) <= data->size()); - std::copy(std::begin(*data) + offset, std::begin(*data) + offset + size, - reinterpret_cast(out)); - offset += size; +seri::SeriResult StaticDomainStorageReader::Read(seri::SizeReadTag data) { + std::uint32_t u_size{}; + TRY_RESULT(Read(seri::DataTag{u_size})); + data.size = static_cast(u_size); + return Ok{seri::good}; } -ReadResult StaticDomainStorageReader::result() const { - return ReadResult::kYes; +seri::SeriResult StaticDomainStorageReader::Read(seri::DataReadTag data) { + if (data_buffer.size() < data.size) { + return Error{seri::read_eof}; + } + std::memcpy(data.data, data_buffer.data(), data.size); + data_buffer = data_buffer.subspan(data.size); + return Ok{seri::good}; } - -void StaticDomainStorageReader::result(ReadResult) {} } // namespace ae diff --git a/aether/domain_storage/static_domain_storage.h b/aether/domain_storage/static_domain_storage.h index 013f76c9..3ae50bfa 100644 --- a/aether/domain_storage/static_domain_storage.h +++ b/aether/domain_storage/static_domain_storage.h @@ -17,24 +17,23 @@ #ifndef AETHER_DOMAIN_STORAGE_STATIC_DOMAIN_STORAGE_H_ #define AETHER_DOMAIN_STORAGE_STATIC_DOMAIN_STORAGE_H_ +#include + #include "aether/domain_storage/static_object_types.h" #include "aether/obj/idomain_storage.h" -#include "aether/tele.h" +#include "aether/tele.h" // IWYU pragma: keep namespace ae { class StaticDomainStorageReader final : public IDomainStorageReader { public: explicit StaticDomainStorageReader(Span const& d); - void read(void* out, std::size_t size) override; - - ReadResult result() const override; - void result(ReadResult) override; + seri::SeriResult Read(seri::SizeReadTag data) override; + seri::SeriResult Read(seri::DataReadTag data) override; private: - Span const* data; - std::size_t offset; + std::span data_buffer; }; template diff --git a/aether/gateway_api/server_endpoints.h b/aether/gateway_api/server_endpoints.h index c29fba42..f620c06e 100644 --- a/aether/gateway_api/server_endpoints.h +++ b/aether/gateway_api/server_endpoints.h @@ -17,14 +17,14 @@ #ifndef AETHER_GATEWAY_API_SERVER_ENDPOINTS_H_ #define AETHER_GATEWAY_API_SERVER_ENDPOINTS_H_ -#include #include +#include #include "aether-miscpp/crc.h" -#include "aether/mstream.h" -#include "aether/types/address.h" -#include "aether/mstream_buffers.h" #include "aether-miscpp/reflect/reflect.h" +#include "aether-miscpp/serialization/binary_archive.h" + +#include "aether/types/address.h" namespace ae { struct ServerEndpoints { @@ -38,9 +38,11 @@ template <> struct hash { std::size_t operator()(ae::ServerEndpoints const& endpoints) const { std::vector data; - auto writer = ae::VectorWriter<>{data}; - auto os = ae::omstream{writer}; - os << endpoints; + auto archive = ae::seri::BinaryArchive{ + ae::seri::BinaryVectorBuffer<>{data}, + }; + archive.Save(endpoints); + return static_cast( crc32::from_buffer(data.data(), data.size()).value); } diff --git a/aether/memory_buffer.h b/aether/memory_buffer.h deleted file mode 100644 index 5b90e1ca..00000000 --- a/aether/memory_buffer.h +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright 2024 Aethernet Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef AETHER_MEMORY_BUFFER_H_ -#define AETHER_MEMORY_BUFFER_H_ - -#include -#include -#include -#include -#include - -namespace ae { -template , - std::streamsize BLOCK_SIZE = 1024> -class MemStreamBuf : public std::basic_streambuf { - public: - using streambuf = std::basic_streambuf; - using char_type = typename streambuf::char_type; - using int_type = typename streambuf::int_type; - using traits_type = typename streambuf::traits_type; - using pos_type = typename streambuf::pos_type; - using off_type = typename streambuf::off_type; - - static constexpr std::streamsize kBlockSize = BLOCK_SIZE; - - private: - char_type* membuf_{nullptr}; - - public: - MemStreamBuf() { SetCapacity(kBlockSize); } - explicit MemStreamBuf(std::streamsize capacity) { - SetExactCapacity(capacity); - } - MemStreamBuf(MemStreamBuf const&) = delete; - MemStreamBuf(MemStreamBuf&& other) noexcept : membuf_{other.membuf_} { - this->swap(other); - other.membuf_ = nullptr; - } - - virtual ~MemStreamBuf() { - if (membuf_) { - std::free(membuf_); - membuf_ = nullptr; - } - } - - /** - * \brief Changes the buffer capacity to value multiple to the block size. - */ - constexpr char_type* SetCapacity(std::streamsize new_size) noexcept { - // size must be a multiple of BLOCK_SIZE - new_size = new_size ? (((new_size - 1) / kBlockSize + 1) * kBlockSize) : 0; - return SetExactCapacity(new_size); - } - - char_type* SetExactCapacity(std::streamsize new_size) noexcept { - // minimum size is one byte - if (new_size <= 0) { - return nullptr; - } - auto* new_buf = std::realloc(membuf_, static_cast(new_size)); - if (new_buf == nullptr) { - return nullptr; - } - membuf_ = static_cast(new_buf); - - auto cur_size = Size(); - // set pbase, pptr and epptr to new buffer - streambuf::setp(membuf_, membuf_ + new_size); - // advance pptr to old position or end of buffer is smaller - streambuf::pbump(static_cast(std::min(cur_size, new_size))); - // update get area pointers - auto cur_pos = streambuf::gptr() - streambuf::eback(); - streambuf::setg(streambuf::pbase(), streambuf::pbase() + cur_pos, - streambuf::pptr()); - return membuf_; - } - - constexpr char_type* ShrinkToFit() { return SetExactCapacity(Size()); } - - constexpr char_type* Data() const { return membuf_; } - - constexpr std::streamsize Size() const { - return streambuf::pptr() - streambuf::pbase(); - } - - constexpr std::streamsize Capacity() const { - return streambuf::epptr() - streambuf::pbase(); - } - - // Available capacity to write - constexpr std::streamsize AvailableCapacity() const { - return streambuf::epptr() - streambuf::pptr(); - } - - // Available data size to read - constexpr std::streamsize AvailableData() const { - return streambuf::egptr() - streambuf::gptr(); - } - - /** - * \brief Implements std::basic_streambuf::xsputn - */ - std::streamsize xsputn(const char_type* s, std::streamsize count) override { - if (count > AvailableCapacity()) { - SetCapacity(Size() + count); - } - - auto written = streambuf::xsputn(s, count); - streambuf::setg(streambuf::eback(), streambuf::gptr(), streambuf::pptr()); - - return written; - } - - /** - * \brief Implements std::basic_streambuf::overflow - */ - int_type overflow(int_type ch) override { - // try to increase the size by one block - if (!SetCapacity(Size() + - static_cast(sizeof(char_type)))) { - // size increase did not work, return eof - return traits_type::eof(); - } - // put char if not eof - if (!traits_type::eq_int_type(ch, traits_type::eof())) { - char_type ch_char = traits_type::to_char_type(ch); - xsputn(&ch_char, 1); - } - return traits_type::not_eof(ch); - } - - // Implements std::basic_streambuf::showmanyc - std::streamsize showmanyc() override { return -1; } - - /** - * \brief Implements std::basic_streambuf::setbuf - * \param[in] s - new buffer, must be allocated with std::malloc, MemStreamBuf - * owns this new buffer. - */ - MemStreamBuf* setbuf(char_type* s, std::streamsize size) override { - if (membuf_) { - std::free(membuf_); - } - membuf_ = s; - streambuf::setp(membuf_, membuf_ + size); - return this; - } - - /** - * \brief Implements std::basic_streambuf::seekpos - */ - pos_type seekpos(pos_type sp, std::ios_base::openmode mode) override { - if (mode == std::ios_base::in) { - streambuf::setg(membuf_, membuf_ + static_cast(sp), - membuf_ + static_cast(Size())); - } else if (mode == std::ios_base::out) { - streambuf::setp(membuf_, membuf_ + static_cast(sp)); - } - return sp; - } - - private: -}; -} // namespace ae -#endif // AETHER_MEMORY_BUFFER_H_ */ diff --git a/aether/mstream.h b/aether/mstream.h deleted file mode 100644 index d6b26a5f..00000000 --- a/aether/mstream.h +++ /dev/null @@ -1,690 +0,0 @@ -/* - * Copyright 2024 Aethernet Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Copyright 2016 Aether authors. All Rights Reserved. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ============================================================================= - -#ifndef AETHER_MSTREAM_H_ -#define AETHER_MSTREAM_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "aether-miscpp/reflect/domain_visitor.h" // IWYU pragma: keep -#include "aether-miscpp/reflect/reflect.h" -#include "aether/types/nullable_type.h" - -namespace ae { - -// Base classes to simplify std::conditional checks in serialization functions. -class stream {}; -class ostream : public stream {}; -class istream : public stream {}; - -/** - * struct OBuffer { - * using size_type = std::uint32_t; - * size_t write(void* data, size_t size); - * }; - */ - -template -class omstream : public ostream { - public: - OBuffer& ob_; - - omstream(OBuffer& out_buffer) : ob_{out_buffer} {} - - void write(const void* data, size_t size) { ob_.write(data, size); } -}; - -enum class ReadResult : char { - kNo, - kYes, -}; - -/** - * struct IBuffer { - * using size_type = std::uint32_t; - * size_t read(void* data, size_t size, size_t minimum_size); - * ReadResult result() const; - * void result(ReadResult); - }; - */ - -template -class imstream : public istream { - public: - IBuffer& ib_; - - imstream(IBuffer& input_buffer) : ib_{input_buffer} {} - - void read(void* data, size_t size) { ib_.read(data, size); } - - ReadResult result() const { return ib_.result(); } - void result(ReadResult result) { ib_.result(result); } -}; - -template -inline bool data_was_read(TStream& /* is */) { - return true; -} - -template -inline bool data_was_read(imstream& is) { - return is.result() == ReadResult::kYes; -} - -template -inline bool data_was_written(TStream& /* s */) { - return true; -} - -template -struct omstream_enable_if : std::enable_if&> {}; - -template -using omstream_enable_if_t = typename omstream_enable_if::type; - -template -struct imstream_enable_if : std::enable_if&> {}; - -template -using imstream_enable_if_t = typename imstream_enable_if::type; - -template -struct has_omstream : std::false_type {}; - -template -struct has_omstream&>() - << std::declval())>> - : std::true_type {}; - -template -struct has_imstream : std::false_type {}; - -template -struct has_imstream< - T, Ib, - std::void_t&>() >> std::declval())>> - : std::true_type {}; - -/*********************** operator << implementation **************** */ -template -omstream_enable_if_t::value && std::is_arithmetic::value, - Ob> -operator<<(omstream& s, const T& v) { - s.write(&v, sizeof(T)); - return s; -} - -template -imstream_enable_if_t::value && std::is_arithmetic::value, - Ib> -operator>>(imstream& s, T& v) { - s.read(&v, sizeof(T)); - return s; -} - -template -omstream_enable_if_t, Ob> operator<<(omstream& s, - const T& v) { - using Type = typename std::underlying_type::type; - auto t = static_cast(v); - s << t; - return s; -} - -template -imstream_enable_if_t::value, Ib> operator>>(imstream& s, - T& v) { - using Type = typename std::underlying_type::type; - Type t; - s >> t; - if (s.result() == ReadResult::kYes) { - v = static_cast(t); - } - return s; -} - -template -omstream& operator<<(omstream& s, bool v) { - constexpr std::uint8_t true_val = 0x01; - constexpr std::uint8_t false_val = 0x00; - if (v) { - s.write(&true_val, 1); - } else { - s.write(&false_val, 1); - } - return s; -} - -template -imstream& operator>>(imstream& s, bool& v) { - constexpr std::uint8_t true_val = 0x01; - std::uint8_t temp; - s.read(&temp, 1); - v = (temp == true_val); - return s; -} - -template -omstream& operator<<(omstream& s, const char* t) { - auto length = std::strlen(t); - s << static_cast(length); - s.write(t, length); - return s; -} - -template -omstream& operator<<(omstream& s, const std::string& t) { - s << static_cast(t.size()); - s.write(t.data(), t.size()); - return s; -} - -template -imstream& operator>>(imstream& s, std::string& t) { - typename Ib::size_type size; - s >> size; - if (data_was_read(s)) { - t.resize(static_cast(size)); - s.read(t.data(), static_cast(size)); - } - return s; -} - -template -omstream& operator<<(omstream& s, const std::string_view& t) { - s << static_cast(t.size()); - s.write(t.data(), t.size()); - return s; -} - -template -omstream_enable_if_t, Ob> operator<<( - omstream& s, const std::vector& t) { - s << static_cast(t.size()); - s.write(reinterpret_cast(t.data()), t.size() * sizeof(T)); - return s; -} - -template -imstream_enable_if_t, Ib> operator>>(imstream& s, - std::vector& t) { - typename Ib::size_type size; - s >> size; - if (data_was_read(s)) { - t.resize(static_cast(size)); - s.read(reinterpret_cast(t.data()), t.size() * sizeof(T)); - } - return s; -} - -template -omstream_enable_if_t, Ob> operator<<( - omstream& s, const std::vector& t) { - s << static_cast(t.size()); - for (const T& v : t) { - s << v; - } - return s; -} - -template -imstream_enable_if_t, Ib> operator>>(imstream& s, - std::vector& t) { - typename Ib::size_type size; - s >> size; - if (!data_was_read(s)) { - return s; - } - t.resize(static_cast(size)); - for (auto& v : t) { - s >> v; - if (!data_was_read(s)) { - break; - } - } - return s; -} - -template -omstream_enable_if_t, Ob> operator<<(omstream& s, - T const (&t)[N]) { - s.write(reinterpret_cast(t), N * sizeof(T)); - return s; -} - -template -imstream_enable_if_t, Ib> operator>>(imstream& s, - T (&t)[N]) { - s.read(reinterpret_cast(t), N * sizeof(T)); - return s; -} - -template -omstream_enable_if_t, Ob> operator<<( - omstream& s, const std::array& t) { - s.write(reinterpret_cast(t.data()), t.size() * sizeof(T)); - return s; -} - -template -imstream_enable_if_t, Ib> operator>>(imstream& s, - std::array& t) { - s.read(reinterpret_cast(t.data()), t.size() * sizeof(T)); - return s; -} - -template -omstream_enable_if_t, Ob> operator<<( - omstream& s, const std::array& t) { - for (const T& v : t) { - s << v; - } - return s; -} - -template -imstream_enable_if_t, Ib> operator>>(imstream& s, - std::array& t) { - for (auto& v : t) { - s >> v; - if (!data_was_read(s)) { - break; - } - } - return s; -} - -template -omstream& operator<<(omstream& s, const std::map& t) { - s << static_cast(t.size()); - for (const auto& i : t) { - s << i; - } - return s; -} -template -omstream& operator<<(omstream& s, const std::unordered_map& t) { - s << static_cast(t.size()); - for (const auto& i : t) { - s << i; - } - return s; -} - -template -imstream& operator>>(imstream& s, std::map& t) { - typename Ib::size_type size{}; - s >> size; - if (!data_was_read(s)) { - return s; - } - t.clear(); - for (std::size_t i = 0; i < static_cast(size); i++) { - std::pair kv; - s >> kv; - if (!data_was_read(s)) { - break; - } - t.emplace(std::move(kv)); - } - return s; -} - -template -imstream& operator>>(imstream& s, std::unordered_map& t) { - typename Ib::size_type size{}; - s >> size; - if (!data_was_read(s)) { - return s; - } - t.clear(); - t.reserve(static_cast(size)); - for (auto i = 0; i < static_cast(size); ++i) { - std::pair kv; - s >> kv; - if (!data_was_read(s)) { - break; - } - t.emplace(std::move(kv)); - } - return s; -} - -template -omstream& operator<<(omstream& s, const std::pair& t) { - s << t.first << t.second; - return s; -} -template -imstream& operator>>(imstream& s, std::pair& t) { - s >> t.first >> t.second; - return s; -} - -template -omstream& operator<<(omstream& s, const std::deque& t) { - s << static_cast(t.size()); - for (const auto& v : t) { - s << v; - } - return s; -} -template -imstream& operator>>(imstream& s, std::deque& t) { - typename Ib::size_type size; - s >> size; - - if (!data_was_read(s)) { - return s; - } - t.resize(static_cast(size)); - - for (auto& v : t) { - s >> v; - if (!data_was_read(s)) { - break; - } - } - return s; -} - -template -omstream& operator<<(omstream& s, const std::list& t) { - s << static_cast(t.size()); - for (const auto& i : t) { - s << i; - } - return s; -} - -template -imstream_enable_if_t, Ib>& operator>>( - imstream& s, std::list& t) { - typename Ib::size_type size; - s >> size; - if (!data_was_read(s)) { - return s; - } - t.clear(); - for (uint32_t i = 0; i < size; i++) { - T v; - s >> v; - if (!data_was_read(s)) { - break; - } - t.push_back(std::move(v)); - } - return s; -} - -template -omstream& operator<<(omstream& s, - std::chrono::duration const& val) { - s << static_cast( - std::chrono::duration_cast(val).count()); - return s; -} - -template -imstream& operator>>(imstream& s, std::chrono::duration& val) { - std::uint32_t temp; - s >> temp; - val = std::chrono::duration_cast>( - std::chrono::microseconds{ - static_cast(temp)}); - return s; -} - -template -omstream& operator<<(omstream& s, - std::chrono::time_point const& t) { - auto d = std::chrono::duration_cast( - t.time_since_epoch()); - s << static_cast(d.count()); - return s; -} - -template -imstream& operator>>(imstream& s, std::chrono::time_point& t) { - std::uint64_t tp; - s >> tp; - if (!data_was_read(s)) { - return s; - } - t = std::chrono::time_point(std::chrono::microseconds(tp)); - return s; -} - -template -omstream& operator<<(omstream& s, std::optional const& v) { - if (v) { - s << true; // save has_value = true - s << v.value(); - } else { - s << false; // save has_value = false - } - return s; -} - -template -imstream& operator>>(imstream& s, std::optional& v) { - bool has_value{}; - s >> has_value; - if (!data_was_read(s)) { - return s; - } - if (has_value) { - T temp{}; - s >> temp; - if (data_was_read(s)) { - v.emplace(std::move(temp)); - } - } - return s; -} - -template -omstream& operator<<(omstream& s, std::variant const& v) { - static_assert( - sizeof...(Ts) <= std::numeric_limits::max(), - "std::variant mstream serialization supports at most 255 alternatives " - "(indices 0..254; tag 255 is reserved as the valueless sentinel)"); - if (v.valueless_by_exception()) { - auto const tag = std::numeric_limits::max(); - s << tag; - return s; - } - auto const tag = static_cast(v.index()); - s << tag; - std::visit([&s](auto const& value) { s << value; }, v); - return s; -} - -template -imstream& operator>>(imstream& s, std::variant& v) { - static_assert( - sizeof...(Ts) <= std::numeric_limits::max(), - "std::variant mstream serialization supports at most 255 alternatives " - "(indices 0..254; tag 255 is reserved as the valueless sentinel)"); - std::uint8_t tag{}; - s >> tag; - if (!data_was_read(s)) { - return s; - } - - if (tag == std::numeric_limits::max()) { - // Explicit valueless-by-exception sentinel; no payload follows. - return s; - } - - if (tag >= sizeof...(Ts)) { - s.result(ReadResult::kNo); - return s; - } - - auto handled = std::invoke( - [&](std::index_sequence) { - return (std::invoke( - [&](std::integral_constant) { - if (tag == static_cast(I)) { - auto& alternative = v.template emplace(); - s >> alternative; - return true; - } - return false; - }, - std::integral_constant{}) || - ...); - }, - std::make_index_sequence()); - if (!handled) { - s.result(ReadResult::kNo); - return s; - } - if (!data_was_read(s)) { - s.result(ReadResult::kNo); - } - return s; -} - -template -omstream& operator<<(omstream& s, const std::unique_ptr& v) { - if (v) { - s << true; - s << *v; - } else { - s << false; - } - return s; -} - -template -omstream& operator<<(omstream& s, const std::shared_ptr& v) { - if (v) { - s << true; - s << *v; - } else { - s << false; - } - return s; -} - -template -imstream& operator>>(imstream& s, std::unique_ptr& v) { - bool has_value{}; - s >> has_value; - if (!data_was_read(s)) { - return s; - } - if (has_value) { - auto temp = std::make_unique(); - s >> *temp; - if (data_was_read(s)) { - v = std::move(temp); - } - } - return s; -} - -template -imstream& operator>>(imstream& s, std::shared_ptr& v) { - bool has_value{}; - s >> has_value; - if (!data_was_read(s)) { - return s; - } - if (has_value) { - auto temp = std::make_unique(); - s >> *temp; - if (data_was_read(s)) { - v = std::move(temp); - } - } - return s; -} - -template -omstream_enable_if_t::value, Ob> operator<<( - omstream& s, T const* const& v) { - if (v != nullptr) { - s << *v; - } - return s; -} - -template -imstream_enable_if_t::value, Ib> operator>>(imstream& s, - T* const& v) { - if (v != nullptr) { - s >> *v; - } - return s; -} - -template -std::enable_if_t::value, imstream&> operator>>( - imstream& s, T& t) { - if constexpr (std::is_base_of_v, T>) { - t.Load(s); - } else { - auto reflection = reflect::Reflection{t}; - reflection.Apply([&s](auto&... val) { ((s >> val), ...); }); - } - return s; -} - -template -std::enable_if_t::value, omstream&> operator<<( - omstream& s, T const& t) { - if constexpr (std::is_base_of_v, T>) { - t.Save(s); - } else { - auto reflection = reflect::Reflection{t}; - reflection.Apply([&s](auto const&... val) { ((s << val), ...); }); - } - return s; -} -} // namespace ae - -#endif // AETHER_MSTREAM_H_ diff --git a/aether/mstream_buffers.h b/aether/mstream_buffers.h deleted file mode 100644 index 7a9eba20..00000000 --- a/aether/mstream_buffers.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2024 Aethernet Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef AETHER_MSTREAM_BUFFERS_H_ -#define AETHER_MSTREAM_BUFFERS_H_ - -#include -#include - -#include "aether/mstream.h" -#include "aether/memory_buffer.h" - -namespace ae { - -// implements OBuffer -template -struct VectorWriter { - using size_type = SizeType; - - std::vector& data_; - - VectorWriter(std::vector& data) : data_(data) {} - virtual ~VectorWriter() = default; - - std::size_t write(void const* data, std::size_t size) { - data_.insert(data_.end(), reinterpret_cast(data), - reinterpret_cast(data) + size); - return size; - } -}; - -template -struct VectorReader { - using size_type = SizeType; - std::vector const& data_; - std::size_t offset_ = 0; - ReadResult result_{}; - - VectorReader(std::vector const& data) : data_(data) {} - virtual ~VectorReader() = default; - - size_t read(void* data, size_t size) { - if (offset_ + size > data_.size()) { - result_ = ReadResult::kNo; - return 0; - } - std::memcpy(data, data_.data() + offset_, size); - offset_ += size; - result_ = ReadResult::kYes; - return size; - } - - ReadResult result() const { return result_; } - void result(ReadResult result) { result_ = result; } -}; - -/** - * \brief VectorWriter with limit in size - */ -template -struct LimitVectorWriter { - using size_type = SizeType; - - LimitVectorWriter(std::vector& buffer, std::size_t l) - : data_buffer{buffer}, limit{l} {} - - std::size_t write(void const* data, std::size_t size) { - if (end || ((data_buffer.size() + size) >= limit)) { - // end of writing - end = true; - return 0; - } - data_buffer.insert(data_buffer.end(), - reinterpret_cast(data), - reinterpret_cast(data) + size); - return size; - } - - std::vector& data_buffer; - std::size_t limit; - bool end = false; -}; - -template -struct MemStreamReader { - using size_type = SizeType; - - MemStreamBuf<> buffer_; - - ReadResult read_result_{}; - - size_t add_data(uint8_t const* data, size_t size) { - // expand buffer with new data - auto s = buffer_.sputn(reinterpret_cast(data), - static_cast(size)); - return static_cast(s); - } - - void reset_read() { buffer_.pubseekpos(0, std::ios_base::in); } - void reset_write() { buffer_.pubseekpos(0, std::ios_base::out); } - - size_t read(void* dst, size_t size) { - auto s = buffer_.sgetn(static_cast(dst), - static_cast(size)); - if (s != size) { - read_result_ = ReadResult::kNo; - } else { - read_result_ = ReadResult::kYes; - } - return static_cast(s); - } - - ReadResult result() const { return read_result_; } - void result(ReadResult result) { read_result_ = result; } -}; - -template -struct MemStreamWriter { - using size_type = SizeType; - - std::size_t write(void const* data, std::size_t size) { - buffer.xsputn(reinterpret_cast(data), - static_cast(size)); - return size; - } - - MemStreamBuf<> buffer; -}; -} // namespace ae - -#endif // AETHER_MSTREAM_BUFFERS_H_ */ diff --git a/aether/obj/domain.cpp b/aether/obj/domain.cpp index 6b3db271..cd367440 100644 --- a/aether/obj/domain.cpp +++ b/aether/obj/domain.cpp @@ -75,16 +75,8 @@ void DomainGraph::SaveRootImpl(Ptr const& ptr, ObjId obj_id) { } } -std::unique_ptr DomainGraph::GetReader( - DomainQuery const& query) { - auto load = domain->storage_->Load(query); - if ((load.result == DomainLoadResult::kEmpty) || - (load.result == DomainLoadResult::kRemoved)) { - load.reader = std::make_unique(); - } - - assert(load.reader && "Reader must be created!"); - return std::move(load.reader); +DomainLoad DomainGraph::GetReader(DomainQuery const& query) { + return domain->storage_->Load(query); } std::unique_ptr DomainGraph::GetWriter( @@ -184,4 +176,52 @@ Factory* Domain::GetMostRelatedFactory(ObjId id) { Factory* Domain::FindClassFactory(std::uint32_t class_id) { return registry_->FindFactory(class_id); } + +namespace seri { +SeriResult Serializer, std::string>::Deseri( + BinaryArchive& archive, Meta val) const { + std::size_t size{}; + TRY_RESULT(archive.buffer().Read(SizeTag{size})); + if (size > archive.max_container_load_size()) { + return Error{container_too_large}; + } + val.value.resize(size); + auto* data = reinterpret_cast(val.value.data()); + return archive.buffer().Read(DataReadTag{data, size}); +} + +SeriResult Serializer, std::string>::Seri( + BinaryArchive& archive, Meta val) const { + auto const size = val.value.size(); + TRY_RESULT(archive.buffer().Write(SizeTag{size})); + auto const* data = reinterpret_cast(val.value.data()); + TRY_RESULT(archive.buffer().Write(DataWriteTag{data, size})); + return Ok{good}; +} + +SeriResult +Serializer, std::vector>::Deseri( + BinaryArchive& archive, + Meta> val) const { + std::size_t size{}; + TRY_RESULT(archive.buffer().Read(SizeTag{size})); + if (size > archive.max_container_load_size()) { + return Error{container_too_large}; + } + val.value.resize(size); + auto* data = reinterpret_cast(val.value.data()); + return archive.buffer().Read(DataReadTag{data, size}); +} + +SeriResult +Serializer, std::vector>::Seri( + BinaryArchive& archive, + Meta const> val) const { + auto const size = val.value.size(); + TRY_RESULT(archive.buffer().Write(SizeTag{size})); + auto const* data = reinterpret_cast(val.value.data()); + TRY_RESULT(archive.buffer().Write(DataWriteTag{data, size})); + return Ok{good}; +} +} // namespace seri } // namespace ae diff --git a/aether/obj/domain.h b/aether/obj/domain.h index c9422afa..e25de5ff 100644 --- a/aether/obj/domain.h +++ b/aether/obj/domain.h @@ -26,11 +26,12 @@ #include "aether/clock.h" -#include "aether/mstream.h" - -#include "aether-miscpp/reflect/domain_visitor.h" +#include "aether-miscpp/domain_visitor/domain_visitor.h" #include "aether-miscpp/reflect/reflect.h" +#include "aether-miscpp/serialization/binary_archive.h" +#include "aether-miscpp/serialization/serialization.h" #include "aether/ptr/ptr_view.h" +#include "aether/tiered_int_serializer.h" #include "aether/obj/idomain_storage.h" #include "aether/obj/obj_id.h" @@ -63,33 +64,38 @@ struct DomainCycleDetector { std::set visited_nodes; }; -class DomainStorageReaderEmpty final : public IDomainStorageReader { - public: - void read(void*, std::size_t) override {} - ReadResult result() const override { return ReadResult::kNo; } - void result(ReadResult) override {} -}; - -struct DomainBufferWriter { - using size_type = IDomainStorageReader::size_type; - - void write(void const* data, std::size_t size) { writer->write(data, size); } - - ObjId id; - DomainGraph* domain_graph{}; - IDomainStorageWriter* writer; -}; +struct DomainBuffer { + seri::SeriResult Write(seri::SizeWriteTag data) const { + if (writer == nullptr) { + return Error{seri::write_eof}; + } + return writer->Write(data); + } -struct DomainBufferReader { - using size_type = IDomainStorageReader::size_type; + seri::SeriResult Write(seri::DataWriteTag data) const { + if (writer == nullptr) { + return Error{seri::write_eof}; + } + return writer->Write(data); + } - void read(void* data, std::size_t size) { reader->read(data, size); } - ReadResult result() const { return reader->result(); } - void result(ReadResult result) { reader->result(result); } + seri::SeriResult Read(seri::SizeReadTag data) const { + if (reader == nullptr) { + return Error{seri::read_eof}; + } + return reader->Read(data); + } + seri::SeriResult Read(seri::DataReadTag data) const { + if (reader == nullptr) { + return Error{seri::read_eof}; + } + return reader->Read(data); + } ObjId id; DomainGraph* domain_graph{}; - IDomainStorageReader* reader; + IDomainStorageWriter* writer{nullptr}; + IDomainStorageReader* reader{nullptr}; }; /** @@ -115,20 +121,20 @@ class DomainGraph { void SaveRootImpl(Ptr const& ptr, ObjId obj_id); template - void Load(T& obj, ObjId obj_id); + seri::SeriResult Load(T& obj, ObjId obj_id); template - void LoadVersion(Version version, T& obj, ObjId obj_id); + seri::SeriResult LoadVersion(Version version, T& obj, ObjId obj_id); template - void Save(T const& obj, ObjId obj_id); + seri::SeriResult Save(T const& obj, ObjId obj_id); template - void SaveVersion(Version version, T const& obj, ObjId obj_id); + seri::SeriResult SaveVersion(Version version, T const& obj, ObjId obj_id); Domain* domain{}; DomainCycleDetector cycle_detector{}; private: - std::unique_ptr GetReader(DomainQuery const& query); + DomainLoad GetReader(DomainQuery const& query); std::unique_ptr GetWriter(DomainQuery const& query); }; @@ -177,92 +183,153 @@ Ptr DomainGraph::LoadCopy(ObjId ref_id, ObjId copy_id) { } template -void DomainGraph::Load(T& obj, ObjId obj_id) { +seri::SeriResult DomainGraph::Load(T& obj, ObjId obj_id) { if (!cycle_detector.Add(T::kClassId, obj_id)) { - return; + return Ok{seri::good}; } if constexpr (HasAnyVersionedLoad::value) { + auto result = seri::SeriResult{Ok{seri::good}}; version_iterator( - obj, [this, obj_id](auto version, auto& obj) { - this->LoadVersion(version, obj, obj_id); + obj, [this, obj_id, &result](auto version, auto& obj) { + if (result.IsErr()) { + return; + } + result = this->LoadVersion(version, obj, obj_id); }); + return result; } else { - LoadVersion(T::kCurrentVersion, obj, obj_id); + return LoadVersion(T::kCurrentVersion, obj, obj_id); } } +struct LoadVisitor { + void operator()(auto& v) { + if (res) { + res = bin_archive.Load(v); + } + } + + seri::BinaryArchive& bin_archive; + seri::SeriResult res{Ok{seri::good}}; +}; + template -void DomainGraph::LoadVersion(Version version, T& obj, ObjId obj_id) { - auto storage_reader = GetReader({obj_id, T::kClassId, V}); - DomainBufferReader reader{obj_id, this, storage_reader.get()}; - imstream is(reader); - - auto visitor_func = [&is](auto& value) { - is >> value; - return false; - }; +seri::SeriResult DomainGraph::LoadVersion(Version version, T& obj, + ObjId obj_id) { + auto load = GetReader({obj_id, T::kClassId, V}); + if (load.result != DomainLoadResult::kLoaded) { + return Ok{seri::good}; + } + + auto bin_archive = + seri::BinaryArchive{DomainBuffer{.id = obj_id, + .domain_graph = this, + .writer = {}, + .reader = load.reader.get()}}; + + auto load_visitor = LoadVisitor{bin_archive}; // if T has any versioned, it also must have Load for this version if constexpr (HasAnyVersionedLoad::value) { - VersionNodeVisitor visitor{std::move(visitor_func)}; + auto visitor = VersionNodeVisitor{load_visitor}; obj.Load(version, visitor); } else { // load or deserialize object - reflect::DomainVisit(obj, std::move(visitor_func)); + domain_visitor::DomainVisit(obj, load_visitor); } + return load_visitor.res; } template -void DomainGraph::Save(T const& obj, ObjId obj_id) { +seri::SeriResult DomainGraph::Save(T const& obj, ObjId obj_id) { if (!cycle_detector.Add(T::kClassId, obj_id)) { - return; + return Ok{seri::good}; } if constexpr (HasAnyVersionedSave::value) { + auto result = seri::SeriResult{Ok{seri::good}}; version_iterator( - obj, [this, obj_id](auto version, auto& obj) { - this->SaveVersion(version, obj, obj_id); + obj, [this, obj_id, &result](auto version, auto& obj) { + if (result.IsErr()) { + return; + } + result = this->SaveVersion(version, obj, obj_id); }); + return result; } else { - SaveVersion(T::kCurrentVersion, obj, obj_id); + return SaveVersion(T::kCurrentVersion, obj, obj_id); } } +struct SaveVisitor { + void operator()(auto const& v) { + if (res) { + res = bin_archive.Save(v); + } + } + + seri::BinaryArchive& bin_archive; + seri::SeriResult res{Ok{seri::good}}; +}; + template -void DomainGraph::SaveVersion(Version version, T const& obj, ObjId obj_id) { +seri::SeriResult DomainGraph::SaveVersion(Version version, T const& obj, + ObjId obj_id) { auto storage_writer = GetWriter({obj_id, T::kClassId, V}); - DomainBufferWriter writer{obj_id, this, storage_writer.get()}; - omstream os(writer); - auto visitor_func = [&os](auto const& value) { - os << value; - return false; - }; + auto bin_archive = + seri::BinaryArchive{DomainBuffer{.id = obj_id, + .domain_graph = this, + .writer = storage_writer.get(), + .reader = {}}}; + + auto save_visitor = SaveVisitor{bin_archive}; if constexpr (HasAnyVersionedSave::value) { - VersionNodeVisitor visitor{std::move(visitor_func)}; + auto visitor = VersionNodeVisitor{save_visitor}; obj.Save(version, visitor); } else { // load or deserialize object - reflect::DomainVisit(obj, std::move(visitor_func)); + domain_visitor::DomainVisit( + obj, save_visitor, + domain_visitor::PolicyConst{}); } + return save_visitor.res; } +namespace seri { template -std::enable_if_t, imstream&> -operator>>(imstream& is, T& obj) { - is.ib_.domain_graph->Load(obj, is.ib_.id); - return is; -} + requires(std::is_base_of_v) +struct Serializer, T> { + using Archive = BinaryArchive; -template -std::enable_if_t, omstream&> -operator<<(omstream& os, T const& obj) { - os.ob_.domain_graph->Save(obj, os.ob_.id); - return os; -} + SeriResult Seri(Archive& arch, Meta meta_val) const { + return arch.buffer().domain_graph->Save(meta_val.value, arch.buffer().id); + } + + SeriResult Deseri(Archive& arch, Meta meta_val) const { + return arch.buffer().domain_graph->Load(meta_val.value, arch.buffer().id); + } +}; + +template <> +struct Serializer, std::string> { + SeriResult Deseri(BinaryArchive& archive, + Meta val) const; + SeriResult Seri(BinaryArchive& archive, + Meta val) const; +}; + +template <> +struct Serializer, std::vector> { + SeriResult Deseri(BinaryArchive& archive, + Meta> val) const; + SeriResult Seri(BinaryArchive& archive, + Meta const> val) const; +}; +} // namespace seri } // namespace ae #endif // AETHER_OBJ_DOMAIN_H_ diff --git a/aether/obj/idomain_storage.h b/aether/obj/idomain_storage.h index e448cd31..befce8fc 100644 --- a/aether/obj/idomain_storage.h +++ b/aether/obj/idomain_storage.h @@ -21,7 +21,8 @@ #include #include -#include "aether/mstream.h" +#include "aether-miscpp/serialization/binary_archive.h" + #include "aether/obj/obj_id.h" namespace ae { @@ -41,24 +42,22 @@ struct DomainQuery { std::uint8_t version; }; +// BinaryBuffer implementation for storage writer class IDomainStorageWriter { public: - using size_type = std::uint32_t; - virtual ~IDomainStorageWriter() = default; - virtual void write(void const* data, std::size_t size) = 0; + virtual seri::SeriResult Write(seri::SizeWriteTag data) = 0; + virtual seri::SeriResult Write(seri::DataWriteTag data) = 0; }; +// BinaryBuffer implementation for storage reader class IDomainStorageReader { public: - using size_type = std::uint32_t; - virtual ~IDomainStorageReader() = default; - virtual void read(void* data, std::size_t size) = 0; - virtual ReadResult result() const = 0; - virtual void result(ReadResult result) = 0; + virtual seri::SeriResult Read(seri::SizeReadTag data) = 0; + virtual seri::SeriResult Read(seri::DataReadTag data) = 0; }; struct DomainLoad { diff --git a/aether/obj/obj.cpp b/aether/obj/obj.cpp index 06edc1f1..4a2e450c 100644 --- a/aether/obj/obj.cpp +++ b/aether/obj/obj.cpp @@ -29,7 +29,7 @@ Obj::~Obj() { uint32_t Obj::GetClassId() const { return kClassId; } -namespace reflect { +namespace domain_visitor { std::size_t GetObjIndexImpl(Obj const* obj, std::uint32_t class_id) { auto res = crc32::from_buffer( reinterpret_cast(&class_id), sizeof(class_id)); @@ -38,5 +38,5 @@ std::size_t GetObjIndexImpl(Obj const* obj, std::uint32_t class_id) { sizeof(id), res); return res.value; } -} // namespace reflect +} // namespace domain_visitor } // namespace ae diff --git a/aether/obj/obj.h b/aether/obj/obj.h index bf35079b..2fff4f8b 100644 --- a/aether/obj/obj.h +++ b/aether/obj/obj.h @@ -68,7 +68,7 @@ class Obj { ObjId obj_id; }; -namespace reflect { +namespace domain_visitor { extern std::size_t GetObjIndexImpl(Obj const* obj, std::uint32_t class_id); template @@ -77,8 +77,7 @@ struct ObjectIndex>> { return GetObjIndexImpl(obj, T::kClassId); } }; -} // namespace reflect - +} // namespace domain_visitor } // namespace ae #define _AE_OBJECT_FIELDS(CLASS_ID, BASE_CLASS_ID, VERSION) \ diff --git a/aether/obj/obj_ptr.h b/aether/obj/obj_ptr.h index 5a736d9d..89167782 100644 --- a/aether/obj/obj_ptr.h +++ b/aether/obj/obj_ptr.h @@ -22,7 +22,10 @@ #include #include -#include "aether-miscpp/reflect/domain_visitor.h" // IWYU pragma: keep +#include "aether-miscpp/domain_visitor/domain_visitor.h" // IWYU pragma: keep +#include "aether-miscpp/serialization/binary_archive.h" +#include "aether-miscpp/serialization/serialization.h" + #include "aether/obj/domain.h" #include "aether/obj/obj_id.h" #include "aether/obj/obj_ptr_base.h" @@ -45,8 +48,10 @@ struct ObjProp { }; struct CreateWith { - CreateWith(Domain* d) : domain{d} { assert(d != nullptr); } - CreateWith(Domain& d) : domain{&d} {} + CreateWith(Domain* d) : domain{d} { // NOLINT(*explicit*) + assert(d != nullptr); + } + CreateWith(Domain& d) : domain{&d} {} // NOLINT(*explicit*) CreateWith&& with_id(ObjId id) && { obj_id = id; @@ -68,12 +73,7 @@ class ObjPtr : public ObjectPtrBase { template friend class ObjPtr; - template - friend imstream& operator>>( - imstream& is, ObjPtr& ptr); - template - friend omstream& operator<<( - omstream& os, ObjPtr const& ptr); + friend struct seri::Serializer, ObjPtr>; public: /** @@ -111,7 +111,7 @@ class ObjPtr : public ObjectPtrBase { template requires(IsAbleToCast::value) - ObjPtr(ObjPtr ptr) noexcept + ObjPtr(ObjPtr ptr) noexcept // NOLINT(*explicit*) : ObjectPtrBase{static_cast(ptr)}, ptr_{std::move(ptr.ptr_)} {} @@ -258,7 +258,7 @@ Ptr const& ObjPtr::Load() { template Ptr const& ObjPtr::Load() const { - return const_cast*>(this)->Load(); + return const_cast*>(this)->Load(); // NOLINT(*const-cast) } template @@ -320,33 +320,39 @@ void ObjPtr::Reset() { flags_ = flags() & ObjFlags::kUnloaded; } +} // namespace ae + +namespace ae::seri { template -imstream& operator>>(imstream& is, - ObjPtr& ptr) { - is >> static_cast(ptr); - if (!(ptr.flags() & ObjFlags::kUnloadedByDefault) && - !(ptr.flags() & ObjFlags::kUnloaded)) { - // Load the object only if it's valid and unloaded flag is not set - ptr.ptr_ = is.ib_.domain_graph->LoadPtr(ptr.id()); +struct Serializer, ObjPtr> { + using Archive = BinaryArchive; + + SeriResult Seri(Archive& archive, Meta const> meta) const { + TRY_RESULT((archive.Save(static_cast(meta.value)))); + if (meta.value.ptr_) { + archive.buffer().domain_graph->SavePtr(meta.value.ptr_, meta.value.id()); + } + return Ok{seri::good}; } - return is; -} -template -omstream& operator<<(omstream& os, - ObjPtr const& ptr) { - os << static_cast(ptr); - if (ptr.ptr_) { - os.ob_.domain_graph->SavePtr(ptr.ptr_, ptr.id()); + SeriResult Deseri(Archive& archive, Meta> meta) const { + TRY_RESULT((archive.Load(static_cast(meta.value)))); + + if (!(meta.value.flags() & ObjFlags::kUnloadedByDefault) && + !(meta.value.flags() & ObjFlags::kUnloaded)) { + // Load the object only if it's valid and unloaded flag is not set + meta.value.ptr_ = + archive.buffer().domain_graph->LoadPtr(meta.value.id()); + } + return Ok{seri::good}; } - return os; -} -} // namespace ae +}; +} // namespace ae::seri -namespace ae::reflect { +namespace ae::domain_visitor { template struct NodeVisitor> : NodeVisitor> { - using Policy = AnyPolicyMatch; + using Policy = PolicyMatch; using Base = NodeVisitor>; template @@ -367,6 +373,6 @@ struct NodeVisitor> : NodeVisitor> { } } }; -} // namespace ae::reflect +} // namespace ae::domain_visitor #endif // AETHER_OBJ_OBJ_PTR_H_ diff --git a/aether/obj/obj_ptr_base.cpp b/aether/obj/obj_ptr_base.cpp index f0561a29..646de5f2 100644 --- a/aether/obj/obj_ptr_base.cpp +++ b/aether/obj/obj_ptr_base.cpp @@ -16,8 +16,8 @@ #include "aether/obj/obj_ptr_base.h" -#include "aether/obj/obj_id.h" #include "aether/obj/domain.h" +#include "aether/obj/obj_id.h" namespace ae { @@ -38,17 +38,21 @@ void ObjectPtrBase::SetFlags(ObjFlags flags) { flags_ = flags; } ObjectPtrBase& ObjectPtrBase::operator=(ObjectPtrBase const& ptr) noexcept = default; -imstream& operator>>(imstream& is, - ObjectPtrBase& ptr) { - ptr.domain_ = is.ib_.domain_graph->domain; - is >> ptr.id_ >> ptr.flags_; - return is; -} +namespace seri { +using ObjPtrBaseSerializer = + Serializer, ObjectPtrBase>; -omstream& operator<<(omstream& os, - ObjectPtrBase const& ptr) { - os << ptr.id_ << ptr.flags_; - return os; +SeriResult ObjPtrBaseSerializer::Seri(Archive& archive, + Meta meta) const { + TRY_RESULT((archive.buffer().Write(DataTag{meta.value.id_}))); + return archive.buffer().Write(DataTag{meta.value.flags_}); } +SeriResult ObjPtrBaseSerializer::Deseri(Archive& archive, + Meta meta) const { + meta.value.domain_ = archive.buffer().domain_graph->domain; + TRY_RESULT((archive.buffer().Read(DataTag{meta.value.id_}))); + return archive.buffer().Read(DataTag{meta.value.flags_}); +} +} // namespace seri } // namespace ae diff --git a/aether/obj/obj_ptr_base.h b/aether/obj/obj_ptr_base.h index 099fd0ef..f648c11b 100644 --- a/aether/obj/obj_ptr_base.h +++ b/aether/obj/obj_ptr_base.h @@ -17,17 +17,16 @@ #ifndef AETHER_OBJ_OBJ_PTR_BASE_H_ #define AETHER_OBJ_OBJ_PTR_BASE_H_ -#include "aether/obj/obj_id.h" +#include "aether-miscpp/serialization/serialization.h" + #include "aether/obj/domain.h" +#include "aether/obj/obj_id.h" namespace ae { class ObjectPtrBase { - friend imstream& operator>>( - imstream& is, ObjectPtrBase& ptr); - - friend omstream& operator<<( - omstream& os, ObjectPtrBase const& ptr); + friend struct seri::Serializer, + ObjectPtrBase>; public: ObjectPtrBase(); @@ -48,11 +47,16 @@ class ObjectPtrBase { ObjFlags flags_; }; -imstream& operator>>(imstream& is, - ObjectPtrBase& ptr); +namespace seri { +template <> +struct Serializer, ObjectPtrBase> { + using Archive = BinaryArchive; + + SeriResult Seri(Archive& archive, Meta meta) const; -omstream& operator<<(omstream& os, - ObjectPtrBase const& ptr); + SeriResult Deseri(Archive& archive, Meta meta) const; +}; +} // namespace seri } // namespace ae #endif // AETHER_OBJ_OBJ_PTR_BASE_H_ diff --git a/aether/obj/registrar.h b/aether/obj/registrar.h index efe6752c..932be7de 100644 --- a/aether/obj/registrar.h +++ b/aether/obj/registrar.h @@ -19,7 +19,7 @@ #include -#include "aether-miscpp/reflect/type_index.h" +#include "aether-miscpp/meta/type_index.h" #include "aether/ptr/ptr.h" #include "aether/obj/domain.h" @@ -37,7 +37,7 @@ class Registrar { Factory::LoadFunc(&Load), Factory::SaveFunc(&Save), #ifdef DEBUG - std::string{reflect::GetTypeName()}, + std::string{GetTypeName()}, cls_id, base_id, #endif // DEBUG diff --git a/aether/obj/version_iterator.h b/aether/obj/version_iterator.h index 0cec501a..1cb71847 100644 --- a/aether/obj/version_iterator.h +++ b/aether/obj/version_iterator.h @@ -183,12 +183,14 @@ static constexpr inline auto version_iterator = VersionIterator{}; template struct VersionNodeVisitor { - constexpr explicit VersionNodeVisitor(Visitor&& vis) - : visitor{std::move(vis)} {} + template + requires(!std::is_same_v, VersionNodeVisitor>) + constexpr explicit VersionNodeVisitor(U&& vis) + : visitor{std::forward(vis)} {} template void operator()(U&&... vals) { - (std::forward(visitor)(std::forward(vals)), ...); + (visitor(std::forward(vals)), ...); } Visitor visitor; diff --git a/aether/ptr/ptr.h b/aether/ptr/ptr.h index 7a307ebd..c5aad133 100644 --- a/aether/ptr/ptr.h +++ b/aether/ptr/ptr.h @@ -28,7 +28,8 @@ // IWYU pragma: begin_keeps #include "aether/type_traits.h" -#include "aether-miscpp/reflect/domain_visitor.h" +#include "aether-miscpp/domain_visitor/domain_visitor.h" + #include "aether/ptr/ptr_management.h" #include "aether/ptr/ref_tree.h" // IWYU pragma: end_keeps @@ -236,7 +237,7 @@ void PtrVisitChildren(PtrBase const* self, void* arg, assert(obj != nullptr && "Ptr is not initialized"); auto ref_visitor = RefVisitor{.arg = arg, .cb = cb}; - reflect::DomainVisit(*obj, PtrRefDnv{ref_visitor}); + domain_visitor::DomainVisit(*obj, PtrRefDnv{ref_visitor}); } template @@ -266,7 +267,7 @@ template Ptr MakePtr(TArgs&&... args) { constexpr auto size = sizeof(PtrStorage); static_assert(size < std::numeric_limits::max()); - static_assert(reflect::HasNodeVisitor::value, + static_assert(domain_visitor::HasNodeVisitor::value, "Type must be reflectable to be used in Ptr"); auto alloc = std::allocator{}; @@ -292,22 +293,23 @@ auto MakePtr(TArgs&&... args) { } // namespace ae -namespace ae::reflect { +namespace ae::domain_visitor { template struct NodeVisitor> { - using Policy = AnyPolicyMatch; + using Policy = PolicyMatch; void Visit(ae::Ptr& obj, CycleDetector& cycle_detector, - PtrRefDnv&& visitor) const { - reflect::ApplyVisitor(obj, cycle_detector, std::move(visitor)); + PtrRefDnv const& visitor) const { + domain_visitor::ApplyVisitor(obj, cycle_detector, visitor); } void Visit(ae::Ptr const& obj, CycleDetector& cycle_detector, - PtrRefDnv&& visitor) const { - reflect::ApplyVisitor(obj, cycle_detector, std::move(visitor)); + PtrRefDnv const& visitor) const { + domain_visitor::ApplyVisitor(obj, cycle_detector, visitor); } template + requires(!std::is_same_v>) void Visit(ae::Ptr const& obj, CycleDetector& cycle_detector, Visitor&& visitor) const { if (obj) { @@ -316,6 +318,7 @@ struct NodeVisitor> { } template + requires(!std::is_same_v>) void Visit(ae::Ptr& obj, CycleDetector& cycle_detector, Visitor&& visitor) const { if (obj) { @@ -326,10 +329,10 @@ struct NodeVisitor> { template void ApplyVisit(U&& obj, CycleDetector& cycle_detector, Visitor&& visitor) const { - reflect::ApplyVisitor(std::forward(obj), cycle_detector, - std::forward(visitor)); + domain_visitor::ApplyVisitor(std::forward(obj), cycle_detector, + std::forward(visitor)); } }; -} // namespace ae::reflect +} // namespace ae::domain_visitor #endif // AETHER_PTR_PTR_H_ diff --git a/aether/ptr/ptr_management.h b/aether/ptr/ptr_management.h index 2502e062..4468b389 100644 --- a/aether/ptr/ptr_management.h +++ b/aether/ptr/ptr_management.h @@ -22,7 +22,7 @@ #include #include -#include "aether-miscpp/reflect/domain_visitor.h" +#include "aether-miscpp/domain_visitor/domain_visitor.h" #include "aether-miscpp/types/aligned_storage.h" namespace ae { @@ -99,7 +99,8 @@ struct RefVisitor { }; using PtrRefDnv = - reflect::DomainNodeVisitor; + domain_visitor::DomainNodeVisitor; } // namespace ae #endif // AETHER_PTR_PTR_MANAGEMENT_H_ diff --git a/aether/server_keys.cpp b/aether/server_keys.cpp index 2ea06e55..06b9f1ce 100644 --- a/aether/server_keys.cpp +++ b/aether/server_keys.cpp @@ -23,7 +23,7 @@ ServerKeys::ServerKeys(ServerId server_id, const Key& master_key) : server_id_{server_id}, master_key_{master_key} { nonce_.Init(); nonce_.Next(); - Derive(server_id, master_key, key_number_); + Derive(server_id_, master_key, key_number_); } CryptoNonce const& ServerKeys::nonce() const { return nonce_; } diff --git a/aether/server_keys.h b/aether/server_keys.h index 2913dda6..398c28b8 100644 --- a/aether/server_keys.h +++ b/aether/server_keys.h @@ -17,14 +17,14 @@ #ifndef AETHER_SERVER_KEYS_H_ #define AETHER_SERVER_KEYS_H_ -#include #include +#include #include +#include "aether/crypto/crypto_nonce.h" #include "aether/crypto/key.h" #include "aether/types/server_id.h" -#include "aether/crypto/crypto_nonce.h" namespace ae { class ServerKeys { @@ -47,7 +47,7 @@ class ServerKeys { ServerId server_id_{}; Key master_key_; - TieredInt key_number_{}; + std::uint32_t key_number_{}; CryptoNonce nonce_; Key client_to_server_key_; Key server_to_client_key_; diff --git a/aether/stream_api/sized_packet_gate.cpp b/aether/stream_api/sized_packet_gate.cpp index cd6e9212..117398da 100644 --- a/aether/stream_api/sized_packet_gate.cpp +++ b/aether/stream_api/sized_packet_gate.cpp @@ -16,8 +16,9 @@ #include "aether/stream_api/sized_packet_gate.h" -#include "aether/mstream.h" -#include "aether/mstream_buffers.h" +#include + +#include "aether/vector_buffer.h" namespace ae { @@ -27,10 +28,10 @@ static constexpr std::size_t kSizedPacketOverhead = DataBuffer SizedPacketGate::WriteIn(DataBuffer&& buffer) { DataBuffer write_buffer; write_buffer.reserve(buffer.size() + kSizedPacketOverhead); - - auto buffer_writer = VectorWriter(write_buffer); - auto os = omstream{buffer_writer}; - os << buffer; + auto vec_buff = VectorBuffer{write_buffer}; + vec_buff.Write(seri::SizeWriteTag{buffer.size()}); + vec_buff.Write( + seri::DataWriteTag{std::move(buffer).data(), std::move(buffer).size()}); return write_buffer; } diff --git a/aether/tele_statistics.h b/aether/tele_statistics.h index 50db1e07..8b2e8d50 100644 --- a/aether/tele_statistics.h +++ b/aether/tele_statistics.h @@ -19,6 +19,7 @@ #include +#include "aether-miscpp/serialization/binary_archive.h" #include "aether-tele/traps/statistics_trap.h" #include "aether/config.h" @@ -59,6 +60,42 @@ class TeleStatistics : public Obj { std::shared_ptr trap_ = std::make_shared(); #endif }; + +#if AE_TELE_ENABLED && AE_TELE_LOG_TO_STATISTICS +namespace seri { +template +struct Serializer, tele::LogStorage> { + SeriResult Seri(BinaryArchive& archive, + Meta const> meta) const { + auto size = meta.value.size(); + TRY_RESULT(archive.buffer().Write(SizeTag{std::as_const(size)})); + + auto s = meta.value.start; + while (size > 0) { + auto to_write = (Capacity - s) > size ? size : Capacity - s; + + TRY_RESULT(archive.buffer().Write( + DataWriteTag{meta.value.buffer.data() + s, to_write})); + + s = (s + to_write) % Capacity; + size = size - to_write; + } + return Ok{seri::good}; + } + + SeriResult Deseri(BinaryArchive& archive, + Meta> meta) const { + std::size_t size; // NOLINT(*init-variables) + TRY_RESULT(archive.buffer().Read(SizeTag{size})); + assert(size < Capacity && "Saved buffer bigger than capacity"); + meta.value.start = 0; + meta.value.pos = size; + + return archive.buffer().Read(DataReadTag{meta.value.buffer.data(), size}); + } +}; +} // namespace seri +#endif } // namespace ae #endif // AETHER_TELE_TRAPS_TELE_STATISTICS_H_ diff --git a/aether/tiered_int_serializer.h b/aether/tiered_int_serializer.h new file mode 100644 index 00000000..eef3092d --- /dev/null +++ b/aether/tiered_int_serializer.h @@ -0,0 +1,74 @@ +/* + * Copyright 2026 Aethernet Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AETHER_TIERED_INT_SERIALIZER_H_ +#define AETHER_TIERED_INT_SERIALIZER_H_ + +#include "numeric/tiered_int.h" + +#include "aether-miscpp/serialization/binary_archive.h" + +namespace ae::seri { + +template +struct TIntWriter { + template + requires(std::is_integral_v) + TIntWriter& operator<<(T const& v) { + res = buffer.Write(seri::DataTag{v}); + return *this; + } + + B& buffer; + SeriResult res{Ok{seri::good}}; +}; + +template +struct TIntReader { + template + requires(std::is_integral_v) + TIntReader& operator>>(T& v) { + res = buffer.Read(seri::DataTag{v}); + return *this; + } + + B& buffer; + SeriResult res{Ok{seri::good}}; +}; + +template +struct Serializer, TieredInt> { + using Archive = BinaryArchive; + using TInt = TieredInt; + + SeriResult Seri(Archive& archive, Meta meta) const { + auto writer = TIntWriter{.buffer = archive.buffer()}; + meta.value.Serialize(writer); + return writer.res; + } + + SeriResult Deseri(Archive& archive, Meta meta) const { + auto reader = TIntReader{.buffer = archive.buffer()}; + auto r = meta.value.Deserialize(reader); + if (r != TierDeserializeRes::kFinished) { + return Error{read_eof}; + } + return reader.res; + } +}; +} // namespace ae::seri + +#endif // AETHER_TIERED_INT_SERIALIZER_H_ diff --git a/aether/transport/data_packet_collector.cpp b/aether/transport/data_packet_collector.cpp index adede00a..15c135ca 100644 --- a/aether/transport/data_packet_collector.cpp +++ b/aether/transport/data_packet_collector.cpp @@ -16,22 +16,22 @@ #include "aether/transport/data_packet_collector.h" -#include -#include #include +#include -#include "aether/mstream_buffers.h" -#include "aether/mstream.h" +#include "aether/vector_buffer.h" namespace ae { Packet::Packet(std::size_t expected_size) - : mem_buffer{static_cast(expected_size)}, - expected_packet_size{expected_size} {} + : mem_buffer{std::make_unique(expected_size)}, + expected_packet_size{expected_size}, + pos{} {} Packet::Packet(Packet&& other) noexcept : mem_buffer{std::move(other.mem_buffer)}, - expected_packet_size{other.expected_packet_size} {} + expected_packet_size{other.expected_packet_size}, + pos{other.pos} {} void StreamDataPacketCollector::AddData(std::uint8_t const* data, std::size_t size) { @@ -53,28 +53,21 @@ void StreamDataPacketCollector::AddData(std::uint8_t const* data, } } -void StreamDataPacketCollector::AddData(DataBuffer const& data_buffer) { - AddData(data_buffer.data(), data_buffer.size()); -} - std::vector StreamDataPacketCollector::PopPacket() { // no completed packet, return empty if (packets_.empty() || !IsPacketComplete(packets_.front())) { return {}; } auto& packet = packets_.front(); - std::vector data_packet(packet.expected_packet_size); - std::copy(packet.mem_buffer.Data(), - packet.mem_buffer.Data() + packet.mem_buffer.Size(), - std::begin(data_packet)); + std::vector data_packet(packet.mem_buffer.get(), + packet.mem_buffer.get() + packet.pos); packets_.pop(); return data_packet; } bool StreamDataPacketCollector::IsPacketComplete(Packet const& packet) { - return packet.mem_buffer.Size() == - static_cast(packet.expected_packet_size); + return packet.pos == packet.expected_packet_size; } std::pair StreamDataPacketCollector::GetPacketSize( @@ -88,33 +81,29 @@ std::pair StreamDataPacketCollector::GetPacketSize( temp_data_buffer_.insert(temp_data_buffer_.end(), data, data + use_max_size); - VectorReader reader(temp_data_buffer_); - auto is = imstream{reader}; + VectorBuffer vec_buffer(temp_data_buffer_); + std::size_t packet_size{}; - PacketSize packet_size; - is >> packet_size; - if (!data_was_read(is)) { + if (!vec_buffer.Read(seri::SizeReadTag{packet_size})) { return {0, size}; } temp_data_buffer_.clear(); - assert((temp_buffer_size + size) >= reader.offset_); - return {static_cast(packet_size), - reader.offset_ - temp_buffer_size}; + assert((temp_buffer_size + size) >= vec_buffer.read_offset); + return {packet_size, vec_buffer.read_offset - temp_buffer_size}; } std::size_t StreamDataPacketCollector::WriteToPacket(Packet& packet, std::uint8_t const* data, std::size_t size) { - auto avail_cap = packet.mem_buffer.AvailableCapacity(); - auto write_size = avail_cap > static_cast(size) - ? static_cast(size) - : avail_cap; + auto avail_cap = packet.expected_packet_size - packet.pos; + auto write_size = avail_cap > size ? size : avail_cap; - packet.mem_buffer.sputn(reinterpret_cast(data), write_size); + std::memcpy(packet.mem_buffer.get() + packet.pos, data, write_size); + packet.pos += write_size; - return static_cast(write_size); + return write_size; } } // namespace ae diff --git a/aether/transport/data_packet_collector.h b/aether/transport/data_packet_collector.h index d4c71411..17bdb41f 100644 --- a/aether/transport/data_packet_collector.h +++ b/aether/transport/data_packet_collector.h @@ -19,13 +19,13 @@ #include #include -#include +#include #include #include +#include #include -#include "aether/memory_buffer.h" #include "aether/types/data_buffer.h" namespace ae { @@ -37,15 +37,15 @@ struct Packet { Packet(Packet const&) = delete; Packet(Packet&& other) noexcept; - MemStreamBuf<> mem_buffer; + std::unique_ptr mem_buffer; std::size_t expected_packet_size; + std::size_t pos; }; class StreamDataPacketCollector { public: // fill packets in queue with provided stream data_buffer void AddData(std::uint8_t const* data, std::size_t size); - void AddData(DataBuffer const& data_buffer); // pops a packet data if any, else return empty std::vector PopPacket(); @@ -55,8 +55,8 @@ class StreamDataPacketCollector { std::pair GetPacketSize(std::uint8_t const* data, std::size_t size); // return data offset - std::size_t WriteToPacket(Packet& packet, std::uint8_t const* data, - std::size_t size); + static std::size_t WriteToPacket(Packet& packet, std::uint8_t const* data, + std::size_t size); std::queue packets_; // used if packet is not complete to get packet size diff --git a/aether/transport/modems/modem_transport.cpp b/aether/transport/modems/modem_transport.cpp index 7c90bdd7..8b1a0b73 100644 --- a/aether/transport/modems/modem_transport.cpp +++ b/aether/transport/modems/modem_transport.cpp @@ -18,8 +18,7 @@ #if MODEM_TRANSPORT_ENABLED -# include "aether/mstream.h" -# include "aether/mstream_buffers.h" +# include "aether/vector_buffer.h" # include "aether/write_action/failed_write_action.h" # include "aether/transport/transport_tele.h" @@ -191,11 +190,10 @@ WriteAction& ModemTransport::WriteTcp(DataBuffer&& in_data) { // Make TCP packet with its size at the beginning auto packet_data = std::vector{}; - packet_data.reserve(in_data.size() + 4); - VectorWriter vw{packet_data}; - auto os = omstream{vw}; - // copy data with size - os << std::move(in_data); // NOLINT + VectorBuffer vw{packet_data}; + vw.Write(seri::SizeWriteTag(in_data.size())); + vw.Write( + seri::DataWriteTag{std::move(in_data).data(), std::move(in_data).size()}); auto* send_action = send_queue_manager.AddPacket(ae_context_, *this, std::move(packet_data)); diff --git a/aether/transport/system_sockets/sockets/get_sock_addr.cpp b/aether/transport/system_sockets/sockets/get_sock_addr.cpp index f6ccf085..0c80d4c5 100644 --- a/aether/transport/system_sockets/sockets/get_sock_addr.cpp +++ b/aether/transport/system_sockets/sockets/get_sock_addr.cpp @@ -20,14 +20,15 @@ # include +# include "aether-miscpp/misc/override.h" + # include "aether/env.h" -# include "aether-miscpp/reflect/override_func.h" namespace ae { SockAddr GetSockAddr(AddressPort const& ip_address_port) { SockAddr sock_addr{}; - std::visit(reflect::OverrideFunc{ + std::visit(Override{ # if AE_SUPPORT_IPV4 == 1 [&](IpV4Addr const& ipv4) { sock_addr.size = sizeof(sock_addr.data.ipv4); diff --git a/aether/transport/system_sockets/sockets/lwip_get_addr.cpp b/aether/transport/system_sockets/sockets/lwip_get_addr.cpp index 89fc53d5..89beb448 100644 --- a/aether/transport/system_sockets/sockets/lwip_get_addr.cpp +++ b/aether/transport/system_sockets/sockets/lwip_get_addr.cpp @@ -18,15 +18,16 @@ #if (defined(ESP_PLATFORM)) -# include # include +# include +# include "aether-miscpp/misc/override.h" # include "aether-miscpp/reflect/reflect.h" namespace ae { std::optional LwipGetAddr(const AddressPort& addr_port) { return std::visit( - reflect::OverrideFunc{ + Override{ # if AE_SUPPORT_IPV4 [&](IpV4Addr const& ipv4) -> std::optional { ip_addr_t lwip_addr; diff --git a/aether/transport/system_sockets/tcp/tcp.cpp b/aether/transport/system_sockets/tcp/tcp.cpp index e57de5ca..0a1803d1 100644 --- a/aether/transport/system_sockets/tcp/tcp.cpp +++ b/aether/transport/system_sockets/tcp/tcp.cpp @@ -18,8 +18,8 @@ #if defined SYSTEM_SOCKET_TCP_TRANSPORT_ENABLED -# include # include +# include # include "aether/transport/transport_tele.h" @@ -81,8 +81,9 @@ void TcpBase::OnRecvData(Span data) { read_event_ = false; for (auto data = data_packet_collector_.PopPacket(); !data.empty(); data = data_packet_collector_.PopPacket()) { - AE_TELE_DEBUG(kTcpTransportReceive, "Socket {} received data size {}", - endpoint_, data.size()); + AE_TELE_DEBUG(kTcpTransportReceive, + "Socket {} received data size {} \ndata{}", endpoint_, + data.size(), data); out_data_event_.Emit(data); } }); diff --git a/aether/transport/system_sockets/tcp/tcp.h b/aether/transport/system_sockets/tcp/tcp.h index ff8f18d4..7f871c22 100644 --- a/aether/transport/system_sockets/tcp/tcp.h +++ b/aether/transport/system_sockets/tcp/tcp.h @@ -28,19 +28,18 @@ # include # include -# include "aether/common.h" # include "aether/ae_context.h" -# include "aether/poller/poller.h" +# include "aether/common.h" # include "aether/events/multi_subscription.h" +# include "aether/poller/poller.h" +# include "aether/vector_buffer.h" -# include "aether/mstream.h" -# include "aether/mstream_buffers.h" # include "aether/stream_api/istream.h" -# include "aether/transport/packet_send_action.h" -# include "aether/transport/packet_queue_manager.h" # include "aether/transport/data_packet_collector.h" -# include "aether/write_action/failed_write_action.h" +# include "aether/transport/packet_queue_manager.h" +# include "aether/transport/packet_send_action.h" # include "aether/transport/system_sockets/sockets/isocket.h" +# include "aether/write_action/failed_write_action.h" # include "aether/transport/transport_tele.h" @@ -175,10 +174,10 @@ class TcpTransport final : public tcp_internal::TcpBase { in_data.size()); auto packet_data = std::vector{}; - VectorWriter vw{packet_data}; - auto os = omstream{vw}; - // copy data with size - os << std::move(in_data); // NOLINT + VectorBuffer vw{packet_data}; + vw.Write(seri::SizeWriteTag(in_data.size())); + vw.Write(seri::DataWriteTag{std::move(in_data).data(), + std::move(in_data).size()}); auto* send_action = queue_manager_.AddPacket(ae_context_, socket_, std::move(packet_data)); diff --git a/aether/types/address.h b/aether/types/address.h index 6a972355..94733342 100644 --- a/aether/types/address.h +++ b/aether/types/address.h @@ -24,8 +24,12 @@ #include #include -#include "aether/config.h" #include "aether-miscpp/reflect/reflect.h" +#include "aether-miscpp/serialization/binary_archive.h" +#include "aether-miscpp/serialization/serialization.h" + +#include "aether/config.h" +#include "aether/obj/domain.h" #include "aether/types/variant_type.h" #include "aether-miscpp/format/format.h" @@ -47,17 +51,18 @@ bool operator==(IpV4Addr const& left, IpV4Addr const& right); bool operator!=(IpV4Addr const& left, IpV4Addr const& right); bool operator<(IpV4Addr const& left, IpV4Addr const& right); -template -imstream& operator>>(imstream& s, IpV4Addr& ipv4) { - s >> ipv4.ipv4_value; - return s; -} - -template -omstream& operator<<(omstream& s, IpV4Addr const& ipv4) { - s << ipv4.ipv4_value; - return s; -} +namespace seri { +template +struct Serializer { + SeriResult Seri(A& archive, Meta meta) const { + return archive.Save( + Meta{meta.value.ipv4_value, "value"}); + } + SeriResult Deseri(A& archive, Meta meta) const { + return archive.Load(Meta{meta.value.ipv4_value, "value"}); + } +}; +} // namespace seri struct IpV6Addr { std::uint8_t ipv6_value[16]; @@ -67,17 +72,18 @@ bool operator==(IpV6Addr const& left, IpV6Addr const& right); bool operator!=(IpV6Addr const& left, IpV6Addr const& right); bool operator<(IpV6Addr const& left, IpV6Addr const& right); -template -imstream& operator>>(imstream& s, IpV6Addr& ipv6) { - s >> ipv6.ipv6_value; - return s; -} - -template -omstream& operator<<(omstream& s, IpV6Addr const& ipv6) { - s << ipv6.ipv6_value; - return s; -} +namespace seri { +template +struct Serializer { + SeriResult Seri(A& archive, Meta meta) const { + return archive.Save( + Meta{meta.value.ipv6_value, "value"}); + } + SeriResult Deseri(A& archive, Meta meta) const { + return archive.Load(Meta{meta.value.ipv6_value, "value"}); + } +}; +} // namespace seri struct NamedAddr { AE_REFLECT_MEMBERS(name) @@ -154,9 +160,9 @@ struct Formatter { #if AE_SUPPORT_IPV6 == 1 char buffer[2]{}; for (std::size_t i = 0; i < 16; i++) { - auto result = std::to_chars(buffer, buffer + 2, - static_cast(value.ipv6_value[i]), - 16); + auto result = + std::to_chars(buffer, buffer + 2, + static_cast(value.ipv6_value[i]), 16); ctx.out().write(std::string_view{ buffer, static_cast(result.ptr - buffer)}); if (i < 15) { diff --git a/aether/types/nullable_type.h b/aether/types/nullable_type.h index aa7d7b1b..32c13ca8 100644 --- a/aether/types/nullable_type.h +++ b/aether/types/nullable_type.h @@ -17,14 +17,18 @@ #ifndef AETHER_TYPES_NULLABLE_TYPE_H_ #define AETHER_TYPES_NULLABLE_TYPE_H_ -#include #include #include -#include #include +#include +#include +#include +#include +#include -#include "aether/type_traits.h" #include "aether-miscpp/reflect/reflect.h" +#include "aether-miscpp/serialization/serialization.h" +#include "aether/type_traits.h" namespace ae { template @@ -40,35 +44,33 @@ class NullableValues { public: static constexpr std::size_t kBitsCount = sizeof...(TArgs); - using ValueType = + using MaskType = decltype(SelectValueType()); explicit NullableValues(TArgs&... args) : arg_refs_{args...} {} - template - void Load(Stream& is) { - ValueType mask_value{}; - is >> mask_value; - LoadValue(is, std::bitset{mask_value}, - std::make_index_sequence()); + seri::SeriResult Seri(seri::Archive auto& archive) const { + TRY_RESULT(archive.Save(BuildMask())); + return SeriValues(archive, std::make_index_sequence{}); } - template - void Save(Stream& os) const { - auto value = BuildMask(); - os << value; - SaveValues(os, std::make_index_sequence()); + seri::SeriResult Deseri(seri::Archive auto& archive) { + MaskType mask_value{}; + TRY_RESULT(archive.Load(seri::Meta{mask_value})); + return DeseriValues(archive, std::bitset{mask_value}, + std::make_index_sequence{}); } private: - ValueType BuildMask() const { + MaskType BuildMask() const { return BuildMaskImpl(std::make_index_sequence()); } template - ValueType BuildMaskImpl(std::index_sequence) const { + constexpr MaskType BuildMaskImpl(std::index_sequence) const { std::bitset mask; + (std::invoke([&]() { auto& v = std::get(arg_refs_); if constexpr (IsOptional>::value) { @@ -76,42 +78,56 @@ class NullableValues { } }), ...); - return static_cast(mask.to_ulong()); + return static_cast(mask.to_ulong()); } - template - void SaveValues(Stream& os, std::index_sequence) const { - (std::invoke([&]() { - auto& v = std::get(arg_refs_); - if constexpr (IsOptional>::value) { - if (v.has_value()) { - os << v.value(); - } - } else { - os << v; - } - }), - ...); + template + seri::SeriResult SeriValue(seri::Archive auto& archive) const { + auto const& value = std::get(arg_refs_); + if constexpr (IsOptional>::value) { + if (value.has_value()) { + return archive.Save(value.value()); + } + return Ok{seri::good}; + } else { + return archive.Save(value); + } } - template - void LoadValue(Stream& is, std::bitset mask, - std::index_sequence) { - (std::invoke([&]() { - auto& v = std::get(arg_refs_); - using VType = std::decay_t; - if constexpr (IsOptional::value) { - // if not it's not nullable - if (!mask[Is]) { - typename VType::value_type value; - is >> value; - v = value; - } - } else { - is >> v; - } - }), - ...); + template + seri::SeriResult SeriValues(seri::Archive auto& archive, + std::index_sequence) const { + seri::SeriResult result{Ok{seri::good}}; + ((result.IsOk() ? result = SeriValue(archive) : result), ...); + return result; + } + + template + seri::SeriResult DeseriValue(seri::Archive auto& archive, + std::bitset const& mask) { + auto& value = std::get(arg_refs_); + using ValueType = std::decay_t; + if constexpr (IsOptional::value) { + if (!mask[I]) { + typename ValueType::value_type loaded_value{}; + TRY_RESULT(archive.Load(loaded_value)); + value = std::move(loaded_value); + } else { + value = std::nullopt; + } + return Ok{seri::good}; + } else { + return archive.Load(value); + } + } + + template + seri::SeriResult DeseriValues(seri::Archive auto& archive, + std::bitset const& mask, + std::index_sequence) { + seri::SeriResult result{Ok{seri::good}}; + ((result.IsOk() ? result = DeseriValue(archive, mask) : result), ...); + return result; } std::tuple arg_refs_; @@ -129,16 +145,14 @@ class NullableValues { template class NullableType { public: - template - void Load(Stream& is) { + seri::SeriResult Seri(seri::Archive auto& archive) const { auto values = BuildNullableValues(*this); - values.Load(is); + return values.Seri(archive); } - template - void Save(Stream& os) const { - auto values = BuildNullableValues(const_cast(*this)); - values.Save(os); + seri::SeriResult Deseri(seri::Archive auto& archive) { + auto values = BuildNullableValues(*this); + return values.Deseri(archive); } private: @@ -160,16 +174,17 @@ class NullableType { template static auto BuildArgList(U& obj) { - auto refl = reflect::Reflection{obj}; + auto refl = reflect::make_reflection(obj); return refl.Apply( [](auto&... fields) { return BuildArgListImpl(fields...); }); } template static auto BuildNullableValues(TSelf& self) { - static_assert(reflect::IsReflectable::value, - "T must be reflecatable type"); - auto args_list = BuildArgList(static_cast(self)); + static_assert(reflect::Reflectable, "T must be reflecatable type"); + using ReflectedType = + std::conditional_t, T const, T>; + auto args_list = BuildArgList(static_cast(self)); return std::apply([](auto&... args) { return NullableValues{args...}; }, args_list); } diff --git a/aether/types/statistic_counter.h b/aether/types/statistic_counter.h index 29434b3a..44078232 100644 --- a/aether/types/statistic_counter.h +++ b/aether/types/statistic_counter.h @@ -31,13 +31,16 @@ IGNORE_IMPLICIT_CONVERSION() DISABLE_WARNING_POP() #include "aether-miscpp/format/format.h" +#include "aether-miscpp/serialization/binary_archive.h" + #include "aether/common.h" -#include "aether/mstream.h" namespace ae { template > class StatisticsCounter final { + template + friend struct seri::Serializer; friend struct Formatter>; public: @@ -98,33 +101,38 @@ class StatisticsCounter final { std::size_t size() const { return value_buffer_.size(); } bool empty() const { return value_buffer_.empty(); } - template - friend imstream& operator>>(imstream& is, StatisticsCounter& value) { - typename Ib::size_type size; - is >> size; - for (std::size_t i = 0; (i < static_cast(size)) && - (i < value.value_buffer_.max_size()); - ++i) { - TValue temp; - is >> temp; - value.value_buffer_.push(std::move(temp)); + private: + etl::circular_buffer value_buffer_; +}; + +namespace seri { +template +struct Serializer, + StatisticsCounter> { + using SCounter = StatisticsCounter; + + SeriResult Seri(BinaryArchive& archive, Meta meta) const { + TRY_RESULT(archive.buffer().Write(SizeTag{ + static_cast(meta.value.value_buffer_.size())})); + for (auto const& v : meta.value.value_buffer_) { + TRY_RESULT(archive.Save(Meta{v})); } - return is; + return Ok{seri::good}; } - template - friend omstream& operator<<(omstream& os, - StatisticsCounter const& value) { - os << static_cast(value.value_buffer_.size()); - for (auto const& v : value.value_buffer_) { - os << v; + SeriResult Deseri(BinaryArchive& archive, Meta meta) const { + std::size_t size{}; + TRY_RESULT(archive.buffer().Read(SizeTag{size})); + for (std::size_t i = 0; + (i < size) && (i < meta.value.value_buffer_.max_size()); ++i) { + T temp; + TRY_RESULT(archive.Load(Meta{temp})); + meta.value.value_buffer_.push(std::move(temp)); } - return os; + return Ok{seri::good}; } - - private: - etl::circular_buffer value_buffer_; }; +} // namespace seri /** * \brief Formatter implementation. diff --git a/aether/types/variant_type.h b/aether/types/variant_type.h index 5c93ce5f..3c58799d 100644 --- a/aether/types/variant_type.h +++ b/aether/types/variant_type.h @@ -17,14 +17,13 @@ #ifndef AETHER_TYPES_VARIANT_TYPE_H_ #define AETHER_TYPES_VARIANT_TYPE_H_ -#include #include +#include #include #include -#include -#include "aether/mstream.h" #include "aether-miscpp/meta/type_list.h" +#include "aether-miscpp/serialization/serialization.h" namespace ae { template @@ -42,69 +41,45 @@ class VariantType : public std::variant { public: using index_type = IndexType; using Variant = std::variant; + static constexpr std::size_t kSize = sizeof...(Variants); // use all variant constructors using Variant::Variant; using Variant::operator=; - private: - template - static constexpr index_type GetIndexByOrder(std::size_t order, - std::index_sequence) { - index_type res{}; - ( - [&]() { - if (order == Is) { - res = TypeAt_t>::Index; - } - }(), - ...); - return res; - } - - template - static constexpr std::size_t GetOrderByIndex(index_type index, - std::index_sequence) { - std::size_t res{}; - ( - [&]() { - if (index == TypeAt_t>::Index) { - res = Is; - } - }(), - ...); - return res; + // Get currently stored variant index + constexpr auto Index() const { + return GetIndexByOrder(this->index(), + std::make_index_sequence()); } - template - static bool LoadElement(Stream& stream, Variant& var) { - using T = std::variant_alternative_t; - T t{}; - stream >> t; - var = std::move(t); - return true; + template + constexpr auto const& Get() const { + static_assert((std::is_same_v || ...), + "Type not found"); + return GetImpl(std::make_index_sequence()); } - template - static void Load(Stream& stream, std::size_t order, Variant& var, - std::index_sequence const&) { - (std::invoke([&]() { - if (order == Is) { - LoadElement(stream, var); - } - }), - ...); + template + static constexpr IndexType GetIndexByOrder(std::size_t order, + std::index_sequence) { + constexpr auto index_arr = + std::array{TypeAt_t>::Index...}; + return index_arr[order]; } - template - static void Save(Stream& stream, std::size_t order, Variant const& var, - std::index_sequence const&) { - (std::invoke([&]() { - if (order == Is) { - stream << std::get(var); - } - }), - ...); + template + static constexpr std::size_t GetOrderByIndex(IndexType index, + std::index_sequence) { + std::size_t res{}; + bool found = (((TypeAt_t>::Index == index) + ? (res = Is, true) + : false) || + ...); + if (found) { + return res; + } + return kSize; } template @@ -117,38 +92,50 @@ class VariantType : public std::variant { } } - public: - // Get currently stored variant index - constexpr auto Index() const { - return GetIndexByOrder(this->index(), - std::make_index_sequence()); + // serialization + template + static seri::SeriResult Load(seri::Archive auto& archive, std::size_t order, + Variant& val) { + if constexpr (I >= kSize) { + return Error{seri::invalid_variant_index}; + } else { + if (I == order) { + auto& ref = val.template emplace(); + TRY_RESULT(archive.Load(ref)); + return Ok{seri::good}; + } + return Load(archive, order, val); + } } - template - constexpr auto const& Get() const { - static_assert((std::is_same_v || ...), - "Type not found"); - return GetImpl(std::make_index_sequence()); + template + static seri::SeriResult Save(seri::Archive auto& archive, std::size_t order, + Variant const& val) { + if constexpr (I >= kSize) { + return Error{seri::invalid_variant_index}; + } else { + if (I == order) { + return archive.Save(std::get(val)); + } + return Save(archive, order, val); + } } - template - friend imstream operator>>(imstream& is, VariantType& v) { - index_type index{}; - is >> index; - auto order = - GetOrderByIndex(index, std::make_index_sequence()); - assert(order < sizeof...(Variants)); - Load(is, order, v, std::make_index_sequence()); - return is; + seri::SeriResult Seri(seri::Archive auto& archive) const { + auto order = this->index(); + TRY_RESULT((archive.Save( + GetIndexByOrder(order, std::make_index_sequence())))); + + return Save(archive, order, *this); } - template - friend omstream operator<<(omstream& os, VariantType const& v) { - auto order = v.index(); - os << GetIndexByOrder(order, - std::make_index_sequence()); - Save(os, order, v, std::make_index_sequence()); - return os; + seri::SeriResult Deseri(seri::Archive auto& archive) { + IndexType index{}; + TRY_RESULT((archive.Load(seri::Meta{index}))); + + auto order = GetOrderByIndex(index, std::make_index_sequence()); + + return Load(archive, order, *this); } }; } // namespace ae diff --git a/aether/vector_buffer.h b/aether/vector_buffer.h new file mode 100644 index 00000000..2550cc9f --- /dev/null +++ b/aether/vector_buffer.h @@ -0,0 +1,79 @@ +/* + * Copyright 2026 Aethernet Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AETHER_VECTOR_BUFFER_H_ +#define AETHER_VECTOR_BUFFER_H_ + +#include +#include +#include +#include + +#include // IWYU pragma: keep +#include "aether-miscpp/serialization/binary_archive.h" + +#include "aether/tiered_int_serializer.h" + +namespace ae { + +template +struct VectorBuffer { + using PackedSize = TieredInt; + + seri::SeriResult Write(seri::SizeWriteTag tag) { + auto v = PackedSize{tag.size}; + auto writer = seri::TIntWriter{.buffer = *this}; + v.Serialize(writer); + return writer.res; + } + + seri::SeriResult Write(seri::DataWriteTag tag) { + buff.insert(std::end(buff), static_cast(tag.data), + static_cast(tag.data) + tag.size); + return Ok{seri::good}; + } + + seri::SeriResult Read(seri::SizeReadTag tag) { + auto v = PackedSize{}; + auto reader = seri::TIntReader{.buffer = *this}; + TierDeserializeRes r = v.Deserialize(reader); + if (r != TierDeserializeRes::kFinished) { + return Error{seri::read_eof}; + } + if (!reader.res) { + return reader.res; + } + tag.size = static_cast(v); + return Ok{seri::good}; + } + + seri::SeriResult Read(seri::DataReadTag tag) { + if (tag.size > buff.size() - read_offset) { + return Error{seri::read_eof}; + } + + std::memcpy(tag.data, buff.data() + read_offset, tag.size); + read_offset += tag.size; + + return Ok{seri::good}; + } + + std::vector& buff; + std::size_t read_offset{0}; +}; +} // namespace ae + +#endif // AETHER_VECTOR_BUFFER_H_ diff --git a/opencode.json b/opencode.json deleted file mode 100644 index df64ef17..00000000 --- a/opencode.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://opencode.ai/config.json", - "model": "openai/gpt-5.5-fast", - "small_model": "openai/gpt-5.4-mini-fast", - "default_agent": "team-lead" -} diff --git a/tests/test-api-protocol/assert_packet.h b/tests/test-api-protocol/assert_packet.h index bbb8b491..54f1bd0c 100644 --- a/tests/test-api-protocol/assert_packet.h +++ b/tests/test-api-protocol/assert_packet.h @@ -19,12 +19,13 @@ #include -#include #include +#include + +#include "aether-miscpp/serialization/binary_archive.h" -#include "aether/mstream.h" -#include "aether/mstream_buffers.h" #include "aether/api_protocol/api_message.h" +#include "aether/tiered_int_serializer.h" // IWYU pragma: export #if defined(__clang__) || defined(__GNUC__) # define FUNCTION_NAME __PRETTY_FUNCTION__ @@ -37,29 +38,32 @@ template struct Skip {}; -using imstream_t = ae::imstream>; - -template -void AssertPacketEntry(imstream_t& is, T const& t) { +template +void AssertPacketEntry(A& a, T const& t) { T temp; - is >> temp; + a.Load(temp); TEST_ASSERT_MESSAGE(t == temp, FUNCTION_NAME); } -template -void AssertPacketEntry(imstream_t& is, Skip const&) { +template +void AssertPacketEntry(A& a, Skip const&) { T temp; - is >> temp; + a.Load(temp); } template void AssertPacket(std::vector const& data, Args const&... args) { auto const* old_file = Unity.TestFile; UnitySetTestFile(__FILE__); - ae::VectorReader ib{data}; - imstream_t is{ib}; - (AssertPacketEntry(is, args), ...); - TEST_ASSERT_EQUAL_MESSAGE(data.size(), ib.offset_, FUNCTION_NAME); + + auto archive = ae::seri::BinaryArchive{ + ae::VectorBuffer{ + const_cast&>(data)}, // NOLINT + }; + + (AssertPacketEntry(archive, args), ...); + TEST_ASSERT_EQUAL_MESSAGE(data.size(), archive.buffer().read_offset, + FUNCTION_NAME); UnitySetTestFile(old_file); } diff --git a/tests/test-api-protocol/test-method-call.cpp b/tests/test-api-protocol/test-method-call.cpp index 7b57d11d..03053f4e 100644 --- a/tests/test-api-protocol/test-method-call.cpp +++ b/tests/test-api-protocol/test-method-call.cpp @@ -67,7 +67,6 @@ class ApiLevel0 : public ApiClassImpl { api_level1{protocol_context}, method_3{protocol_context}, method_4{protocol_context}, - method_5{protocol_context, api_level1}, method_6{protocol_context, Method6Proc{api_level1}}, return_result_api{protocol_context} {} @@ -76,9 +75,6 @@ class ApiLevel0 : public ApiClassImpl { void Method4Impl(PromiseResult promise, int a) { method_4_event.Emit(promise.request_id, a); } - void Method5Impl(SubContextImpl sub_api, int a) { - sub_api.Parse(api_level1); - } void Method6Impl(int a, SubApiImpl sub_api) { sub_api.Parse(api_level1); } @@ -87,14 +83,12 @@ class ApiLevel0 : public ApiClassImpl { AE_METHODS(RegMethod<03, &ApiLevel0::Method3Impl>, RegMethod<04, &ApiLevel0::Method4Impl>, - RegMethod<05, &ApiLevel0::Method5Impl>, RegMethod<06, &ApiLevel0::Method6Impl>, ExtApi<&ApiLevel0::return_result_api>); // call methods to make packet Method<03, void(int a, std::string b)> method_3; Method<04, ApiPromise(int a)> method_4; - Method<05, SubContext(int a)> method_5; Method<06, void(int a, SubApi sub), Method6Proc> method_6; // to signal method impl is called @@ -104,38 +98,6 @@ class ApiLevel0 : public ApiClassImpl { ApiLevel1 api_level1; }; -void test_ApiMethodInvoke() { - ProtocolContext pc; - - auto api_level0 = ApiLevel0{pc}; - auto call_context = ApiContext{api_level0}; - - call_context->method_3(12, "asd"); - - call_context->method_5(54)->method_3(451.F); - - DataBuffer packet = std::move(call_context); - - AssertPacket(packet, MessageId{3}, int{12}, std::string{"asd"}, MessageId{5}, - int{54}, Skip{}, MessageId{3}, float{451.F}); - - bool level0_method3_called = false; - bool level1_method3_called = false; - - EventSubscriber{api_level0.method_3_event}.Subscribe( - [&](int, std::string const&) { level0_method3_called = true; }); - - EventSubscriber{api_level0.api_level1.method_3_event}.Subscribe( - [&](int) { level1_method3_called = true; }); - - // send/receive - auto parser = ApiParser{pc, packet}; - parser.Parse(api_level0); - - TEST_ASSERT(level0_method3_called); - TEST_ASSERT(level1_method3_called); -} - void test_ReturnResult() { ProtocolContext pc; @@ -414,7 +376,6 @@ void test_PendingResponseDuplicateReplacementPreservesFifo() { int test_method_call() { UNITY_BEGIN(); - RUN_TEST(ae::test_method_call::test_ApiMethodInvoke); RUN_TEST(ae::test_method_call::test_ReturnResult); RUN_TEST(ae::test_method_call::test_MethodWithSubApi); RUN_TEST(ae::test_method_call::test_ProtocolContextStackAccess); diff --git a/tests/test-domain-storage/test_ds_synchronization.cpp b/tests/test-domain-storage/test_ds_synchronization.cpp index 68c26565..afe3357a 100644 --- a/tests/test-domain-storage/test_ds_synchronization.cpp +++ b/tests/test-domain-storage/test_ds_synchronization.cpp @@ -42,12 +42,10 @@ auto GetFsDomainStorage() { std::vector DataGetter( std::unique_ptr& reader) { - std::uint8_t size; - reader->read(&size, sizeof(size)); - std::vector res; - res.resize(size + 1); - res[0] = size; - reader->read(res.data() + 1, res.size() - 1); + std::size_t size{}; + TEST_ASSERT(reader->Read(ae::seri::SizeReadTag{size})); + auto res = std::vector(size); + TEST_ASSERT(reader->Read(ae::seri::DataReadTag{res.data(), res.size()})); return res; } @@ -55,10 +53,17 @@ static constexpr auto classes_1 = std::array{100, 101}; static constexpr auto classes_2 = std::array{200}; static constexpr auto classes_3 = std::array{300, 302}; -static constexpr auto data_1 = std::array{4, 251, 12, 42, 11}; -static constexpr auto data_2 = std::array{4, 252, 13, 42, 11}; -static constexpr auto data_3 = std::array{4, 253, 14, 42, 11}; -static constexpr auto data_4 = std::array{4, 254, 15, 42, 11}; +static constexpr auto data_1 = std::array{251, 12, 42, 11}; +static constexpr auto data_2 = std::array{252, 13, 42, 11}; +static constexpr auto data_3 = std::array{253, 14, 42, 11}; +static constexpr auto data_4 = std::array{254, 15, 42, 11}; + +static constexpr auto storage_data_1 = + std::array{4, 0, 0, 0, 251, 12, 42, 11}; +static constexpr auto storage_data_2 = + std::array{4, 0, 0, 0, 252, 13, 42, 11}; +static constexpr auto storage_data_3 = + std::array{4, 0, 0, 0, 253, 14, 42, 11}; static constexpr auto static_data = StaticDomainData{ // object map @@ -68,9 +73,9 @@ static constexpr auto static_data = StaticDomainData{ }}, // data map StaticMap{{ - std::pair{ObjectPathKey{1, 100, 0}, Span{data_1}}, - std::pair{ObjectPathKey{1, 101, 0}, Span{data_2}}, - std::pair{ObjectPathKey{2, 200, 0}, Span{data_3}}, + std::pair{ObjectPathKey{1, 100, 0}, Span{storage_data_1}}, + std::pair{ObjectPathKey{1, 101, 0}, Span{storage_data_2}}, + std::pair{ObjectPathKey{2, 200, 0}, Span{storage_data_3}}, }}, }; @@ -109,7 +114,9 @@ void TestSyncDataStorage(std::unique_ptr sds, // add data and get after { auto writer_2_201 = data_storage.Store({ObjId{2}, 201, 0}); - writer_2_201->write(data_4.data(), data_4.size()); + TEST_ASSERT(writer_2_201->Write(ae::seri::SizeWriteTag{data_4.size()})); + TEST_ASSERT(writer_2_201->Write( + ae::seri::DataWriteTag{data_4.data(), data_4.size()})); writer_2_201.reset(); auto load_2_201 = data_storage.Load({ObjId{2}, 201, 0}); @@ -131,7 +138,9 @@ void TestSyncDataStorage(std::unique_ptr sds, // add an object { auto writer_3_300 = data_storage.Store({ObjId{3}, 300, 0}); - writer_3_300->write(data_4.data(), data_4.size()); + TEST_ASSERT(writer_3_300->Write(ae::seri::SizeWriteTag{data_4.size()})); + TEST_ASSERT(writer_3_300->Write( + ae::seri::DataWriteTag{data_4.data(), data_4.size()})); writer_3_300.reset(); auto load_3_300 = data_storage.Load({ObjId{3}, 300, 0}); diff --git a/tests/test-object-system/map_domain_storage.cpp b/tests/test-object-system/map_domain_storage.cpp index 910c98e5..9f04c760 100644 --- a/tests/test-object-system/map_domain_storage.cpp +++ b/tests/test-object-system/map_domain_storage.cpp @@ -16,39 +16,57 @@ #include "tests/test-object-system/map_domain_storage.h" -#include "aether/mstream_buffers.h" +#include +#include namespace ae { class MapDomainStorageWriter final : public IDomainStorageWriter { public: MapDomainStorageWriter(DomainQuery q, MapDomainStorage& s) - : query{std::move(q)}, storage{&s}, vector_writer{data} {} + : query{std::move(q)}, storage{&s} {} ~MapDomainStorageWriter() override { - storage->SaveData(query, std::move(data)); + storage->SaveData(query, std::move(data_buffer)); } - void write(void const* data, std::size_t size) override { - vector_writer.write(data, size); + seri::SeriResult Write(seri::SizeWriteTag data) override { + auto const u_size = static_cast(data.size); + return Write(seri::DataTag{u_size}); + } + + seri::SeriResult Write(seri::DataWriteTag data) override { + data_buffer.insert( + std::end(data_buffer), reinterpret_cast(data.data), + reinterpret_cast(data.data) + data.size); + return Ok{seri::good}; } DomainQuery query; MapDomainStorage* storage; - ObjectData data; - VectorWriter vector_writer; + ObjectData data_buffer; }; class MapDomainStorageReader final : public IDomainStorageReader { public: explicit MapDomainStorageReader(ObjectData const& d) - : data{&d}, reader{*data} {} + : data_buffer{d.data(), d.size()} {} - void read(void* data, std::size_t size) override { reader.read(data, size); } + seri::SeriResult Read(seri::SizeReadTag data) override { + std::uint32_t u_size{}; + TRY_RESULT(Read(seri::DataTag{u_size})); + data.size = static_cast(u_size); + return Ok{seri::good}; + } - ReadResult result() const override { return ReadResult::kYes; } - void result(ReadResult) override {} + seri::SeriResult Read(seri::DataReadTag data) override { + if (data_buffer.size() < data.size) { + return Error{seri::read_eof}; + } + std::memcpy(data.data, data_buffer.data(), data.size); + data_buffer = data_buffer.subspan(data.size); + return Ok{seri::good}; + } - ObjectData const* data; - VectorReader reader; + std::span data_buffer; }; std::unique_ptr MapDomainStorage::Store( diff --git a/tests/test-object-system/test-obj-create.cpp b/tests/test-object-system/test-obj-create.cpp index 1957af11..9c23277e 100644 --- a/tests/test-object-system/test-obj-create.cpp +++ b/tests/test-object-system/test-obj-create.cpp @@ -19,12 +19,12 @@ #include "aether/obj/domain.h" #include "aether/obj/obj_ptr.h" #include "aether/obj/registry.h" -#include "objects/foo.h" -#include "objects/bob.h" #include "objects/bar.h" -#include "objects/poopa_loopa.h" +#include "objects/bob.h" #include "objects/collector.h" #include "objects/family.h" +#include "objects/foo.h" +#include "objects/poopa_loopa.h" #include "map_domain_storage.h" diff --git a/tests/test-transport/test-data-packet-collector.cpp b/tests/test-transport/test-data-packet-collector.cpp index fe4ecb23..5b6519db 100644 --- a/tests/test-transport/test-data-packet-collector.cpp +++ b/tests/test-transport/test-data-packet-collector.cpp @@ -20,9 +20,10 @@ #include #include -#include "aether/mstream.h" -#include "aether/mstream_buffers.h" +#include "aether-miscpp/serialization/binary_archive.h" + #include "aether/transport/data_packet_collector.h" +#include "aether/vector_buffer.h" namespace ae::test_data_pc { void test_DataPacketCollectorEmpty() { @@ -33,9 +34,8 @@ void test_DataPacketCollectorEmpty() { std::vector MakeStreamPacket(std::vector data) { std::vector packet_size_data; - VectorWriter writer(packet_size_data); - omstream os{writer}; - os << PacketSize{data.size()}; + auto buffer = VectorBuffer{packet_size_data}; + TEST_ASSERT(buffer.Write(seri::SizeWriteTag{data.size()})); data.insert(data.begin(), packet_size_data.begin(), packet_size_data.end()); @@ -44,24 +44,25 @@ std::vector MakeStreamPacket(std::vector data) { std::vector TestPacket() { std::vector packet; - VectorWriter writer(packet); - omstream os{writer}; - - os << std::string{"Hello"}; - os << int{12}; - os << float{12.42}; + auto archive = seri::BinaryArchive{seri::BinaryVectorBuffer<>{packet}}; + TEST_ASSERT(archive.Save(std::string{"Hello"})); + TEST_ASSERT(archive.Save(int{12})); + TEST_ASSERT(archive.Save(float{12.42})); return MakeStreamPacket(std::move(packet)); } inline void AssertPacket(std::vector const& data_packet) { - VectorReader reader(data_packet); - imstream is{reader}; + auto archive = seri::BinaryArchive{ + seri::BinaryVectorBuffer<>{ + const_cast&>(data_packet)}, // NOLINT + }; std::string str; - int i; - float f; - is >> str >> i >> f; - TEST_ASSERT(data_was_read(is)); + int i{}; + float f{}; + TEST_ASSERT(archive.Load(str)); + TEST_ASSERT(archive.Load(i)); + TEST_ASSERT(archive.Load(f)); TEST_ASSERT_EQUAL_STRING("Hello", str.c_str()); TEST_ASSERT_EQUAL(12, i); TEST_ASSERT_EQUAL(12.42, f); @@ -71,7 +72,7 @@ void test_AddOnePacket() { StreamDataPacketCollector collector; auto packet = TestPacket(); - collector.AddData(packet); + collector.AddData(packet.data(), packet.size()); auto data_packet = collector.PopPacket(); TEST_ASSERT(!data_packet.empty()); @@ -85,7 +86,7 @@ void test_AddFewPackets() { StreamDataPacketCollector collector; for (auto i = 0; i < 2; ++i) { auto packet = TestPacket(); - collector.AddData(std::move(packet)); + collector.AddData(packet.data(), packet.size()); } for (auto i = 0; i < 2; ++i) { auto data_packet = collector.PopPacket(); @@ -101,7 +102,7 @@ void test_AddFewPackets() { void test_AddBigPacket() { StreamDataPacketCollector collector; auto garbage = MakeStreamPacket(std::vector(1200)); - collector.AddData(garbage); + collector.AddData(garbage.data(), garbage.size()); auto data_packet = collector.PopPacket(); TEST_ASSERT_EQUAL(1200, data_packet.size()); @@ -117,7 +118,7 @@ void test_AddFewPacketInOne() { std::end(packet)); } - collector.AddData(std::move(cumulative_packet)); + collector.AddData(cumulative_packet.data(), cumulative_packet.size()); for (auto i = 0; i < 2; ++i) { auto data_packet = collector.PopPacket(); @@ -133,18 +134,18 @@ void test_BigPacketPartially() { StreamDataPacketCollector collector; auto garbage = MakeStreamPacket(std::vector(1200)); // add 1 byte - collector.AddData({garbage[0]}); + collector.AddData(garbage.data(), 1); { auto p = collector.PopPacket(); TEST_ASSERT(p.empty()); } - collector.AddData({garbage[1]}); + collector.AddData(garbage.data() + 1, 1); { auto p = collector.PopPacket(); TEST_ASSERT(p.empty()); } // add rest of data - collector.AddData({std::next(std::begin(garbage), 2), std::end(garbage)}); + collector.AddData(garbage.data() + 2, garbage.size() - 2); { // packet complete auto p = collector.PopPacket(); diff --git a/tests/test-types/test-nullable-type.cpp b/tests/test-types/test-nullable-type.cpp index 039fa0c3..3bbc28e7 100644 --- a/tests/test-types/test-nullable-type.cpp +++ b/tests/test-types/test-nullable-type.cpp @@ -18,8 +18,9 @@ #include -#include "aether/mstream.h" -#include "aether/mstream_buffers.h" +#include "aether-miscpp/reflect/reflect.h" +#include "aether-miscpp/serialization/binary_archive.h" + #include "aether/types/nullable_type.h" #include "tests/test-api-protocol/assert_packet.h" @@ -49,6 +50,16 @@ struct BasedOnNoOption : Base, NullableType { bool d; }; +struct Bar { + AE_REFLECT_MEMBERS(value, enabled); + std::optional value; + bool enabled; +}; + +struct Foo : Bar, NullableType { + AE_REFLECT(AE_BASE(Bar)); +}; + void test_SaveLoadNullableType() { std::vector buffer; NoOptionalData data; @@ -57,19 +68,15 @@ void test_SaveLoadNullableType() { // save { - auto buffer_writer = VectorWriter<>{buffer}; - auto stream = omstream{buffer_writer}; - - stream << data; + auto archive = seri::BinaryArchive{VectorBuffer{buffer}}; + TEST_ASSERT(data.Seri(archive)); } AssertPacket(buffer, std::uint8_t{}, int{42}, bool{true}); NoOptionalData load_data{}; { - auto buffer_reader = VectorReader<>{buffer}; - auto stream = imstream{buffer_reader}; - - stream >> load_data; + auto archive = seri::BinaryArchive{VectorBuffer{buffer}}; + TEST_ASSERT(load_data.Deseri(archive)); } TEST_ASSERT(load_data.a == data.a); TEST_ASSERT(load_data.c == data.c); @@ -86,10 +93,9 @@ void test_OptionalData() { // save { - auto buffer_writer = VectorWriter<>{buffer}; - auto stream = omstream{buffer_writer}; - - stream << data_has_value << data_no_has_value; + auto archive = seri::BinaryArchive{VectorBuffer{buffer}}; + TEST_ASSERT(data_has_value.Seri(archive)); + TEST_ASSERT(data_no_has_value.Seri(archive)); } AssertPacket(buffer, std::uint8_t{}, int{42}, bool{true}, std::uint8_t{0x1}, @@ -98,10 +104,9 @@ void test_OptionalData() { WithOptionalData load_data_has_value{}; WithOptionalData load_data_no_has_value{}; { - auto buffer_reader = VectorReader<>{buffer}; - auto stream = imstream{buffer_reader}; - - stream >> load_data_has_value >> load_data_no_has_value; + auto archive = seri::BinaryArchive{VectorBuffer{buffer}}; + TEST_ASSERT(load_data_has_value.Deseri(archive)); + TEST_ASSERT(load_data_no_has_value.Deseri(archive)); } TEST_ASSERT(load_data_has_value.b.has_value()); TEST_ASSERT(load_data_has_value.b == data_has_value.b); @@ -111,6 +116,25 @@ void test_OptionalData() { TEST_ASSERT(load_data_no_has_value.c == data_no_has_value.c); } +void test_OptionalDataAbsentClearsPrepopulatedValue() { + std::vector buffer; + WithOptionalData data{}; + data.c = true; + { + auto archive = seri::BinaryArchive{VectorBuffer{buffer}}; + TEST_ASSERT(data.Seri(archive)); + } + + WithOptionalData load_data{}; + load_data.b = 42; + { + auto archive = seri::BinaryArchive{VectorBuffer{buffer}}; + TEST_ASSERT(load_data.Deseri(archive)); + } + TEST_ASSERT(!load_data.b.has_value()); + TEST_ASSERT(load_data.c == data.c); +} + void test_WithDerived() { std::vector buffer; BasedOnNoOption data{}; @@ -126,10 +150,9 @@ void test_WithDerived() { // save { - auto buffer_writer = VectorWriter<>{buffer}; - auto stream = omstream{buffer_writer}; - - stream << data << data_opt_value; + auto archive = seri::BinaryArchive{VectorBuffer{buffer}}; + TEST_ASSERT(data.Seri(archive)); + TEST_ASSERT(data_opt_value.Seri(archive)); } AssertPacket(buffer, std::uint8_t{0x4}, int{42}, bool{true}, bool{true}, @@ -137,10 +160,9 @@ void test_WithDerived() { BasedOnNoOption load_data{}; BasedOnNoOption load_data_opt_value{}; { - auto buffer_reader = VectorReader<>{buffer}; - auto stream = imstream{buffer_reader}; - - stream >> load_data >> load_data_opt_value; + auto archive = seri::BinaryArchive{VectorBuffer{buffer}}; + TEST_ASSERT(load_data.Deseri(archive)); + TEST_ASSERT(load_data_opt_value.Deseri(archive)); } TEST_ASSERT(load_data.a == data.a); TEST_ASSERT(!load_data.b.has_value()); @@ -153,12 +175,47 @@ void test_WithDerived() { TEST_ASSERT(load_data_opt_value.d == data_opt_value.d); } +void test_InheritedOptionalWithBaseReflection() { + std::vector buffer; + Foo without_value{}; + without_value.enabled = true; + Foo with_value{}; + with_value.value = 42; + with_value.enabled = true; + + { + auto archive = seri::BinaryArchive{VectorBuffer{buffer}}; + TEST_ASSERT(without_value.Seri(archive)); + TEST_ASSERT(with_value.Seri(archive)); + } + + AssertPacket(buffer, std::uint8_t{0x1}, bool{true}, std::uint8_t{}, int{42}, + bool{true}); + + Foo loaded_without_value{}; + loaded_without_value.value = 7; + Foo loaded_with_value{}; + { + auto archive = seri::BinaryArchive{VectorBuffer{buffer}}; + TEST_ASSERT(loaded_without_value.Deseri(archive)); + TEST_ASSERT(loaded_with_value.Deseri(archive)); + } + + TEST_ASSERT(!loaded_without_value.value.has_value()); + TEST_ASSERT(loaded_without_value.enabled == without_value.enabled); + TEST_ASSERT(loaded_with_value.value == with_value.value); + TEST_ASSERT(loaded_with_value.enabled == with_value.enabled); +} + } // namespace ae::test_nullable_type int test_nullable_type() { UNITY_BEGIN(); RUN_TEST(ae::test_nullable_type::test_SaveLoadNullableType); RUN_TEST(ae::test_nullable_type::test_OptionalData); + RUN_TEST( + ae::test_nullable_type::test_OptionalDataAbsentClearsPrepopulatedValue); RUN_TEST(ae::test_nullable_type::test_WithDerived); + RUN_TEST(ae::test_nullable_type::test_InheritedOptionalWithBaseReflection); return UNITY_END(); } diff --git a/tests/test-types/test-variant-type.cpp b/tests/test-types/test-variant-type.cpp index de4d51a7..30a043ef 100644 --- a/tests/test-types/test-variant-type.cpp +++ b/tests/test-types/test-variant-type.cpp @@ -17,132 +17,63 @@ #include #include -#include -#include #include -#include "aether/mstream.h" -#include "aether/mstream_buffers.h" +#include "aether-miscpp/serialization/binary_archive.h" +#include "aether/types/variant_type.h" namespace ae::test_variant_type { -static_assert(std::numeric_limits::max() == 255); -static_assert(std::numeric_limits::max() - 1 == 254); - -// Do not instantiate a 255-alternative std::variant here: on Windows x86 MSVC -// it can explode object sections in this test TU. mstream.h already enforces -// the 255-alternative limit with a static_assert. - -void test_VariantRoundTripExactType() { - std::vector buffer; - std::variant value{std::uint32_t{42}}; - - { - auto buffer_writer = VectorWriter<>{buffer}; - auto stream = omstream{buffer_writer}; - - stream << value; - } - - TEST_ASSERT_EQUAL_UINT8(1, buffer[0]); - - std::variant loaded{}; - { - auto buffer_reader = VectorReader<>{buffer}; - auto stream = imstream{buffer_reader}; - - stream >> loaded; - TEST_ASSERT(data_was_read(stream)); - } - - TEST_ASSERT_EQUAL(1, loaded.index()); - TEST_ASSERT(std::holds_alternative(loaded)); - TEST_ASSERT_EQUAL_UINT32(42, std::get(loaded)); -} - -void test_VariantValuelessSentinel() { - std::vector buffer{std::numeric_limits::max()}; - std::variant value{std::uint32_t{7}}; - - { - auto buffer_reader = VectorReader<>{buffer}; - auto stream = imstream{buffer_reader}; - - stream >> value; - TEST_ASSERT(data_was_read(stream)); - } - - TEST_ASSERT_EQUAL(1, value.index()); - TEST_ASSERT(std::holds_alternative(value)); - TEST_ASSERT_EQUAL_UINT32(7, std::get(value)); +using TestVariant = + VariantType, VPair<254, std::uint32_t>, + VPair<7, std::uint16_t>>; + +void test_VariantTypeLoadsLegacyPackets() { + auto small_packet = std::vector{3, 42, 0, 0, 0}; + auto small_value = TestVariant{}; + auto small_archive = + seri::BinaryArchive{seri::BinaryVectorBuffer<>{small_packet}}; + TEST_ASSERT(small_archive.Load(small_value)); + TEST_ASSERT_EQUAL_UINT8(3, small_value.Index()); + TEST_ASSERT_EQUAL_INT(42, small_value.Get()); + + // Legacy packets use explicit tags, including the high uint8_t boundary. + auto large_packet = std::vector{254, 0x78, 0x56, 0x34, 0x12}; + auto large_value = TestVariant{}; + auto large_archive = + seri::BinaryArchive{seri::BinaryVectorBuffer<>{large_packet}}; + TEST_ASSERT(large_archive.Load(large_value)); + TEST_ASSERT_EQUAL_UINT8(254, large_value.Index()); + TEST_ASSERT_EQUAL_UINT32(0x12345678, large_value.Get()); } -void test_VariantUnsupportedBoundaryTag254Fails() { - std::vector buffer{ - static_cast(std::numeric_limits::max() - 1)}; - std::variant value{std::uint32_t{7}}; - - { - auto buffer_reader = VectorReader<>{buffer}; - auto stream = imstream{buffer_reader}; +void test_VariantTypeSavesLegacyPackets() { + auto buffer = std::vector{}; + auto value = TestVariant{std::uint16_t{0x1234}}; + auto archive = seri::BinaryArchive{seri::BinaryVectorBuffer<>{buffer}}; - stream >> value; - TEST_ASSERT(!data_was_read(stream)); - } + TEST_ASSERT(archive.Save(value)); - TEST_ASSERT_EQUAL(1, value.index()); - TEST_ASSERT(std::holds_alternative(value)); - TEST_ASSERT_EQUAL_UINT32(7, std::get(value)); + auto const expected = std::vector{7, 0x34, 0x12}; + TEST_ASSERT_EQUAL_UINT8_ARRAY(expected.data(), buffer.data(), + expected.size()); } -void test_VariantBoundaryTag254AndSentinel255() { - std::variant value{ - std::in_place_index<2>, std::uint16_t{9}}; - - std::vector buffer; - { - auto buffer_writer = VectorWriter<>{buffer}; - auto stream = omstream{buffer_writer}; - - stream << value; - } - - TEST_ASSERT_EQUAL_UINT8(2, buffer[0]); - - std::variant loaded{}; - { - auto buffer_reader = VectorReader<>{buffer}; - auto stream = imstream{buffer_reader}; - - stream >> loaded; - TEST_ASSERT(data_was_read(stream)); - } - - TEST_ASSERT_EQUAL(2, loaded.index()); - TEST_ASSERT(std::holds_alternative(loaded)); - TEST_ASSERT_EQUAL_UINT16(9, std::get(loaded)); - - std::vector sentinel_buffer{std::numeric_limits::max()}; - auto sentinel = std::variant{std::in_place_index<0>, 3}; - { - auto buffer_reader = VectorReader<>{sentinel_buffer}; - auto stream = imstream{buffer_reader}; - - stream >> sentinel; - TEST_ASSERT(data_was_read(stream)); - } +void test_VariantTypeUnknownTagFails() { + std::vector buffer{255}; + auto value = TestVariant{std::uint32_t{7}}; + auto archive = seri::BinaryArchive{seri::BinaryVectorBuffer<>{buffer}}; - TEST_ASSERT_EQUAL(0, sentinel.index()); - TEST_ASSERT(std::holds_alternative(sentinel)); - TEST_ASSERT_EQUAL_INT(3, std::get(sentinel)); + TEST_ASSERT(!archive.Load(value)); + TEST_ASSERT_EQUAL_UINT8(254, value.Index()); + TEST_ASSERT_EQUAL_UINT32(7, value.Get()); } } // namespace ae::test_variant_type int test_variant_type() { UNITY_BEGIN(); - RUN_TEST(ae::test_variant_type::test_VariantRoundTripExactType); - RUN_TEST(ae::test_variant_type::test_VariantValuelessSentinel); - RUN_TEST(ae::test_variant_type::test_VariantUnsupportedBoundaryTag254Fails); - RUN_TEST(ae::test_variant_type::test_VariantBoundaryTag254AndSentinel255); + RUN_TEST(ae::test_variant_type::test_VariantTypeLoadsLegacyPackets); + RUN_TEST(ae::test_variant_type::test_VariantTypeSavesLegacyPackets); + RUN_TEST(ae::test_variant_type::test_VariantTypeUnknownTagFails); return UNITY_END(); }