Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix compound intermediate nodes #432

Merged
merged 2 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion callbacks.c
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@ cb_match_tree (struct callback_node *callbacks, GNode *root)
GList *callbacks_to_call = NULL;
struct callback_node *next_level;

if (callbacks == NULL)
if (callbacks == NULL || root == NULL)
{
return NULL;
}
Expand All @@ -545,6 +545,8 @@ cb_match_tree (struct callback_node *callbacks, GNode *root)

GNode *last_bit = apteryx_path_to_node (new_root, root->data, NULL);

assert (last_bit);

last_bit->children = root->children;
root->data = last_bit->data;
root->parent = last_bit->parent;
Expand Down
83 changes: 71 additions & 12 deletions rpc.c
Original file line number Diff line number Diff line change
Expand Up @@ -935,41 +935,100 @@ rpc_msg_decode_string (rpc_message msg)
return value;
}

static void rpc_msg_encode_tree_full (rpc_message msg, GNode *root, bool break_key);

static void
rpc_msg_add_children (rpc_message msg, GNode *root)
{
GNode *child = NULL;

/* If there are any children, write them here. */
if (g_node_first_child(root))
if (g_node_first_child (root))
{
rpc_msg_encode_uint8 (msg, rpc_start_children);
for (child = g_node_first_child(root); child; child = g_node_next_sibling(child))
for (child = g_node_first_child (root); child; child = g_node_next_sibling (child))
{
rpc_msg_encode_tree (msg, child);
rpc_msg_encode_tree_full (msg, child, true);
}
rpc_msg_encode_uint8 (msg, rpc_end_children);
}
}

void
rpc_msg_encode_tree (rpc_message msg, GNode *root)
static void
rpc_msg_encode_tree_full (rpc_message msg, GNode *root, bool break_key)
{
const char *key = APTERYX_NAME (root);
const char *value = APTERYX_HAS_VALUE (root) ? APTERYX_VALUE (root) : NULL;

rpc_msg_encode_uint8 (msg, rpc_value);
/* Write this key (and value). */
rpc_msg_encode_string (msg, key);
rpc_msg_encode_string (msg, value ?: "");
if (break_key && strchr (key, '/'))
{
char *broken_key = g_strdup (key);
char *end;
char *chunk = broken_key;
int extra_nodes = 0;

while ((end = strchr (chunk, '/')) != NULL)
{
/* Truncate at the first slash */
*end = '\0';

/* This is an intermediate node, so we don't ever want to
* print the value attached to the end, just the first bit of
* the key.
*/
rpc_msg_encode_uint8 (msg, rpc_value);
rpc_msg_encode_string (msg, chunk);
rpc_msg_encode_string (msg, "");

/* We are going to need to close exactly this many children nodes
* at the end of the loop, so keep count.
*/
extra_nodes++;
rpc_msg_encode_uint8 (msg, rpc_start_children);

/* Skip past the null terminator */
chunk = end + 1;
}

/* Write the end of the key and the value (if any.) */
rpc_msg_encode_uint8 (msg, rpc_value);
rpc_msg_encode_string (msg, chunk);
rpc_msg_encode_string (msg, value ?: "");
g_free (broken_key);

/* If this node has no value, add its children. */
if (!APTERYX_HAS_VALUE (root))
{
rpc_msg_add_children (msg, root);
}

/* If this node has no value, add its children. */
if (!APTERYX_HAS_VALUE (root))
/* Close all the children opened when breaking up the key */
while (extra_nodes--)
{
rpc_msg_encode_uint8 (msg, rpc_end_children);
}
}
else
{
rpc_msg_add_children (msg, root);
rpc_msg_encode_uint8 (msg, rpc_value);
/* Write this key (and value). */
rpc_msg_encode_string (msg, key);
rpc_msg_encode_string (msg, value ?: "");

/* If this node has no value, add its children. */
if (!APTERYX_HAS_VALUE (root))
{
rpc_msg_add_children (msg, root);
}
}
}

void
rpc_msg_encode_tree (rpc_message msg, GNode *root)
{
rpc_msg_encode_tree_full (msg, root, false);
}

static GNode *
_rpc_msg_decode_tree (rpc_message msg, GNode *root)
{
Expand Down
40 changes: 39 additions & 1 deletion test.c
Original file line number Diff line number Diff line change
Expand Up @@ -7167,6 +7167,43 @@ _watch_tree_cleanup ()
CU_ASSERT (assert_apteryx_empty ());
}

void
test_set_tree_long_end_nodes ()
{
GNode *root = APTERYX_NODE (NULL, g_strdup (TEST_PATH));
APTERYX_LEAF (root, g_strdup ("multiple/keys/in/path"), g_strdup ("please"));
APTERYX_LEAF (root, g_strdup ("multiple/keys/other/path"), g_strdup ("crash?"));
apteryx_watch_tree (TEST_PATH"/*", test_watch_tree_callback);
CU_ASSERT (apteryx_set_tree (root));
usleep (TEST_SLEEP_TIMEOUT);
CU_ASSERT (g_node_n_nodes (watch_tree_root, G_TRAVERSE_LEAVES) == 2);
apteryx_free_tree (root);
apteryx_prune (TEST_PATH);
usleep (TEST_SLEEP_TIMEOUT);

apteryx_unwatch_tree (TEST_PATH"/*", test_watch_tree_callback);
apteryx_prune (TEST_PATH);
_watch_tree_cleanup ();
}

void
test_set_tree_long_intermediate_node ()
{
GNode *root = APTERYX_NODE(NULL, g_strdup (TEST_PATH));
GNode *node = APTERYX_NODE (root, g_strdup ("multiple/keys"));
APTERYX_LEAF (node, g_strdup ("in/path"), g_strdup ("different"));
APTERYX_LEAF (node, g_strdup ("other/path"), g_strdup ("test"));
APTERYX_LEAF (node, g_strdup ("more/values"), g_strdup ("check"));
apteryx_watch_tree (TEST_PATH"/*", test_watch_tree_callback);
CU_ASSERT (apteryx_set_tree (root));
usleep (TEST_SLEEP_TIMEOUT);
CU_ASSERT (g_node_n_nodes (watch_tree_root, G_TRAVERSE_LEAVES) == 3);

apteryx_unwatch_tree (TEST_PATH"/*", test_watch_tree_callback);
apteryx_prune (TEST_PATH);
_watch_tree_cleanup ();
}

void
test_watch_tree ()
{
Expand Down Expand Up @@ -10630,6 +10667,8 @@ static CU_TestInfo tests_api_tree[] = {
{ "tree sort children", test_tree_sort_children },
{ "set tree", test_set_tree },
{ "set tree empty", test_set_tree_empty },
{ "set tree long end nodes", test_set_tree_long_end_nodes },
{ "set tree long intermediate node", test_set_tree_long_intermediate_node },
{ "get tree", test_get_tree },
{ "get tree single node", test_get_tree_single_node },
{ "get tree value on_branch", test_get_tree_value_on_branch },
Expand Down Expand Up @@ -10726,7 +10765,6 @@ static CU_TestInfo tests_api_tree[] = {
{ "query filter requires provided value", test_query_filter_on_provided },
{ "query filter response contains provided value", test_query_filter_selects_provided },
{ "query filter doesn't call provied value if not required", test_query_filter_avoids_provided },

{ "cas tree", test_cas_tree},
{ "cas tree detailed", test_cas_tree_detailed},
{ "tree atomic", test_tree_atomic},
Expand Down
Loading