Skip to content

Commit

Permalink
ffi: Add class for key-value pair log events. (y-scope#507)
Browse files Browse the repository at this point in the history
Co-authored-by: kirkrodrigues <[email protected]>
  • Loading branch information
2 people authored and Jack Luo committed Dec 4, 2024
1 parent b7ad520 commit aad78ce
Show file tree
Hide file tree
Showing 7 changed files with 707 additions and 210 deletions.
4 changes: 3 additions & 1 deletion components/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,8 @@ set(SOURCE_FILES_unitTest
src/clp/ffi/ir_stream/Serializer.hpp
src/clp/ffi/ir_stream/utils.cpp
src/clp/ffi/ir_stream/utils.hpp
src/clp/ffi/KeyValuePairLogEvent.cpp
src/clp/ffi/KeyValuePairLogEvent.hpp
src/clp/ffi/SchemaTree.cpp
src/clp/ffi/SchemaTree.hpp
src/clp/ffi/SchemaTreeNode.hpp
Expand Down Expand Up @@ -492,8 +494,8 @@ set(SOURCE_FILES_unitTest
tests/test-BufferedFileReader.cpp
tests/test-EncodedVariableInterpreter.cpp
tests/test-encoding_methods.cpp
tests/test-ffi_KeyValuePairLogEvent.cpp
tests/test-ffi_SchemaTree.cpp
tests/test-ffi_Value.cpp
tests/test-Grep.cpp
tests/test-hash_utils.cpp
tests/test-ir_encoding_methods.cpp
Expand Down
170 changes: 170 additions & 0 deletions components/core/src/clp/ffi/KeyValuePairLogEvent.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
#include "KeyValuePairLogEvent.hpp"

#include <memory>
#include <string>
#include <string_view>
#include <system_error>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>

#include <outcome/single-header/outcome.hpp>

#include "../ir/EncodedTextAst.hpp"
#include "../time_types.hpp"
#include "SchemaTree.hpp"
#include "SchemaTreeNode.hpp"
#include "Value.hpp"

using clp::ir::EightByteEncodedTextAst;
using clp::ir::FourByteEncodedTextAst;
using std::string;

namespace clp::ffi {
namespace {
/**
* @param type
* @param value
* @return Whether the given schema tree node type matches the given value's type.
*/
[[nodiscard]] auto
node_type_matches_value_type(SchemaTreeNode::Type type, Value const& value) -> bool;

/**
* Validates whether the given node-ID value pairs are leaf nodes in the `SchemaTree` forming a
* sub-tree of their own.
* @param schema_tree
* @param node_id_value_pairs
* @return success if the inputs are valid, or an error code indicating the failure:
* - std::errc::operation_not_permitted if a node ID doesn't represent a valid node in the
* schema tree, or a non-leaf node ID is paired with a value.
* - std::errc::protocol_error if the schema tree node type doesn't match the value's type.
* - std::errc::protocol_not_supported if the same key appears more than once under a parent
* node.
*/
[[nodiscard]] auto validate_node_id_value_pairs(
SchemaTree const& schema_tree,
KeyValuePairLogEvent::NodeIdValuePairs const& node_id_value_pairs
) -> std::errc;

/**
* @param schema_tree
* @param node_id
* @param node_id_value_pairs
* @return Whether the given node is a leaf node in the sub-tree of the `SchemaTree` defined by
* `node_id_value_pairs`. A node is considered a leaf if none of its descendants appear in
* `node_id_value_pairs`.
*/
[[nodiscard]] auto is_leaf_node(
SchemaTree const& schema_tree,
SchemaTreeNode::id_t node_id,
KeyValuePairLogEvent::NodeIdValuePairs const& node_id_value_pairs
) -> bool;

auto node_type_matches_value_type(SchemaTreeNode::Type type, Value const& value) -> bool {
switch (type) {
case SchemaTreeNode::Type::Obj:
return value.is_null();
case SchemaTreeNode::Type::Int:
return value.is<value_int_t>();
case SchemaTreeNode::Type::Float:
return value.is<value_float_t>();
case SchemaTreeNode::Type::Bool:
return value.is<value_bool_t>();
case SchemaTreeNode::Type::UnstructuredArray:
return value.is<FourByteEncodedTextAst>() || value.is<EightByteEncodedTextAst>();
case SchemaTreeNode::Type::Str:
return value.is<string>() || value.is<FourByteEncodedTextAst>()
|| value.is<EightByteEncodedTextAst>();
default:
return false;
}
}

auto validate_node_id_value_pairs(
SchemaTree const& schema_tree,
KeyValuePairLogEvent::NodeIdValuePairs const& node_id_value_pairs
) -> std::errc {
try {
std::unordered_map<SchemaTreeNode::id_t, std::unordered_set<std::string_view>>
parent_node_id_to_key_names;
for (auto const& [node_id, value] : node_id_value_pairs) {
if (SchemaTree::cRootId == node_id) {
return std::errc::operation_not_permitted;
}

auto const& node{schema_tree.get_node(node_id)};
auto const node_type{node.get_type()};
if (false == value.has_value()) {
// Value is an empty object (`{}`, which is not the same as `null`)
if (SchemaTreeNode::Type::Obj != node_type) {
return std::errc::protocol_error;
}
} else if (false == node_type_matches_value_type(node_type, value.value())) {
return std::errc::protocol_error;
}

if (SchemaTreeNode::Type::Obj == node_type
&& false == is_leaf_node(schema_tree, node_id, node_id_value_pairs))
{
// The node's value is `null` or `{}` but its descendants appear in
// `node_id_value_pairs`.
return std::errc::operation_not_permitted;
}

auto const parent_node_id{node.get_parent_id()};
auto const key_name{node.get_key_name()};
if (parent_node_id_to_key_names.contains(parent_node_id)) {
auto const [it, new_key_inserted]{
parent_node_id_to_key_names.at(parent_node_id).emplace(key_name)
};
if (false == new_key_inserted) {
// The key is duplicated under the same parent
return std::errc::protocol_not_supported;
}
} else {
parent_node_id_to_key_names.emplace(parent_node_id, std::unordered_set{key_name});
}
}
} catch (SchemaTree::OperationFailed const& ex) {
return std::errc::operation_not_permitted;
}
return std::errc{};
}

auto is_leaf_node(
SchemaTree const& schema_tree,
SchemaTreeNode::id_t node_id,
KeyValuePairLogEvent::NodeIdValuePairs const& node_id_value_pairs
) -> bool {
std::vector<SchemaTreeNode::id_t> dfs_stack;
dfs_stack.reserve(schema_tree.get_size());
dfs_stack.push_back(node_id);
while (false == dfs_stack.empty()) {
auto const curr_node_id{dfs_stack.back()};
dfs_stack.pop_back();
for (auto const child_node_id : schema_tree.get_node(curr_node_id).get_children_ids()) {
if (node_id_value_pairs.contains(child_node_id)) {
return false;
}
dfs_stack.push_back(child_node_id);
}
}
return true;
}
} // namespace

auto KeyValuePairLogEvent::create(
std::shared_ptr<SchemaTree const> schema_tree,
NodeIdValuePairs node_id_value_pairs,
UtcOffset utc_offset
) -> OUTCOME_V2_NAMESPACE::std_result<KeyValuePairLogEvent> {
if (auto const ret_val{validate_node_id_value_pairs(*schema_tree, node_id_value_pairs)};
std::errc{} != ret_val)
{
return ret_val;
}
return KeyValuePairLogEvent{std::move(schema_tree), std::move(node_id_value_pairs), utc_offset};
}
} // namespace clp::ffi
81 changes: 81 additions & 0 deletions components/core/src/clp/ffi/KeyValuePairLogEvent.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#ifndef CLP_FFI_KEYVALUEPAIRLOGEVENT_HPP
#define CLP_FFI_KEYVALUEPAIRLOGEVENT_HPP

#include <memory>
#include <optional>
#include <unordered_map>
#include <utility>

#include <outcome/single-header/outcome.hpp>

#include "../time_types.hpp"
#include "SchemaTree.hpp"
#include "SchemaTreeNode.hpp"
#include "Value.hpp"

namespace clp::ffi {
/**
* A log event containing key-value pairs. Each event contains:
* - A collection of node-ID & value pairs, where each pair represents a leaf `SchemaTreeNode` in
* the `SchemaTree`.
* - A reference to the `SchemaTree`
* - The UTC offset of the current log event
*/
class KeyValuePairLogEvent {
public:
// Types
using NodeIdValuePairs = std::unordered_map<SchemaTreeNode::id_t, std::optional<Value>>;

// Factory functions
/**
* @param schema_tree
* @param node_id_value_pairs
* @param utc_offset
* @return A result containing the key-value pair log event or an error code indicating the
* failure. See `valdiate_node_id_value_pairs` for the possible error codes.
*/
[[nodiscard]] static auto create(
std::shared_ptr<SchemaTree const> schema_tree,
NodeIdValuePairs node_id_value_pairs,
UtcOffset utc_offset
) -> OUTCOME_V2_NAMESPACE::std_result<KeyValuePairLogEvent>;

// Disable copy constructor and assignment operator
KeyValuePairLogEvent(KeyValuePairLogEvent const&) = delete;
auto operator=(KeyValuePairLogEvent const&) -> KeyValuePairLogEvent& = delete;

// Default move constructor and assignment operator
KeyValuePairLogEvent(KeyValuePairLogEvent&&) = default;
auto operator=(KeyValuePairLogEvent&&) -> KeyValuePairLogEvent& = default;

// Destructor
~KeyValuePairLogEvent() = default;

// Methods
[[nodiscard]] auto get_schema_tree() const -> SchemaTree const& { return *m_schema_tree; }

[[nodiscard]] auto get_node_id_value_pairs() const -> NodeIdValuePairs const& {
return m_node_id_value_pairs;
}

[[nodiscard]] auto get_utc_offset() const -> UtcOffset { return m_utc_offset; }

private:
// Constructor
KeyValuePairLogEvent(
std::shared_ptr<SchemaTree const> schema_tree,
NodeIdValuePairs node_id_value_pairs,
UtcOffset utc_offset
)
: m_schema_tree{std::move(schema_tree)},
m_node_id_value_pairs{std::move(node_id_value_pairs)},
m_utc_offset{utc_offset} {}

// Variables
std::shared_ptr<SchemaTree const> m_schema_tree;
NodeIdValuePairs m_node_id_value_pairs;
UtcOffset m_utc_offset{0};
};
} // namespace clp::ffi

#endif // CLP_FFI_KEYVALUEPAIRLOGEVENT_HPP
11 changes: 0 additions & 11 deletions components/core/src/clp/ffi/Value.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,17 +150,6 @@ class Value {
template <FundamentalPrimitiveValueType T>
explicit Value(T value) : m_value{value} {}

// Disable copy constructor and assignment operator
Value(Value const&) = delete;
auto operator=(Value const&) -> Value& = delete;

// Default move constructor and assignment operator
Value(Value&&) = default;
auto operator=(Value&&) -> Value& = default;

// Destructor
~Value() = default;

// Methods
/**
* @tparam T
Expand Down
11 changes: 0 additions & 11 deletions components/core/src/clp/ir/EncodedTextAst.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,6 @@ class EncodedTextAst {
m_dict_vars{std::move(dict_vars)},
m_encoded_vars{std::move(encoded_vars)} {}

// Disable copy constructor and assignment operator
EncodedTextAst(EncodedTextAst const&) = delete;
auto operator=(EncodedTextAst const&) -> EncodedTextAst& = delete;

// Default move constructor and assignment operator
EncodedTextAst(EncodedTextAst&&) = default;
auto operator=(EncodedTextAst&&) -> EncodedTextAst& = default;

// Destructor
~EncodedTextAst() = default;

// Methods
auto operator==(EncodedTextAst const&) const -> bool = default;

Expand Down
Loading

0 comments on commit aad78ce

Please sign in to comment.