diff --git a/components/core/CMakeLists.txt b/components/core/CMakeLists.txt index 66901ae6c..67eb80040 100644 --- a/components/core/CMakeLists.txt +++ b/components/core/CMakeLists.txt @@ -334,6 +334,8 @@ set(SOURCE_FILES_unitTest src/clp/ffi/ir_stream/encoding_methods.cpp src/clp/ffi/ir_stream/encoding_methods.hpp src/clp/ffi/ir_stream/IrUnitHandlerInterface.hpp + src/clp/ffi/ir_stream/ir_unit_deserialization_methods.cpp + src/clp/ffi/ir_stream/ir_unit_deserialization_methods.hpp src/clp/ffi/ir_stream/protocol_constants.hpp src/clp/ffi/ir_stream/Serializer.cpp src/clp/ffi/ir_stream/Serializer.hpp diff --git a/components/core/src/clp/ffi/ir_stream/Deserializer.cpp b/components/core/src/clp/ffi/ir_stream/Deserializer.cpp index 453d58bcf..11034c4a6 100644 --- a/components/core/src/clp/ffi/ir_stream/Deserializer.cpp +++ b/components/core/src/clp/ffi/ir_stream/Deserializer.cpp @@ -1,8 +1,6 @@ #include "Deserializer.hpp" -#include <cstddef> #include <cstdint> -#include <optional> #include <string> #include <string_view> #include <system_error> @@ -14,27 +12,16 @@ #include <json/single_include/nlohmann/json.hpp> #include <outcome/single-header/outcome.hpp> -#include "../../ErrorCode.hpp" -#include "../../ir/EncodedTextAst.hpp" -#include "../../ir/types.hpp" #include "../../ReaderInterface.hpp" #include "../../time_types.hpp" -#include "../../type_utils.hpp" #include "../KeyValuePairLogEvent.hpp" -#include "../SchemaTree.hpp" -#include "../SchemaTreeNode.hpp" -#include "../Value.hpp" #include "decoding_methods.hpp" +#include "ir_unit_deserialization_methods.hpp" #include "protocol_constants.hpp" #include "utils.hpp" namespace clp::ffi::ir_stream { namespace { -/** - * A collection of schema tree leaf node IDs. It represents the schema of a `KeyValuePairLogEvent`. - */ -using Schema = std::vector<SchemaTreeNode::id_t>; - /** * Class to perform different actions depending on whether a transaction succeeds or fails. The * default state assumes the transaction fails. @@ -78,551 +65,15 @@ class TransactionManager { bool m_success{false}; }; -/** - * @param ir_error_code - * @return Equivalent `std::errc` code indicating the same error type. - */ -[[nodiscard]] auto ir_error_code_to_errc(IRErrorCode ir_error_code) -> std::errc; - /** * @param tag * @return Whether the tag represents a schema tree node. */ [[nodiscard]] auto is_schema_tree_node_tag(encoded_tag_t tag) -> bool; -/** - * @param tag - * @return The corresponding schema tree node type on success. - * @return std::nullopt if the tag doesn't match to any defined schema tree node type. - */ -[[nodiscard]] auto schema_tree_node_tag_to_type(encoded_tag_t tag -) -> std::optional<SchemaTreeNode::Type>; - -/** - * Deserializes the parent ID of a schema tree node. - * @param reader - * @param parent_id Returns the deserialized result. - * @return IRErrorCode::IRErrorCode_Success on success. - * @return IRErrorCode::IRErrorCode_Incomplete_IR if the stream is truncated. - * @return IRErrorCode::IRErrorCode_Corrupted_IR if the next packet in the stream isn't a parent ID. - * @return Same as `deserialize_tag` on any other failure. - */ -[[nodiscard]] auto deserialize_schema_tree_node_parent_id( - ReaderInterface& reader, - SchemaTreeNode::id_t& parent_id -) -> IRErrorCode; - -/** - * Deserializes the key name of a schema tree node. - * @param reader - * @param key_name Returns the deserialized key name. - * @return IRErrorCode::IRErrorCode_Success on success. - * @return Same as `deserialize_tag` or `deserialize_string` on failure. - */ -[[nodiscard]] auto deserialize_schema_tree_node_key_name( - ReaderInterface& reader, - std::string& key_name -) -> IRErrorCode; - -/** - * Deserializes an integer value packet. - * @param reader - * @param tag - * @param val Returns the deserialized value. - * @return IRErrorCode::IRErrorCode_Success on success. - * @return IRErrorCode::IRErrorCode_Incomplete_IR if the stream is truncated. - * @return IRErrorCode::IRErrorCode_Corrupted_IR if the given tag doesn't correspond to an integer - * packet. - */ -[[nodiscard]] auto -deserialize_int_val(ReaderInterface& reader, encoded_tag_t tag, value_int_t& val) -> IRErrorCode; - -/** - * Deserializes a string packet. - * @param reader - * @param tag - * @param deserialized_str Returns the deserialized string. - * @return IRErrorCode::IRErrorCode_Success on success. - * @return IRErrorCode::IRErrorCode_Incomplete_IR if the stream is truncated. - * @return IRErrorCode::IRErrorCode_Corrupted_IR if the given tag doesn't correspond to a string - * packet. - */ -[[nodiscard]] auto deserialize_string( - ReaderInterface& reader, - encoded_tag_t tag, - std::string& deserialized_str -) -> IRErrorCode; - -/** - * Deserializes all UTC offset packets until a non-UTC offset packet tag is read. - * @param reader - * @param tag Takes the current tag as input and returns the last tag read. - * @param utc_offset Returns the deserialized UTC offset. - * @return IRErrorCode::IRErrorCode_Success on success. - * @return Same as `deserialize_utc_offset_change` or `deserialize_tag` on failure. - */ -[[nodiscard]] auto deserialize_utc_offset_changes( - ReaderInterface& reader, - encoded_tag_t& tag, - UtcOffset& utc_offset -) -> IRErrorCode; - -/** - * Deserializes all schema tree node packets and inserts them into the schema tree until a non- - * schema tree node tag is read. - * @param reader - * @param tag Takes the current tag as input and returns the last tag read. - * @param schema_tree Returns the schema tree with all new nodes inserted. - * @return IRErrorCode::IRErrorCode_Success on success. - * @return IRErrorCode::IRErrorCode_Corrupted_IR if the packet tag doesn't correspond to any known - * schema node type or the node being deserialized already exists in the current in-memory schema - * tree. - * @return Same as `deserialize_schema_tree_node_parent_id`, `deserialize_string`, or - * `deserialize_tag` on any other failure. - */ -[[nodiscard]] auto deserialize_schema_tree_nodes( - ReaderInterface& reader, - encoded_tag_t& tag, - SchemaTree& schema_tree -) -> IRErrorCode; - -/** - * Deserializes the IDs of all keys in a log event. - * @param reader - * @param tag Takes the current tag as input and returns the last tag read. - * @param schema Returns the deserialized schema. - * @return IRErrorCode::IRErrorCode_Success on success. - * @return IRErrorCode::IRErrorCode_Incomplete_IR if the stream is truncated. - * @return Same as `deserialize_tag` on any other failure. - */ -[[nodiscard]] auto -deserialize_schema(ReaderInterface& reader, encoded_tag_t& tag, Schema& schema) -> IRErrorCode; - -/** - * Deserializes the next value and pushes the result into `node_id_value_pairs`. - * @param reader - * @param tag - * @param node_id The node ID that corresponds to the value. - * @param node_id_value_pairs Returns the ID-value pair constructed from the deserialized value. - * @return IRErrorCode::IRErrorCode_Success on success. - * @return IRErrorCode::IRErrorCode_Incomplete_IR if the stream is truncated. - * @return IRErrorCode::IRErrorCode_Corrupted_IR if the tag doesn't correspond to any known value - * type. - * @return Same as `deserialize_encoded_text_ast_and_insert_to_node_id_value_pairs` on any other - * failure. - */ -[[nodiscard]] auto deserialize_value_and_insert_to_node_id_value_pairs( - ReaderInterface& reader, - encoded_tag_t tag, - SchemaTreeNode::id_t node_id, - KeyValuePairLogEvent::NodeIdValuePairs& node_id_value_pairs -) -> IRErrorCode; - -/** - * Deserializes an encoded text AST and pushes the result into node_id_value_pairs. - * @tparam encoded_variable_t - * @param reader - * @param node_id The node ID that corresponds to the value. - * @param node_id_value_pairs Returns the ID-value pair constructed by the deserialized encoded text - * AST. - * @return IRErrorCode::IRErrorCode_Success on success. - * @return Same as `deserialize_tag` or `deserialize_encoded_text_ast` on failure. - */ -template <typename encoded_variable_t> -requires(std::is_same_v<ir::four_byte_encoded_variable_t, encoded_variable_t> - || std::is_same_v<ir::eight_byte_encoded_variable_t, encoded_variable_t>) -[[nodiscard]] auto deserialize_encoded_text_ast_and_insert_to_node_id_value_pairs( - ReaderInterface& reader, - SchemaTreeNode::id_t node_id, - KeyValuePairLogEvent::NodeIdValuePairs& node_id_value_pairs -) -> IRErrorCode; - -/** - * Deserializes values and constructs ID-value pairs according to the given schema. The number of - * values to deserialize is indicated by the size of the given schema. - * @param reader - * @param tag - * @param schema The log event's schema. - * @param node_id_value_pairs Returns the constructed ID-value pairs. - * @return IRErrorCode::IRErrorCode_Success on success. - * @return IRErrorCode::IRErrorCode_Corrupted_IR if a key is duplicated in the deserialized log - * event. - * @return Same as `deserialize_tag` or `deserialize_value_and_insert_to_node_id_value_pairs` on any - * other failure. - */ -[[nodiscard]] auto deserialize_value_and_construct_node_id_value_pairs( - ReaderInterface& reader, - encoded_tag_t tag, - Schema const& schema, - KeyValuePairLogEvent::NodeIdValuePairs& node_id_value_pairs -) -> IRErrorCode; - -auto ir_error_code_to_errc(IRErrorCode ir_error_code) -> std::errc { - switch (ir_error_code) { - case IRErrorCode_Incomplete_IR: - return std::errc::result_out_of_range; - case IRErrorCode_Corrupted_IR: - case IRErrorCode_Decode_Error: - return std::errc::protocol_error; - case IRErrorCode_Eof: - return std::errc::no_message_available; - default: - return std::errc::not_supported; - } -} - auto is_schema_tree_node_tag(encoded_tag_t tag) -> bool { return (tag & cProtocol::Payload::SchemaTreeNodeMask) == cProtocol::Payload::SchemaTreeNodeMask; } - -auto schema_tree_node_tag_to_type(encoded_tag_t tag) -> std::optional<SchemaTreeNode::Type> { - switch (tag) { - case cProtocol::Payload::SchemaTreeNodeInt: - return SchemaTreeNode::Type::Int; - case cProtocol::Payload::SchemaTreeNodeFloat: - return SchemaTreeNode::Type::Float; - case cProtocol::Payload::SchemaTreeNodeBool: - return SchemaTreeNode::Type::Bool; - case cProtocol::Payload::SchemaTreeNodeStr: - return SchemaTreeNode::Type::Str; - case cProtocol::Payload::SchemaTreeNodeUnstructuredArray: - return SchemaTreeNode::Type::UnstructuredArray; - case cProtocol::Payload::SchemaTreeNodeObj: - return SchemaTreeNode::Type::Obj; - default: - return std::nullopt; - } -} - -auto deserialize_schema_tree_node_parent_id( - ReaderInterface& reader, - SchemaTreeNode::id_t& parent_id -) -> IRErrorCode { - encoded_tag_t tag{}; - if (auto const err{deserialize_tag(reader, tag)}; IRErrorCode::IRErrorCode_Success != err) { - return err; - } - if (cProtocol::Payload::SchemaTreeNodeParentIdUByte == tag) { - uint8_t deserialized_id{}; - if (false == deserialize_int(reader, deserialized_id)) { - return IRErrorCode::IRErrorCode_Incomplete_IR; - } - parent_id = static_cast<SchemaTreeNode::id_t>(deserialized_id); - } else if (cProtocol::Payload::SchemaTreeNodeParentIdUShort == tag) { - uint16_t deserialized_id{}; - if (false == deserialize_int(reader, deserialized_id)) { - return IRErrorCode::IRErrorCode_Incomplete_IR; - } - parent_id = static_cast<SchemaTreeNode::id_t>(deserialized_id); - } else { - return IRErrorCode::IRErrorCode_Corrupted_IR; - } - return IRErrorCode_Success; -} - -auto deserialize_schema_tree_node_key_name(ReaderInterface& reader, std::string& key_name) - -> IRErrorCode { - encoded_tag_t str_packet_tag{}; - if (auto const err{deserialize_tag(reader, str_packet_tag)}; - IRErrorCode::IRErrorCode_Success != err) - { - return err; - } - if (auto const err{deserialize_string(reader, str_packet_tag, key_name)}; - IRErrorCode::IRErrorCode_Success != err) - { - return err; - } - return IRErrorCode::IRErrorCode_Success; -} - -auto deserialize_int_val(ReaderInterface& reader, encoded_tag_t tag, value_int_t& val) - -> IRErrorCode { - if (cProtocol::Payload::ValueInt8 == tag) { - int8_t deserialized_val{}; - if (false == deserialize_int(reader, deserialized_val)) { - return IRErrorCode::IRErrorCode_Incomplete_IR; - } - // NOLINTNEXTLINE(bugprone-signed-char-misuse,cert-str34-c) - val = deserialized_val; - } else if (cProtocol::Payload::ValueInt16 == tag) { - int16_t deserialized_val{}; - if (false == deserialize_int(reader, deserialized_val)) { - return IRErrorCode::IRErrorCode_Incomplete_IR; - } - val = deserialized_val; - } else if (cProtocol::Payload::ValueInt32 == tag) { - int32_t deserialized_val{}; - if (false == deserialize_int(reader, deserialized_val)) { - return IRErrorCode::IRErrorCode_Incomplete_IR; - } - val = deserialized_val; - } else if (cProtocol::Payload::ValueInt64 == tag) { - int64_t deserialized_val{}; - if (false == deserialize_int(reader, deserialized_val)) { - return IRErrorCode::IRErrorCode_Incomplete_IR; - } - val = deserialized_val; - } else { - return IRErrorCode::IRErrorCode_Corrupted_IR; - } - return IRErrorCode::IRErrorCode_Success; -} - -auto deserialize_string(ReaderInterface& reader, encoded_tag_t tag, std::string& deserialized_str) - -> IRErrorCode { - size_t str_length{}; - if (cProtocol::Payload::StrLenUByte == tag) { - uint8_t length{}; - if (false == deserialize_int(reader, length)) { - return IRErrorCode::IRErrorCode_Incomplete_IR; - } - str_length = static_cast<size_t>(length); - } else if (cProtocol::Payload::StrLenUShort == tag) { - uint16_t length{}; - if (false == deserialize_int(reader, length)) { - return IRErrorCode::IRErrorCode_Incomplete_IR; - } - str_length = static_cast<size_t>(length); - } else if (cProtocol::Payload::StrLenUInt == tag) { - uint32_t length{}; - if (false == deserialize_int(reader, length)) { - return IRErrorCode::IRErrorCode_Incomplete_IR; - } - str_length = static_cast<size_t>(length); - } else { - return IRErrorCode::IRErrorCode_Corrupted_IR; - } - if (clp::ErrorCode_Success != reader.try_read_string(str_length, deserialized_str)) { - return IRErrorCode::IRErrorCode_Incomplete_IR; - } - return IRErrorCode::IRErrorCode_Success; -} - -auto deserialize_utc_offset_changes( - ReaderInterface& reader, - encoded_tag_t& tag, - UtcOffset& utc_offset -) -> IRErrorCode { - while (cProtocol::Payload::UtcOffsetChange == tag) { - if (auto const err{deserialize_utc_offset_change(reader, utc_offset)}; - IRErrorCode::IRErrorCode_Success != err) - { - return err; - } - if (auto const err{deserialize_tag(reader, tag)}; IRErrorCode::IRErrorCode_Success != err) { - return err; - } - } - return IRErrorCode::IRErrorCode_Success; -} - -auto deserialize_schema_tree_nodes( - ReaderInterface& reader, - encoded_tag_t& tag, - SchemaTree& schema_tree -) -> IRErrorCode { - while (is_schema_tree_node_tag(tag)) { - auto const type{schema_tree_node_tag_to_type(tag)}; - if (false == type.has_value()) { - return IRErrorCode::IRErrorCode_Corrupted_IR; - } - - SchemaTreeNode::id_t parent_id{}; - if (auto const err{deserialize_schema_tree_node_parent_id(reader, parent_id)}; - IRErrorCode_Success != err) - { - return err; - } - - std::string key_name; - if (auto const err{deserialize_schema_tree_node_key_name(reader, key_name)}; - IRErrorCode::IRErrorCode_Success != err) - { - return err; - } - - // Insert the node to the schema tree - SchemaTree::NodeLocator const locator{parent_id, key_name, type.value()}; - if (schema_tree.has_node(locator)) { - return IRErrorCode::IRErrorCode_Corrupted_IR; - } - std::ignore = schema_tree.insert_node(locator); - - // Read the next tag - if (auto const err{deserialize_tag(reader, tag)}; IRErrorCode::IRErrorCode_Success != err) { - return err; - } - } - return IRErrorCode::IRErrorCode_Success; -} - -auto deserialize_schema(ReaderInterface& reader, encoded_tag_t& tag, Schema& schema) - -> IRErrorCode { - schema.clear(); - while (true) { - if (cProtocol::Payload::KeyIdUByte == tag) { - uint8_t id{}; - if (false == deserialize_int(reader, id)) { - return IRErrorCode::IRErrorCode_Incomplete_IR; - } - schema.push_back(static_cast<SchemaTreeNode::id_t>(id)); - } else if (cProtocol::Payload::KeyIdUShort == tag) { - uint16_t id{}; - if (false == deserialize_int(reader, id)) { - return IRErrorCode::IRErrorCode_Incomplete_IR; - } - schema.push_back(static_cast<SchemaTreeNode::id_t>(id)); - } else { - break; - } - - if (auto const err{deserialize_tag(reader, tag)}; IRErrorCode::IRErrorCode_Success != err) { - return err; - } - } - - return IRErrorCode::IRErrorCode_Success; -} - -auto deserialize_value_and_insert_to_node_id_value_pairs( - ReaderInterface& reader, - encoded_tag_t tag, - SchemaTreeNode::id_t node_id, - KeyValuePairLogEvent::NodeIdValuePairs& node_id_value_pairs -) -> IRErrorCode { - switch (tag) { - case cProtocol::Payload::ValueInt8: - case cProtocol::Payload::ValueInt16: - case cProtocol::Payload::ValueInt32: - case cProtocol::Payload::ValueInt64: { - value_int_t value_int{}; - if (auto const err{deserialize_int_val(reader, tag, value_int)}; - IRErrorCode::IRErrorCode_Success != err) - { - return err; - } - node_id_value_pairs.emplace(node_id, Value{value_int}); - break; - } - case cProtocol::Payload::ValueFloat: { - uint64_t val{}; - if (false == deserialize_int(reader, val)) { - return IRErrorCode::IRErrorCode_Incomplete_IR; - } - node_id_value_pairs.emplace(node_id, Value{bit_cast<value_float_t>(val)}); - break; - } - case cProtocol::Payload::ValueTrue: - node_id_value_pairs.emplace(node_id, Value{true}); - break; - case cProtocol::Payload::ValueFalse: - node_id_value_pairs.emplace(node_id, Value{false}); - break; - case cProtocol::Payload::StrLenUByte: - case cProtocol::Payload::StrLenUShort: - case cProtocol::Payload::StrLenUInt: { - std::string value_str; - if (auto const err{deserialize_string(reader, tag, value_str)}; - IRErrorCode::IRErrorCode_Success != err) - { - return err; - } - node_id_value_pairs.emplace(node_id, Value{std::move(value_str)}); - break; - } - case cProtocol::Payload::ValueEightByteEncodingClpStr: - if (auto const err{deserialize_encoded_text_ast_and_insert_to_node_id_value_pairs< - ir::eight_byte_encoded_variable_t>(reader, node_id, node_id_value_pairs)}; - IRErrorCode::IRErrorCode_Success != err) - { - return err; - } - break; - case cProtocol::Payload::ValueFourByteEncodingClpStr: - if (auto const err{deserialize_encoded_text_ast_and_insert_to_node_id_value_pairs< - ir::four_byte_encoded_variable_t>(reader, node_id, node_id_value_pairs)}; - IRErrorCode::IRErrorCode_Success != err) - { - return err; - } - break; - case cProtocol::Payload::ValueNull: - node_id_value_pairs.emplace(node_id, Value{}); - break; - case cProtocol::Payload::ValueEmpty: - node_id_value_pairs.emplace(node_id, std::nullopt); - break; - default: - return IRErrorCode::IRErrorCode_Corrupted_IR; - } - return IRErrorCode::IRErrorCode_Success; -} - -template <typename encoded_variable_t> -requires(std::is_same_v<ir::four_byte_encoded_variable_t, encoded_variable_t> - || std::is_same_v<ir::eight_byte_encoded_variable_t, encoded_variable_t>) -[[nodiscard]] auto deserialize_encoded_text_ast_and_insert_to_node_id_value_pairs( - ReaderInterface& reader, - SchemaTreeNode::id_t node_id, - KeyValuePairLogEvent::NodeIdValuePairs& node_id_value_pairs -) -> IRErrorCode { - encoded_tag_t tag{}; - if (auto const err{deserialize_tag(reader, tag)}; IRErrorCode::IRErrorCode_Success != err) { - return err; - } - - std::string logtype; - std::vector<encoded_variable_t> encoded_vars; - std::vector<std::string> dict_vars; - if (auto const err{deserialize_encoded_text_ast(reader, tag, logtype, encoded_vars, dict_vars)}; - IRErrorCode::IRErrorCode_Success != err) - { - return err; - } - - node_id_value_pairs.emplace( - node_id, - Value{ir::EncodedTextAst<encoded_variable_t>{logtype, dict_vars, encoded_vars}} - ); - return IRErrorCode::IRErrorCode_Success; -} - -auto deserialize_value_and_construct_node_id_value_pairs( - ReaderInterface& reader, - encoded_tag_t tag, - Schema const& schema, - KeyValuePairLogEvent::NodeIdValuePairs& node_id_value_pairs -) -> IRErrorCode { - node_id_value_pairs.clear(); - node_id_value_pairs.reserve(schema.size()); - for (auto const node_id : schema) { - if (node_id_value_pairs.contains(node_id)) { - // The key should be unique in a schema - return IRErrorCode_Corrupted_IR; - } - - if (auto const err{deserialize_value_and_insert_to_node_id_value_pairs( - reader, - tag, - node_id, - node_id_value_pairs - )}; - IRErrorCode::IRErrorCode_Success != err) - { - return err; - } - - if (schema.size() != node_id_value_pairs.size()) { - if (auto const err{deserialize_tag(reader, tag)}; - IRErrorCode::IRErrorCode_Success != err) - { - return err; - } - } - } - return IRErrorCode::IRErrorCode_Success; -} } // namespace auto Deserializer::create(ReaderInterface& reader @@ -679,56 +130,49 @@ auto Deserializer::deserialize_to_next_log_event(clp::ReaderInterface& reader }; encoded_tag_t tag{}; - if (auto const err{deserialize_tag(reader, tag)}; IRErrorCode::IRErrorCode_Success != err) { - return ir_error_code_to_errc(err); - } - - if (auto const err{deserialize_utc_offset_changes(reader, tag, m_utc_offset)}; - IRErrorCode::IRErrorCode_Success != err) - { - return ir_error_code_to_errc(err); - } - - if (auto const err{deserialize_schema_tree_nodes(reader, tag, *m_schema_tree)}; - IRErrorCode::IRErrorCode_Success != err) - { - return ir_error_code_to_errc(err); - } + std::string schema_tree_node_key_name; + while (true) { + if (auto const err{deserialize_tag(reader, tag)}; IRErrorCode::IRErrorCode_Success != err) { + return ir_error_code_to_errc(err); + } - Schema schema; - if (auto const err{deserialize_schema(reader, tag, schema)}; - IRErrorCode::IRErrorCode_Success != err) - { - return ir_error_code_to_errc(err); - } + if (cProtocol::Eof == tag) { + return std::errc::no_message_available; + } - KeyValuePairLogEvent::NodeIdValuePairs node_id_value_pairs; - if (false == schema.empty()) { - if (auto const err{deserialize_value_and_construct_node_id_value_pairs( + if (is_schema_tree_node_tag(tag)) { + auto const result{deserialize_ir_unit_schema_tree_node_insertion( reader, tag, - schema, - node_id_value_pairs + schema_tree_node_key_name )}; - IRErrorCode::IRErrorCode_Success != err) - { - return ir_error_code_to_errc(err); + if (result.has_error()) { + return result.error(); + } + auto const& locator{result.value()}; + if (m_schema_tree->has_node(locator)) { + return std::errc::protocol_error; + } + std::ignore = m_schema_tree->insert_node(locator); + continue; } - } else { - if (cProtocol::Payload::ValueEmpty != tag) { - return ir_error_code_to_errc(IRErrorCode::IRErrorCode_Corrupted_IR); + + if (cProtocol::Payload::UtcOffsetChange == tag) { + auto const result{deserialize_ir_unit_utc_offset_change(reader)}; + if (result.has_error()) { + return result.error(); + } + m_utc_offset = result.value(); + continue; } + + break; } - auto result{KeyValuePairLogEvent::create( - m_schema_tree, - std::move(node_id_value_pairs), - m_utc_offset - )}; + auto result{deserialize_ir_unit_kv_pair_log_event(reader, tag, m_schema_tree, m_utc_offset)}; if (false == result.has_error()) { revert_manager.mark_success(); } - return std::move(result); } } // namespace clp::ffi::ir_stream diff --git a/components/core/src/clp/ffi/ir_stream/Deserializer.hpp b/components/core/src/clp/ffi/ir_stream/Deserializer.hpp index 7ba7822da..0fb7e8c83 100644 --- a/components/core/src/clp/ffi/ir_stream/Deserializer.hpp +++ b/components/core/src/clp/ffi/ir_stream/Deserializer.hpp @@ -46,7 +46,7 @@ class Deserializer { // Methods /** - * Deserializes the stream from the given reader up to and including the next log event. + * Deserializes the stream from the given reader up to and including the next log event IR unit. * @param reader * @return A result containing the deserialized log event or an error code indicating the * failure: @@ -55,8 +55,8 @@ class Deserializer { * - std::errc::protocol_error if the IR stream is corrupted. * - std::errc::protocol_not_supported if the IR stream contains an unsupported metadata format * or uses an unsupported version. - * - Same as `KeyValuePairLogEvent::create` if the intermediate deserialized result cannot - * construct a valid key-value pair log event. + * - Forwards `KeyValuePairLogEvent::create`'s return values if the intermediate deserialized + * result cannot construct a valid key-value pair log event. */ [[nodiscard]] auto deserialize_to_next_log_event(ReaderInterface& reader ) -> OUTCOME_V2_NAMESPACE::std_result<KeyValuePairLogEvent>; diff --git a/components/core/src/clp/ffi/ir_stream/ir_unit_deserialization_methods.cpp b/components/core/src/clp/ffi/ir_stream/ir_unit_deserialization_methods.cpp new file mode 100644 index 000000000..cfbe235d5 --- /dev/null +++ b/components/core/src/clp/ffi/ir_stream/ir_unit_deserialization_methods.cpp @@ -0,0 +1,537 @@ +#include "ir_unit_deserialization_methods.hpp" + +#include <cstddef> +#include <cstdint> +#include <memory> +#include <optional> +#include <string> +#include <type_traits> +#include <utility> +#include <vector> + +#include <outcome/single-header/outcome.hpp> + +#include "../../ErrorCode.hpp" +#include "../../ir/EncodedTextAst.hpp" +#include "../../ir/types.hpp" +#include "../../ReaderInterface.hpp" +#include "../../time_types.hpp" +#include "../../type_utils.hpp" +#include "../KeyValuePairLogEvent.hpp" +#include "../SchemaTree.hpp" +#include "../SchemaTreeNode.hpp" +#include "../Value.hpp" +#include "decoding_methods.hpp" +#include "protocol_constants.hpp" +#include "utils.hpp" + +namespace clp::ffi::ir_stream { +namespace { +/** + * A collection of schema tree leaf node IDs. It represents the schema of a `KeyValuePairLogEvent`. + */ +using Schema = std::vector<SchemaTreeNode::id_t>; + +/** + * @param tag + * @return The corresponding schema tree node type on success. + * @return std::nullopt if the tag doesn't match to any defined schema tree node type. + */ +[[nodiscard]] auto schema_tree_node_tag_to_type(encoded_tag_t tag +) -> std::optional<SchemaTreeNode::Type>; + +/** + * Deserializes the parent ID of a schema tree node. + * @param reader + * @param parent_id Returns the deserialized result. + * @return IRErrorCode::IRErrorCode_Success on success. + * @return IRErrorCode::IRErrorCode_Incomplete_IR if the stream is truncated. + * @return IRErrorCode::IRErrorCode_Corrupted_IR if the next packet in the stream isn't a parent ID. + * @return Forwards `deserialize_tag`'s return values on any other failure. + */ +[[nodiscard]] auto deserialize_schema_tree_node_parent_id( + ReaderInterface& reader, + SchemaTreeNode::id_t& parent_id +) -> IRErrorCode; + +/** + * Deserializes the key name of a schema tree node. + * @param reader + * @param key_name Returns the deserialized key name. + * @return IRErrorCode::IRErrorCode_Success on success. + * @return Forwards `deserialize_tag`'s return values on failure. + * @return Forwards `deserialize_string`'s return values on failure. + */ +[[nodiscard]] auto deserialize_schema_tree_node_key_name( + ReaderInterface& reader, + std::string& key_name +) -> IRErrorCode; + +/** + * Deserializes an integer value packet. + * @param reader + * @param tag + * @param val Returns the deserialized value. + * @return IRErrorCode::IRErrorCode_Success on success. + * @return IRErrorCode::IRErrorCode_Incomplete_IR if the stream is truncated. + * @return IRErrorCode::IRErrorCode_Corrupted_IR if the given tag doesn't correspond to an integer + * packet. + */ +[[nodiscard]] auto +deserialize_int_val(ReaderInterface& reader, encoded_tag_t tag, value_int_t& val) -> IRErrorCode; + +/** + * Deserializes a string packet. + * @param reader + * @param tag + * @param deserialized_str Returns the deserialized string. + * @return IRErrorCode::IRErrorCode_Success on success. + * @return IRErrorCode::IRErrorCode_Incomplete_IR if the stream is truncated. + * @return IRErrorCode::IRErrorCode_Corrupted_IR if the given tag doesn't correspond to a string + * packet. + */ +[[nodiscard]] auto deserialize_string( + ReaderInterface& reader, + encoded_tag_t tag, + std::string& deserialized_str +) -> IRErrorCode; + +/** + * Deserializes the IDs of all keys in a log event. + * @param reader + * @param tag Takes the current tag as input and returns the last tag read. + * @param schema Returns the deserialized schema. + * @return IRErrorCode::IRErrorCode_Success on success. + * @return IRErrorCode::IRErrorCode_Incomplete_IR if the stream is truncated. + * @return Forwards `deserialize_tag`'s return values on any other failure. + */ +[[nodiscard]] auto +deserialize_schema(ReaderInterface& reader, encoded_tag_t& tag, Schema& schema) -> IRErrorCode; + +/** + * Deserializes the next value and pushes the result into `node_id_value_pairs`. + * @param reader + * @param tag + * @param node_id The node ID that corresponds to the value. + * @param node_id_value_pairs Returns the ID-value pair constructed from the deserialized value. + * @return IRErrorCode::IRErrorCode_Success on success. + * @return IRErrorCode::IRErrorCode_Incomplete_IR if the stream is truncated. + * @return IRErrorCode::IRErrorCode_Corrupted_IR if the tag doesn't correspond to any known value + * type. + * @return Forwards `deserialize_encoded_text_ast_and_insert_to_node_id_value_pairs`'s return + * values on any other failure. + */ +[[nodiscard]] auto deserialize_value_and_insert_to_node_id_value_pairs( + ReaderInterface& reader, + encoded_tag_t tag, + SchemaTreeNode::id_t node_id, + KeyValuePairLogEvent::NodeIdValuePairs& node_id_value_pairs +) -> IRErrorCode; + +/** + * Deserializes an encoded text AST and pushes the result into node_id_value_pairs. + * @tparam encoded_variable_t + * @param reader + * @param node_id The node ID that corresponds to the value. + * @param node_id_value_pairs Returns the ID-value pair constructed by the deserialized encoded text + * AST. + * @return IRErrorCode::IRErrorCode_Success on success. + * @return Forwards `deserialize_tag`'s return values on failure. + * @return Forwards `deserialize_encoded_text_ast`'s return values on failure. + */ +template <typename encoded_variable_t> +requires(std::is_same_v<ir::four_byte_encoded_variable_t, encoded_variable_t> + || std::is_same_v<ir::eight_byte_encoded_variable_t, encoded_variable_t>) +[[nodiscard]] auto deserialize_encoded_text_ast_and_insert_to_node_id_value_pairs( + ReaderInterface& reader, + SchemaTreeNode::id_t node_id, + KeyValuePairLogEvent::NodeIdValuePairs& node_id_value_pairs +) -> IRErrorCode; + +/** + * Deserializes values and constructs ID-value pairs according to the given schema. The number of + * values to deserialize is indicated by the size of the given schema. + * @param reader + * @param tag + * @param schema The log event's schema. + * @param node_id_value_pairs Returns the constructed ID-value pairs. + * @return IRErrorCode::IRErrorCode_Success on success. + * @return IRErrorCode::IRErrorCode_Corrupted_IR if a key is duplicated in the deserialized log + * event. + * @return Forwards `deserialize_tag`'s return values on failure. + * @return Forwards `deserialize_value_and_insert_to_node_id_value_pairs`'s return values on + * failure. + */ +[[nodiscard]] auto deserialize_value_and_construct_node_id_value_pairs( + ReaderInterface& reader, + encoded_tag_t tag, + Schema const& schema, + KeyValuePairLogEvent::NodeIdValuePairs& node_id_value_pairs +) -> IRErrorCode; + +auto schema_tree_node_tag_to_type(encoded_tag_t tag) -> std::optional<SchemaTreeNode::Type> { + switch (tag) { + case cProtocol::Payload::SchemaTreeNodeInt: + return SchemaTreeNode::Type::Int; + case cProtocol::Payload::SchemaTreeNodeFloat: + return SchemaTreeNode::Type::Float; + case cProtocol::Payload::SchemaTreeNodeBool: + return SchemaTreeNode::Type::Bool; + case cProtocol::Payload::SchemaTreeNodeStr: + return SchemaTreeNode::Type::Str; + case cProtocol::Payload::SchemaTreeNodeUnstructuredArray: + return SchemaTreeNode::Type::UnstructuredArray; + case cProtocol::Payload::SchemaTreeNodeObj: + return SchemaTreeNode::Type::Obj; + default: + return std::nullopt; + } +} + +auto deserialize_schema_tree_node_parent_id( + ReaderInterface& reader, + SchemaTreeNode::id_t& parent_id +) -> IRErrorCode { + encoded_tag_t tag{}; + if (auto const err{deserialize_tag(reader, tag)}; IRErrorCode::IRErrorCode_Success != err) { + return err; + } + if (cProtocol::Payload::SchemaTreeNodeParentIdUByte == tag) { + uint8_t deserialized_id{}; + if (false == deserialize_int(reader, deserialized_id)) { + return IRErrorCode::IRErrorCode_Incomplete_IR; + } + parent_id = static_cast<SchemaTreeNode::id_t>(deserialized_id); + } else if (cProtocol::Payload::SchemaTreeNodeParentIdUShort == tag) { + uint16_t deserialized_id{}; + if (false == deserialize_int(reader, deserialized_id)) { + return IRErrorCode::IRErrorCode_Incomplete_IR; + } + parent_id = static_cast<SchemaTreeNode::id_t>(deserialized_id); + } else { + return IRErrorCode::IRErrorCode_Corrupted_IR; + } + return IRErrorCode_Success; +} + +auto deserialize_schema_tree_node_key_name(ReaderInterface& reader, std::string& key_name) + -> IRErrorCode { + encoded_tag_t str_packet_tag{}; + if (auto const err{deserialize_tag(reader, str_packet_tag)}; + IRErrorCode::IRErrorCode_Success != err) + { + return err; + } + if (auto const err{deserialize_string(reader, str_packet_tag, key_name)}; + IRErrorCode::IRErrorCode_Success != err) + { + return err; + } + return IRErrorCode::IRErrorCode_Success; +} + +auto deserialize_int_val(ReaderInterface& reader, encoded_tag_t tag, value_int_t& val) + -> IRErrorCode { + if (cProtocol::Payload::ValueInt8 == tag) { + int8_t deserialized_val{}; + if (false == deserialize_int(reader, deserialized_val)) { + return IRErrorCode::IRErrorCode_Incomplete_IR; + } + val = deserialized_val; + } else if (cProtocol::Payload::ValueInt16 == tag) { + int16_t deserialized_val{}; + if (false == deserialize_int(reader, deserialized_val)) { + return IRErrorCode::IRErrorCode_Incomplete_IR; + } + val = deserialized_val; + } else if (cProtocol::Payload::ValueInt32 == tag) { + int32_t deserialized_val{}; + if (false == deserialize_int(reader, deserialized_val)) { + return IRErrorCode::IRErrorCode_Incomplete_IR; + } + val = deserialized_val; + } else if (cProtocol::Payload::ValueInt64 == tag) { + int64_t deserialized_val{}; + if (false == deserialize_int(reader, deserialized_val)) { + return IRErrorCode::IRErrorCode_Incomplete_IR; + } + val = deserialized_val; + } else { + return IRErrorCode::IRErrorCode_Corrupted_IR; + } + return IRErrorCode::IRErrorCode_Success; +} + +auto deserialize_string(ReaderInterface& reader, encoded_tag_t tag, std::string& deserialized_str) + -> IRErrorCode { + size_t str_length{}; + if (cProtocol::Payload::StrLenUByte == tag) { + uint8_t length{}; + if (false == deserialize_int(reader, length)) { + return IRErrorCode::IRErrorCode_Incomplete_IR; + } + str_length = static_cast<size_t>(length); + } else if (cProtocol::Payload::StrLenUShort == tag) { + uint16_t length{}; + if (false == deserialize_int(reader, length)) { + return IRErrorCode::IRErrorCode_Incomplete_IR; + } + str_length = static_cast<size_t>(length); + } else if (cProtocol::Payload::StrLenUInt == tag) { + uint32_t length{}; + if (false == deserialize_int(reader, length)) { + return IRErrorCode::IRErrorCode_Incomplete_IR; + } + str_length = static_cast<size_t>(length); + } else { + return IRErrorCode::IRErrorCode_Corrupted_IR; + } + if (clp::ErrorCode_Success != reader.try_read_string(str_length, deserialized_str)) { + return IRErrorCode::IRErrorCode_Incomplete_IR; + } + return IRErrorCode::IRErrorCode_Success; +} + +auto deserialize_schema(ReaderInterface& reader, encoded_tag_t& tag, Schema& schema) + -> IRErrorCode { + schema.clear(); + while (true) { + if (cProtocol::Payload::KeyIdUByte == tag) { + uint8_t id{}; + if (false == deserialize_int(reader, id)) { + return IRErrorCode::IRErrorCode_Incomplete_IR; + } + schema.push_back(static_cast<SchemaTreeNode::id_t>(id)); + } else if (cProtocol::Payload::KeyIdUShort == tag) { + uint16_t id{}; + if (false == deserialize_int(reader, id)) { + return IRErrorCode::IRErrorCode_Incomplete_IR; + } + schema.push_back(static_cast<SchemaTreeNode::id_t>(id)); + } else { + break; + } + + if (auto const err{deserialize_tag(reader, tag)}; IRErrorCode::IRErrorCode_Success != err) { + return err; + } + } + + return IRErrorCode::IRErrorCode_Success; +} + +auto deserialize_value_and_insert_to_node_id_value_pairs( + ReaderInterface& reader, + encoded_tag_t tag, + SchemaTreeNode::id_t node_id, + KeyValuePairLogEvent::NodeIdValuePairs& node_id_value_pairs +) -> IRErrorCode { + switch (tag) { + case cProtocol::Payload::ValueInt8: + case cProtocol::Payload::ValueInt16: + case cProtocol::Payload::ValueInt32: + case cProtocol::Payload::ValueInt64: { + value_int_t value_int{}; + if (auto const err{deserialize_int_val(reader, tag, value_int)}; + IRErrorCode::IRErrorCode_Success != err) + { + return err; + } + node_id_value_pairs.emplace(node_id, Value{value_int}); + break; + } + case cProtocol::Payload::ValueFloat: { + uint64_t val{}; + if (false == deserialize_int(reader, val)) { + return IRErrorCode::IRErrorCode_Incomplete_IR; + } + node_id_value_pairs.emplace(node_id, Value{bit_cast<value_float_t>(val)}); + break; + } + case cProtocol::Payload::ValueTrue: + node_id_value_pairs.emplace(node_id, Value{true}); + break; + case cProtocol::Payload::ValueFalse: + node_id_value_pairs.emplace(node_id, Value{false}); + break; + case cProtocol::Payload::StrLenUByte: + case cProtocol::Payload::StrLenUShort: + case cProtocol::Payload::StrLenUInt: { + std::string value_str; + if (auto const err{deserialize_string(reader, tag, value_str)}; + IRErrorCode::IRErrorCode_Success != err) + { + return err; + } + node_id_value_pairs.emplace(node_id, Value{std::move(value_str)}); + break; + } + case cProtocol::Payload::ValueEightByteEncodingClpStr: + if (auto const err{deserialize_encoded_text_ast_and_insert_to_node_id_value_pairs< + ir::eight_byte_encoded_variable_t>(reader, node_id, node_id_value_pairs)}; + IRErrorCode::IRErrorCode_Success != err) + { + return err; + } + break; + case cProtocol::Payload::ValueFourByteEncodingClpStr: + if (auto const err{deserialize_encoded_text_ast_and_insert_to_node_id_value_pairs< + ir::four_byte_encoded_variable_t>(reader, node_id, node_id_value_pairs)}; + IRErrorCode::IRErrorCode_Success != err) + { + return err; + } + break; + case cProtocol::Payload::ValueNull: + node_id_value_pairs.emplace(node_id, Value{}); + break; + case cProtocol::Payload::ValueEmpty: + node_id_value_pairs.emplace(node_id, std::nullopt); + break; + default: + return IRErrorCode::IRErrorCode_Corrupted_IR; + } + return IRErrorCode::IRErrorCode_Success; +} + +template <typename encoded_variable_t> +requires(std::is_same_v<ir::four_byte_encoded_variable_t, encoded_variable_t> + || std::is_same_v<ir::eight_byte_encoded_variable_t, encoded_variable_t>) +[[nodiscard]] auto deserialize_encoded_text_ast_and_insert_to_node_id_value_pairs( + ReaderInterface& reader, + SchemaTreeNode::id_t node_id, + KeyValuePairLogEvent::NodeIdValuePairs& node_id_value_pairs +) -> IRErrorCode { + encoded_tag_t tag{}; + if (auto const err{deserialize_tag(reader, tag)}; IRErrorCode::IRErrorCode_Success != err) { + return err; + } + + std::string logtype; + std::vector<encoded_variable_t> encoded_vars; + std::vector<std::string> dict_vars; + if (auto const err{deserialize_encoded_text_ast(reader, tag, logtype, encoded_vars, dict_vars)}; + IRErrorCode::IRErrorCode_Success != err) + { + return err; + } + + node_id_value_pairs.emplace( + node_id, + Value{ir::EncodedTextAst<encoded_variable_t>{logtype, dict_vars, encoded_vars}} + ); + return IRErrorCode::IRErrorCode_Success; +} + +auto deserialize_value_and_construct_node_id_value_pairs( + ReaderInterface& reader, + encoded_tag_t tag, + Schema const& schema, + KeyValuePairLogEvent::NodeIdValuePairs& node_id_value_pairs +) -> IRErrorCode { + node_id_value_pairs.clear(); + node_id_value_pairs.reserve(schema.size()); + for (auto const node_id : schema) { + if (node_id_value_pairs.contains(node_id)) { + // The key should be unique in a schema + return IRErrorCode_Corrupted_IR; + } + + if (auto const err{deserialize_value_and_insert_to_node_id_value_pairs( + reader, + tag, + node_id, + node_id_value_pairs + )}; + IRErrorCode::IRErrorCode_Success != err) + { + return err; + } + + if (schema.size() != node_id_value_pairs.size()) { + if (auto const err{deserialize_tag(reader, tag)}; + IRErrorCode::IRErrorCode_Success != err) + { + return err; + } + } + } + return IRErrorCode::IRErrorCode_Success; +} +} // namespace + +auto deserialize_ir_unit_schema_tree_node_insertion( + ReaderInterface& reader, + encoded_tag_t tag, + std::string& key_name +) -> OUTCOME_V2_NAMESPACE::std_result<SchemaTree::NodeLocator> { + auto const type{schema_tree_node_tag_to_type(tag)}; + if (false == type.has_value()) { + return ir_error_code_to_errc(IRErrorCode::IRErrorCode_Corrupted_IR); + } + + SchemaTreeNode::id_t parent_id{}; + if (auto const err{deserialize_schema_tree_node_parent_id(reader, parent_id)}; + IRErrorCode_Success != err) + { + return ir_error_code_to_errc(err); + } + + if (auto const err{deserialize_schema_tree_node_key_name(reader, key_name)}; + IRErrorCode::IRErrorCode_Success != err) + { + return ir_error_code_to_errc(err); + } + + return SchemaTree::NodeLocator{parent_id, key_name, type.value()}; +} + +auto deserialize_ir_unit_utc_offset_change(ReaderInterface& reader +) -> OUTCOME_V2_NAMESPACE::std_result<UtcOffset> { + UtcOffset utc_offset{0}; + if (auto const err{deserialize_utc_offset_change(reader, utc_offset)}; + IRErrorCode::IRErrorCode_Success != err) + { + return ir_error_code_to_errc(err); + } + return utc_offset; +} + +auto deserialize_ir_unit_kv_pair_log_event( + ReaderInterface& reader, + encoded_tag_t tag, + std::shared_ptr<SchemaTree> schema_tree, + UtcOffset utc_offset +) -> OUTCOME_V2_NAMESPACE::std_result<KeyValuePairLogEvent> { + Schema schema; + if (auto const err{deserialize_schema(reader, tag, schema)}; + IRErrorCode::IRErrorCode_Success != err) + { + return ir_error_code_to_errc(err); + } + + KeyValuePairLogEvent::NodeIdValuePairs node_id_value_pairs; + if (false == schema.empty()) { + if (auto const err{deserialize_value_and_construct_node_id_value_pairs( + reader, + tag, + schema, + node_id_value_pairs + )}; + IRErrorCode::IRErrorCode_Success != err) + { + return ir_error_code_to_errc(err); + } + } else { + if (cProtocol::Payload::ValueEmpty != tag) { + return ir_error_code_to_errc(IRErrorCode::IRErrorCode_Corrupted_IR); + } + } + + return KeyValuePairLogEvent::create( + std::move(schema_tree), + std::move(node_id_value_pairs), + utc_offset + ); +} +} // namespace clp::ffi::ir_stream diff --git a/components/core/src/clp/ffi/ir_stream/ir_unit_deserialization_methods.hpp b/components/core/src/clp/ffi/ir_stream/ir_unit_deserialization_methods.hpp new file mode 100644 index 000000000..0ba21866c --- /dev/null +++ b/components/core/src/clp/ffi/ir_stream/ir_unit_deserialization_methods.hpp @@ -0,0 +1,68 @@ +#ifndef CLP_FFI_IR_STREAM_IR_UNIT_DESERIALIZATION_METHODS_HPP +#define CLP_FFI_IR_STREAM_IR_UNIT_DESERIALIZATION_METHODS_HPP + +#include <memory> +#include <string> + +#include <outcome/single-header/outcome.hpp> + +#include "../../ReaderInterface.hpp" +#include "../../time_types.hpp" +#include "../KeyValuePairLogEvent.hpp" +#include "../SchemaTree.hpp" +#include "decoding_methods.hpp" + +namespace clp::ffi::ir_stream { +/** + * Deserializes a schema tree node insertion IR unit. + * @param reader + * @param tag + * @param key_name Returns the key name of the deserialized new node. This should be the underlying + * storage of the returned schema tree node locator. + * @return A result containing the locator of the inserted schema tree node or an error code + * indicating the failure: + * - std::errc::result_out_of_range if the IR stream is truncated. + * - std::errc::protocol_error if the deserialized node type isn't supported. + * - Forwards `deserialize_schema_tree_node_key_name`'s return values. + * - Forwards `deserialize_schema_tree_node_parent_id`'s return values. + */ +[[nodiscard]] auto deserialize_ir_unit_schema_tree_node_insertion( + ReaderInterface& reader, + encoded_tag_t tag, + std::string& key_name +) -> OUTCOME_V2_NAMESPACE::std_result<SchemaTree::NodeLocator>; + +/** + * Deserializes a UTC offset change IR unit. + * @param reader + * @return A result containing the new UTC offset or an error code indicating the failure: + * - std::errc::result_out_of_range if the IR stream is truncated. + * - Forwards `clp::ffi::ir_stream::deserialize_utc_offset_change`'s return values. + */ +[[nodiscard]] auto deserialize_ir_unit_utc_offset_change(ReaderInterface& reader +) -> OUTCOME_V2_NAMESPACE::std_result<UtcOffset>; + +/** + * Deserializes a key-value pair log event IR unit. + * @param reader + * @param tag + * @param schema_tree Schema tree used to construct the KV-pair log event. + * @param utc_offset UTC offset used to construct the KV-pair log event. + * @return A result containing the deserialized log event or an error code indicating the + * failure: + * - std::errc::result_out_of_range if the IR stream is truncated. + * - std::errc::protocol_error if the IR stream is corrupted. + * - std::errc::protocol_not_supported if the IR stream contains an unsupported metadata format + * or uses an unsupported version. + * - Forwards `KeyValuePairLogEvent::create`'s return values if the intermediate deserialized result + * cannot construct a valid key-value pair log event. + */ +[[nodiscard]] auto deserialize_ir_unit_kv_pair_log_event( + ReaderInterface& reader, + encoded_tag_t tag, + std::shared_ptr<SchemaTree> schema_tree, + UtcOffset utc_offset +) -> OUTCOME_V2_NAMESPACE::std_result<KeyValuePairLogEvent>; +} // namespace clp::ffi::ir_stream + +#endif // CLP_FFI_IR_STREAM_IR_UNIT_DESERIALIZATION_METHODS_HPP diff --git a/components/core/src/clp/ffi/ir_stream/utils.cpp b/components/core/src/clp/ffi/ir_stream/utils.cpp index 66277720c..cfbc9d5a9 100644 --- a/components/core/src/clp/ffi/ir_stream/utils.cpp +++ b/components/core/src/clp/ffi/ir_stream/utils.cpp @@ -2,11 +2,13 @@ #include <cstdint> #include <string_view> +#include <system_error> #include <vector> #include <json/single_include/nlohmann/json.hpp> #include "../../type_utils.hpp" +#include "decoding_methods.hpp" #include "protocol_constants.hpp" namespace clp::ffi::ir_stream { @@ -49,4 +51,18 @@ auto serialize_string(std::string_view str, std::vector<int8_t>& output_buf) -> output_buf.insert(output_buf.cend(), str.cbegin(), str.cend()); return true; } + +auto ir_error_code_to_errc(IRErrorCode ir_error_code) -> std::errc { + switch (ir_error_code) { + case IRErrorCode_Incomplete_IR: + return std::errc::result_out_of_range; + case IRErrorCode_Corrupted_IR: + case IRErrorCode_Decode_Error: + return std::errc::protocol_error; + case IRErrorCode_Eof: + return std::errc::no_message_available; + default: + return std::errc::not_supported; + } +} } // namespace clp::ffi::ir_stream diff --git a/components/core/src/clp/ffi/ir_stream/utils.hpp b/components/core/src/clp/ffi/ir_stream/utils.hpp index b816c6385..4e68ca67a 100644 --- a/components/core/src/clp/ffi/ir_stream/utils.hpp +++ b/components/core/src/clp/ffi/ir_stream/utils.hpp @@ -5,6 +5,7 @@ #include <span> #include <string> #include <string_view> +#include <system_error> #include <vector> #include <json/single_include/nlohmann/json.hpp> @@ -13,6 +14,7 @@ #include "../../ir/types.hpp" #include "../../ReaderInterface.hpp" #include "byteswap.hpp" +#include "decoding_methods.hpp" #include "encoding_methods.hpp" #include "protocol_constants.hpp" @@ -68,6 +70,12 @@ template <typename encoded_variable_t> */ [[nodiscard]] auto serialize_string(std::string_view str, std::vector<int8_t>& output_buf) -> bool; +/** + * @param ir_error_code + * @return Equivalent `std::errc` code indicating the same error type. + */ +[[nodiscard]] auto ir_error_code_to_errc(IRErrorCode ir_error_code) -> std::errc; + template <typename integer_t> auto serialize_int(integer_t value, std::vector<int8_t>& output_buf) -> void { integer_t value_big_endian{}; diff --git a/components/core/tests/test-ir_encoding_methods.cpp b/components/core/tests/test-ir_encoding_methods.cpp index af8314369..ba8c59280 100644 --- a/components/core/tests/test-ir_encoding_methods.cpp +++ b/components/core/tests/test-ir_encoding_methods.cpp @@ -1123,6 +1123,7 @@ TEMPLATE_TEST_CASE( } flush_and_clear_serializer_buffer(serializer, ir_buf); + ir_buf.push_back(clp::ffi::ir_stream::cProtocol::Eof); // Deserialize the results BufferReader reader{size_checked_pointer_cast<char>(ir_buf.data()), ir_buf.size()}; @@ -1143,4 +1144,8 @@ TEMPLATE_TEST_CASE( REQUIRE_FALSE(serialized_json_result.has_error()); REQUIRE((json_obj == serialized_json_result.value())); } + + auto const eof_result{deserializer.deserialize_to_next_log_event(reader)}; + REQUIRE(eof_result.has_error()); + REQUIRE((std::errc::no_message_available == eof_result.error())); }