tree data OPTIMIZE unlink-insert for lyds nodes
Unlink and then insert is inefficient for sorted (lyds) list and
leaf-list nodes. Also improved lyd_unlink_siblings().
diff --git a/src/tree_data.c b/src/tree_data.c
index 9332ca8..782b118 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -775,6 +775,180 @@
}
/**
+ * @brief Check that @p node can be unlinked.
+ *
+ * @param[in] node Node to check
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_unlink_check(struct lyd_node *node)
+{
+ if (!node) {
+ return LY_SUCCESS;
+ }
+
+ if (lysc_is_key(node->schema) && node->parent) {
+ LOGERR(LYD_CTX(node), LY_EINVAL, "Cannot unlink a list key \"%s\", unlink the list instance instead.",
+ LYD_NAME(node));
+ return LY_EINVAL;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Move schema instances before anchor or as the last.
+ *
+ * The nodes will remain sorted according to the schema.
+ *
+ * @param[in] first_sibling First sibling, destination.
+ * @param[in] node Starting node, all following nodes with the same schema will be moved.
+ * @param[out] next_p Next node that has a different schema or NULL.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_move_nodes_ordby_schema(struct lyd_node *first_sibling, struct lyd_node *node, struct lyd_node **next_p)
+{
+ struct lyd_node *second, *anchor, *iter, *next, *dst, *src;
+
+ assert(first_sibling && !first_sibling->prev->next && node && next_p);
+
+ if ((anchor = lyd_insert_node_find_anchor(first_sibling, node))) {
+ /* move the first node to the correct place according to the schema */
+ LY_CHECK_RET(lyd_unlink_check(node));
+ second = node->next;
+ lyd_unlink_ignore_lyds(node);
+ lyd_insert_before_node(anchor, node);
+ lyd_insert_hash(node);
+ if (!second || (node->schema != second->schema)) {
+ /* no more nodes to move */
+ *next_p = second;
+ return LY_SUCCESS;
+ }
+ dst = node;
+ src = second;
+ } else {
+ /* just move all instances to the end */
+ dst = first_sibling->prev;
+ src = node;
+ }
+
+ /* move the rest of source instances after @p node */
+ LY_LIST_FOR_SAFE(src, next, iter) {
+ LY_CHECK_RET(lyd_unlink_check(iter));
+ if (iter->schema != src->schema) {
+ break;
+ }
+ lyd_unlink_ignore_lyds(iter);
+ lyd_insert_after_node(dst, iter);
+ lyd_insert_hash(iter);
+ dst = iter;
+ }
+ *next_p = iter;
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Move nodes regardless of schema.
+ *
+ * The destination for the move is NULL, or a childless parent.
+ *
+ * @param[in] parent Parent to insert into, NULL for top-level sibling.
+ * @param[in] first_src First sibling, all following nodes will be moved.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_move_nodes_at_once(struct lyd_node *parent, struct lyd_node *first_src)
+{
+ struct lyd_node *start, *next, *iter;
+
+ assert(!lyd_child(parent) && first_src && !first_src->prev->next && !first_src->parent);
+
+ LY_CHECK_RET(lyd_unlink_check(first_src));
+
+ /* move the first node */
+ start = first_src->next;
+ if (parent) {
+ lyd_unlink_ignore_lyds(first_src);
+ lyd_insert_only_child(parent, first_src);
+ lyd_insert_hash(first_src);
+ } else {
+ lyd_unlink_ignore_lyds(first_src);
+ }
+
+ /* move the rest of the nodes */
+ LY_LIST_FOR_SAFE(start, next, iter) {
+ LY_CHECK_RET(lyd_unlink_check(iter));
+ lyd_unlink_ignore_lyds(iter);
+ lyd_insert_after_node(first_src->prev, iter);
+ lyd_insert_hash(iter);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Move the nodes in parts according to the schema.
+ *
+ * @param[in] first_sibling First sibling, destination.
+ * @param[in] first_src First sibling, all following nodes will be moved.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_move_nodes_by_schema(struct lyd_node *first_sibling, struct lyd_node *first_src)
+{
+ LY_ERR ret;
+ struct lyd_node *next, *iter, *leader;
+
+ assert(first_sibling && !first_sibling->prev->next && first_src && !first_src->prev->next && !first_src->parent);
+
+ for (iter = first_src; iter; iter = next) {
+ if (lyds_is_supported(iter) &&
+ (lyd_find_sibling_schema(first_sibling, iter->schema, &leader) == LY_SUCCESS)) {
+ ret = lyds_merge(&leader, iter, &next);
+ if (ret) {
+ /* The operation on the sorting tree unexpectedly failed due to some internal issue,
+ * but insert the node anyway although the nodes will not be sorted.
+ */
+ LOGWRN(LYD_CTX(first_src), "Data in \"%s\" are not sorted.", leader->schema->name);
+ LY_CHECK_RET(lyd_move_nodes_ordby_schema(first_sibling, next, &next));
+ }
+ } else {
+ LY_CHECK_RET(lyd_move_nodes_ordby_schema(first_sibling, iter, &next));
+ }
+ first_sibling = iter->prev->next ? first_sibling : iter;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Move a nodes into parent/siblings.
+ *
+ * @param[in] parent Parent to insert into, NULL for top-level sibling.
+ * @param[in] first_sibling First sibling, NULL if no top-level sibling exist yet. Can be also NULL if @p parent is set.
+ * @param[in] first_src First sibling, all following nodes will be moved.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_move_nodes(struct lyd_node *parent, struct lyd_node *first_sibling, struct lyd_node *first_src)
+{
+ LY_ERR ret;
+
+ assert(first_src && (!first_sibling || !first_sibling->prev->next) && !first_src->prev->next);
+
+ first_sibling = first_sibling ? first_sibling : lyd_child(parent);
+ if (!first_sibling) {
+ ret = lyd_move_nodes_at_once(parent, first_src);
+ } else {
+ ret = lyd_move_nodes_by_schema(first_sibling, first_src);
+ }
+
+ return ret;
+}
+
+/**
* @brief Check schema place of a node to be inserted.
*
* @param[in] parent Schema node of the parent data node.
@@ -825,28 +999,18 @@
LIBYANG_API_DEF LY_ERR
lyd_insert_child(struct lyd_node *parent, struct lyd_node *node)
{
- struct lyd_node *iter;
-
LY_CHECK_ARG_RET(NULL, parent, node, !parent->schema || (parent->schema->nodetype & LYD_NODE_INNER), LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(LYD_CTX(parent), LYD_CTX(node), LY_EINVAL);
LY_CHECK_RET(lyd_insert_check_schema(parent->schema, NULL, node->schema));
- if (node->schema && (node->schema->flags & LYS_KEY)) {
- LOGERR(LYD_CTX(parent), LY_EINVAL, "Cannot insert key \"%s\".", node->schema->name);
- return LY_EINVAL;
- }
-
- if (node->parent || node->prev->next) {
- lyd_unlink(node);
- }
-
- while (node) {
- iter = node->next;
- lyd_unlink(node);
+ if (node->parent || node->prev->next || !node->next) {
+ lyd_unlink_tree(node);
lyd_insert_node(parent, NULL, node, 0);
- node = iter;
+ } else {
+ lyd_move_nodes(parent, NULL, node);
}
+
return LY_SUCCESS;
}
@@ -875,7 +1039,7 @@
LIBYANG_API_DEF LY_ERR
lyd_insert_sibling(struct lyd_node *sibling, struct lyd_node *node, struct lyd_node **first)
{
- struct lyd_node *iter;
+ struct lyd_node *first_sibling;
LY_CHECK_ARG_RET(NULL, node, LY_EINVAL);
@@ -883,30 +1047,16 @@
LY_CHECK_RET(lyd_insert_check_schema(NULL, sibling->schema, node->schema));
}
- sibling = lyd_first_sibling(sibling);
-
- if (node->parent || node->prev->next) {
- lyd_unlink(node);
- }
-
- while (node) {
- if (lysc_is_key(node->schema)) {
- LOGERR(LYD_CTX(node), LY_EINVAL, "Cannot insert key \"%s\".", node->schema->name);
- return LY_EINVAL;
- }
-
- iter = node->next;
- lyd_unlink(node);
- lyd_insert_node(NULL, &sibling, node, 0);
- node = iter;
+ first_sibling = lyd_first_sibling(sibling);
+ if (node->parent || node->prev->next || !node->next) {
+ lyd_unlink_tree(node);
+ lyd_insert_node(NULL, &first_sibling, node, 0);
+ } else {
+ lyd_move_nodes(NULL, first_sibling, node);
}
if (first) {
- /* find the first sibling */
- *first = sibling;
- while ((*first)->prev->next) {
- *first = (*first)->prev;
- }
+ *first = node->prev->next ? first_sibling : node;
}
return LY_SUCCESS;
@@ -961,23 +1111,9 @@
}
void
-lyd_unlink(struct lyd_node *node)
+lyd_unlink_ignore_lyds(struct lyd_node *node)
{
- struct lyd_node *iter, *leader;
-
- if (!node) {
- return;
- }
-
- /* unlink from the lyds tree */
- if (lyds_is_supported(node)) {
- if (LYD_NODE_IS_ALONE(node)) {
- leader = node;
- } else {
- lyd_find_sibling_val(node, node->schema, NULL, 0, &leader);
- }
- lyds_unlink(&leader, node);
- }
+ struct lyd_node *iter;
/* update hashes while still linked into the tree */
lyd_unlink_hash(node);
@@ -1024,32 +1160,59 @@
node->prev = node;
}
+void
+lyd_unlink(struct lyd_node *node)
+{
+ struct lyd_node *leader;
+
+ if (!node) {
+ return;
+ }
+
+ /* unlink from the lyds tree */
+ if (lyds_is_supported(node)) {
+ if (!node->prev->next || (node->prev->schema != node->schema)) {
+ leader = node;
+ } else {
+ lyd_find_sibling_val(node, node->schema, NULL, 0, &leader);
+ assert(leader);
+ }
+ lyds_unlink(&leader, node);
+ }
+
+ /* unlink data tree */
+ lyd_unlink_ignore_lyds(node);
+}
+
LIBYANG_API_DEF void
lyd_unlink_siblings(struct lyd_node *node)
{
- struct lyd_node *next, *elem, *first = NULL;
+ struct lyd_node *next, *iter, *leader, *start;
- LY_LIST_FOR_SAFE(node, next, elem) {
- if (lysc_is_key(elem->schema) && elem->parent) {
- LOGERR(LYD_CTX(elem), LY_EINVAL, "Cannot unlink a list key \"%s\", unlink the list instance instead.",
- LYD_NAME(elem));
- return;
- }
+ if (lyds_is_supported(node) && node->prev->next && (node->prev->schema == node->schema)) {
+ /* unlink starts at the non-first item in the (leaf-)list */
+ lyd_find_sibling_val(node, node->schema, NULL, 0, &leader);
+ lyds_split(leader, node, &start);
+ } else {
+ /* unlink @p node */
+ LY_CHECK_RET(lyd_unlink_check(node), );
+ start = node->next;
+ lyd_unlink_ignore_lyds(node);
+ }
- lyd_unlink(elem);
- lyd_insert_node(NULL, &first, elem, 1);
+ /* continue unlinking the rest */
+ LY_LIST_FOR_SAFE(start, next, iter) {
+ LY_CHECK_RET(lyd_unlink_check(iter), );
+ lyd_unlink_ignore_lyds(iter);
+ lyd_insert_after_node(node->prev, iter);
+ lyd_insert_hash(iter);
}
}
LIBYANG_API_DEF void
lyd_unlink_tree(struct lyd_node *node)
{
- if (node && lysc_is_key(node->schema) && node->parent) {
- LOGERR(LYD_CTX(node), LY_EINVAL, "Cannot unlink a list key \"%s\", unlink the list instance instead.",
- LYD_NAME(node));
- return;
- }
-
+ LY_CHECK_RET(lyd_unlink_check(node), );
lyd_unlink(node);
}
diff --git a/src/tree_data_free.c b/src/tree_data_free.c
index b1b48b3..d76a4aa 100644
--- a/src/tree_data_free.c
+++ b/src/tree_data_free.c
@@ -282,7 +282,7 @@
/* in case of the top-level nodes (node->parent is NULL), no unlinking needed */
if (iter->parent) {
lyds_free_metadata(iter);
- lyd_unlink(iter);
+ lyd_unlink_ignore_lyds(iter);
}
lyd_free_subtree(iter);
}
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
index 1cc58c6..d3aa1a7 100644
--- a/src/tree_data_internal.h
+++ b/src/tree_data_internal.h
@@ -665,4 +665,13 @@
*/
LY_ERR lyd_unlink_leafref_node(const struct lyd_node_term *node, const struct lyd_node_term *leafref_node);
+/**
+ * @brief Unlink the specified data subtree.
+ *
+ * The lyds_unlink() is NOT called in this function.
+ *
+ * @param[in] node Data tree node to be unlinked (together with all the children).
+ */
+void lyd_unlink_ignore_lyds(struct lyd_node *node);
+
#endif /* LY_TREE_DATA_INTERNAL_H_ */
diff --git a/src/tree_data_sorted.c b/src/tree_data_sorted.c
index 94f47cc..39b16ef 100644
--- a/src/tree_data_sorted.c
+++ b/src/tree_data_sorted.c
@@ -118,6 +118,16 @@
RBN_COLOR(DST) = RBN_COLOR(SRC);
/**
+ * @brief Reset the red-black node and set new dnode.
+ *
+ * @param[in] RBN Node to reset.
+ * @param[in] DNODE New dnode value for @p rbn.
+ */
+#define RBN_RESET(RBN, DNODE) \
+ *(RBN) = (const struct rb_node){0}; \
+ RBN_DNODE(RBN) = DNODE;
+
+/**
* @brief Metadata name of the Red-black tree.
*/
#define RB_NAME "lyds_tree"
@@ -1085,6 +1095,67 @@
}
void
+lyds_split(struct lyd_node *leader, struct lyd_node *node, struct lyd_node **next_p)
+{
+ struct rb_node *rbt, *rbn;
+ struct lyd_node *iter, *next, *start, *dst;
+ struct lyd_meta *root_meta;
+
+ assert(leader && node);
+
+ rbt = lyds_get_rb_tree(leader, &root_meta);
+ if (!rbt || (leader == node)) {
+ /* Second list is just unlinked */
+ start = node->next;
+ lyd_unlink_ignore_lyds(node);
+ dst = node;
+ LY_LIST_FOR_SAFE(start, next, iter) {
+ if (iter->schema != node->schema) {
+ break;
+ }
+ lyd_unlink_ignore_lyds(iter);
+ lyd_insert_after_node(dst, iter);
+ dst = iter;
+ }
+ *next_p = iter;
+ return;
+ }
+
+ start = node->next;
+ if (!start || (start->schema != node->schema)) {
+ /* @p node is the last node, remove from Red-black tree and unlink */
+ rb_remove_node(root_meta, &rbt, node, &rbn);
+ rb_free_node(rbn);
+ lyd_unlink_ignore_lyds(node);
+ *next_p = start;
+ goto cleanup;
+ }
+
+ /* remove @p node from Red-black tree and unlink */
+ rb_remove_node(root_meta, &rbt, node, &rbn);
+ rb_free_node(rbn);
+ lyd_unlink_ignore_lyds(node);
+
+ /* remove the rest of nodes from Red-black tree and unlink */
+ dst = node;
+ LY_LIST_FOR_SAFE(start, next, iter) {
+ if (iter->schema != node->schema) {
+ break;
+ }
+ rb_remove_node(root_meta, &rbt, iter, &rbn);
+ rb_free_node(rbn);
+ lyd_unlink_ignore_lyds(iter);
+ /* insert them to the second (leaf-)list */
+ lyd_insert_after_node(dst, iter);
+ dst = iter;
+ }
+ *next_p = iter;
+
+cleanup:
+ RBT_SET(root_meta, rbt);
+}
+
+void
lyds_free_metadata(struct lyd_node *node)
{
struct lyd_meta *root_meta;
@@ -1095,6 +1166,280 @@
}
}
+/**
+ * @brief Merge nodes (src) without Red-black tree into (dst) nodes with Red-black tree.
+ *
+ * @param[in,out] leader_dst First instance of the destination (leaf-)list.
+ * @param[in] root_meta_dst Metadata 'lyds_tree' from @p leader_dst.
+ * @param[in] rbt_dst Root of the destination Red-black tree.
+ * @param[in] leader_src First instance of the source (leaf-)list.
+ * @param[out] next_p Data node located after source (leaf-)list.
+ * On error, it points to the some node in source (leaf-)list that failed to merge.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyds_merge_nodes1(struct lyd_node **leader_dst, struct lyd_meta *root_meta_dst, struct rb_node *rbt_dst,
+ struct lyd_node *leader_src, struct lyd_node **next_p)
+{
+ LY_ERR ret;
+ struct rb_node *rbn;
+ struct lyd_node *iter, *next;
+ const struct lysc_node *schema;
+
+ schema = leader_src->schema;
+ for (iter = leader_src; iter && (iter->schema == schema); iter = next) {
+ ret = rb_insert(iter, &rbt_dst, &rbn);
+ if (ret) {
+ /* allocation failed, @p next_p must refer to failed node */
+ break;
+ }
+ next = iter->next;
+ lyd_unlink_ignore_lyds(iter);
+ lyds_link_data_node(leader_dst, iter, root_meta_dst, rbn);
+ lyd_insert_hash(iter);
+ }
+ *next_p = iter;
+
+ RBT_SET(root_meta_dst, rbt_dst);
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Merge nodes which belongs before @p leader_dst.
+ *
+ * Reminder:
+ * Merge nodes (src) with Red-black tree into (dst) nodes without Red-black tree.
+ * Create red-black nodes from destination (leaf-)list and insert them into @p rbt_src.
+ * At the end of the lyds_merge_nodes2(), rbt_src will move and become rbt_dst.
+ *
+ * @param[in,out] leader_dst First instance of the destination (leaf-)list.
+ * @param[in] leader_src First instance of the source (leaf-)list.
+ * @param[in,out] rbt_src Root of the source Red-black tree.
+ * @param[out] dst_iter Last merged node into the @p rbt_src.
+ * @param[out] next_p On error, points to data node which failed to merge.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyds_merge_nodes2_front(struct lyd_node **leader_dst, struct lyd_node *leader_src,
+ struct rb_node **rbt_src, struct rb_node **dst_iter, struct lyd_node **next_p)
+{
+ LY_ERR ret;
+ struct rb_node *prev, *iter;
+ struct lyd_node *dst;
+
+ /* insert destination leader */
+ ret = rb_insert(*leader_dst, rbt_src, dst_iter);
+ LY_CHECK_ERR_RET(ret, *next_p = leader_src, ret);
+
+ /* iterate over source RB tree and move the nodes belonging before destination leader */
+ prev = rb_prev(*dst_iter);
+ dst = *leader_dst;
+ for (iter = prev; iter; iter = rb_prev(iter)) {
+ lyd_unlink_ignore_lyds(RBN_DNODE(iter));
+ lyd_insert_before_node(dst, RBN_DNODE(iter));
+ lyd_insert_hash(RBN_DNODE(iter));
+ dst = RBN_DNODE(iter);
+ }
+ if (prev) {
+ *leader_dst = dst;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Merge nodes which belongs between destination (leaf-)list nodes.
+ *
+ * Reminder:
+ * Merge nodes (src) with Red-black tree into (dst) nodes without Red-black tree.
+ * Create red-black nodes from destination (leaf-)list and insert them into @p rbt_src.
+ * At the end of the lyds_merge_nodes2(), rbt_src will move and become rbt_dst.
+ *
+ * @param[in,out] leader_dst First instance of the destination (leaf-)list.
+ * @param[in,out] rbt_src Root of the source Red-black tree.
+ * @param[out] dst_iter Last merged node into the @p rbt_src.
+ * @param[out] next_p On error, points to data node which failed to merge.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyds_merge_nodes2_among(struct lyd_node **leader_dst, struct rb_node **rbt_src, struct rb_node **dst_iter,
+ struct lyd_node **next_p)
+{
+ LY_ERR ret;
+ struct rb_node *rbn, *iter, *rbn_prev;
+ struct lyd_node *dst, *node;
+ const struct lysc_node *schema;
+
+ schema = (*leader_dst)->schema;
+ rbn = *dst_iter;
+ for (node = RBN_DNODE(*dst_iter); node->next && (node->schema == schema); node = dst->next) {
+ /* insert node from destination (leaf-)list into @p rbt_src */
+ rbn_prev = rbn;
+ ret = rb_insert(node->next, rbt_src, &rbn);
+ LY_CHECK_ERR_RET(ret, *next_p = RBN_DNODE(rb_next(rbn_prev)), ret);
+
+ dst = node;
+ /* move source data nodes between next-to-last inserted node and last inserted node */
+ for (iter = rb_next(rbn_prev); iter != rbn; iter = rb_next(iter)) {
+ lyd_unlink_ignore_lyds(RBN_DNODE(iter));
+ /* insert source data nodes into destination (leaf-)list */
+ lyd_insert_after_node(dst, RBN_DNODE(iter));
+ lyd_insert_hash(RBN_DNODE(iter));
+ dst = RBN_DNODE(iter);
+ }
+ }
+ *dst_iter = rbn;
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Merge nodes which belongs after destination (leaf-)list nodes.
+ *
+ * Reminder:
+ * Merge nodes (src) with Red-black tree into (dst) nodes without Red-black tree.
+ * Create red-black nodes from destination (leaf-)list and insert them into @p rbt_src.
+ * At the end of the lyds_merge_nodes2(), rbt_src will move and become rbt_dst.
+ *
+ * @param[in] dst_iter Last merged node into the @p rbt_src.
+ * @param[out] next_p Data node located after source (leaf-)list.
+ */
+static void
+lyds_merge_nodes2_back(struct rb_node *dst_iter, struct lyd_node **next_p)
+{
+ struct rb_node *iter, *begin;
+ struct lyd_node *dst;
+
+ begin = rb_next(dst_iter);
+ LY_CHECK_RET(!begin,; );
+ dst = RBN_DNODE(dst_iter);
+ for (iter = begin; iter; iter = rb_next(iter)) {
+ *next_p = RBN_DNODE(iter)->next;
+ lyd_unlink_ignore_lyds(RBN_DNODE(iter));
+ lyd_insert_after_node(dst, RBN_DNODE(iter));
+ lyd_insert_hash(RBN_DNODE(iter));
+ dst = RBN_DNODE(iter);
+ }
+}
+
+/**
+ * @brief Merge nodes (src) with Red-black tree into (dst) nodes without Red-black tree.
+ *
+ * Create red-black nodes from destination (leaf-)list and insert them into @p rbt_src.
+ * At the end of the lyds_merge_nodes2(), rbt_src will move and become rbt_dst.
+ *
+ * @param[in,out] leader_dst First instance of the destination (leaf-)list.
+ * @param[in] leader_src First instance of the source (leaf-)list.
+ * @param[in] root_meta_src Metadata 'lyds_tree' from @p leader_src.
+ * @param[in] rbt_src Root of the source Red-black tree.
+ * @param[out] next_p Data node located after source (leaf-)list.
+ * On error, points to data node which failed to merge.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyds_merge_nodes2(struct lyd_node **leader_dst, struct lyd_node *leader_src, struct lyd_meta *root_meta_src,
+ struct rb_node *rbt_src, struct lyd_node **next_p)
+{
+ LY_ERR ret;
+ struct rb_node *dst_iter;
+
+ /* merge first destination node, move source nodes which belongs before this node */
+ ret = lyds_merge_nodes2_front(leader_dst, leader_src, &rbt_src, &dst_iter, next_p);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* RB tree si moved from source to destination (leaf-)list */
+ lyds_move_meta(*leader_dst, root_meta_src);
+
+ /* merge the rest of the destination nodes, move corresponding source nodes */
+ ret = lyds_merge_nodes2_among(leader_dst, &rbt_src, &dst_iter, next_p);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* move the rest of the source nodes */
+ lyds_merge_nodes2_back(dst_iter, next_p);
+
+cleanup:
+ RBT_SET(root_meta_src, rbt_src);
+
+ return ret;
+}
+
+/**
+ * @brief Merge nodes (src) with Red-black tree into (dst) nodes with Red-black tree.
+ *
+ * Possible improvement: find out which rb_tree is bigger and then move nodes into it.
+ * Current implementation blindly guesses that the source Red-black tree is smaller.
+ *
+ * @param[in,out] leader_dst First instance of the destination (leaf-)list.
+ * @param[in] root_meta_dst Metadata 'lyds_tree' from @p leader_dst.
+ * @param[in] rbt_dst Root of the destination Red-black tree.
+ * @param[in] root_meta_src Metadata 'lyds_tree' from @p leader_src.
+ * @param[in] rbt_src Root of the source Red-black tree.
+ * @param[out] next_p Data node located after source (leaf-)list.
+ */
+static void
+lyds_merge_nodes3(struct lyd_node **leader_dst, struct lyd_meta *root_meta_dst, struct rb_node *rbt_dst,
+ struct lyd_meta *root_meta_src, struct rb_node *rbt_src, struct lyd_node **next_p)
+{
+ struct rb_node *iter, *iter_state;
+ struct lyd_node *node;
+
+ /* release source RB tree */
+ RBT_SET(root_meta_src, NULL);
+ lyd_free_meta_single(root_meta_src);
+
+ /* 'randomly' iterate over all source nodes, merge and move to the destination */
+ for (iter = rb_iter_begin(rbt_src, &iter_state); iter; iter = rb_iter_next(&iter_state)) {
+ node = RBN_DNODE(iter);
+ *next_p = node->next;
+ RBN_RESET(iter, node);
+ rb_insert_node(&rbt_dst, iter);
+ lyd_unlink_ignore_lyds(node);
+ lyds_link_data_node(leader_dst, node, root_meta_dst, iter);
+ lyd_insert_hash(node);
+ }
+
+ RBT_SET(root_meta_dst, rbt_dst);
+}
+
+LY_ERR
+lyds_merge(struct lyd_node **leader_dst, struct lyd_node *leader_src, struct lyd_node **next_p)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct rb_node *rbt_dst, *rbt_src;
+ struct lyd_meta *root_meta_dst, *root_meta_src = NULL;
+
+ assert(leader_dst && leader_src && next_p);
+
+ rbt_dst = lyds_get_rb_tree(*leader_dst, &root_meta_dst);
+ rbt_src = lyds_get_rb_tree(leader_src, &root_meta_src);
+
+ if (root_meta_src && !rbt_src) {
+ /* Release unnecessary metadata which can be empty, e.g. when duplicating a node */
+ lyd_free_meta_single(root_meta_src);
+ }
+
+ if (!rbt_dst && !rbt_src) {
+ /* create RB tree from destination nodes, merge and move source nodes */
+ LY_CHECK_RET(lyds_create_metadata(*leader_dst, &root_meta_dst));
+ LY_CHECK_RET(lyds_additionally_create_rb_tree(*leader_dst, root_meta_dst, &rbt_dst));
+ ret = lyds_merge_nodes1(leader_dst, root_meta_dst, rbt_dst, leader_src, next_p);
+ } else if (rbt_dst && !rbt_src) {
+ /* just merge and move source nodes */
+ ret = lyds_merge_nodes1(leader_dst, root_meta_dst, rbt_dst, leader_src, next_p);
+ } else if (!rbt_dst && rbt_src) {
+ /* merge destination nodes with RB tree, move RB tree and source nodes */
+ ret = lyds_merge_nodes2(leader_dst, leader_src, root_meta_src, rbt_src, next_p);
+ } else {
+ /* merge and move source nodes, release source RB tree */
+ assert(rbt_dst && rbt_src);
+ lyds_merge_nodes3(leader_dst, root_meta_dst, rbt_dst,
+ root_meta_src, rbt_src, next_p);
+ }
+
+ return ret;
+}
+
int
lyds_compare_single(struct lyd_node *node1, struct lyd_node *node2)
{
diff --git a/src/tree_data_sorted.h b/src/tree_data_sorted.h
index dfeb719..238ffc6 100644
--- a/src/tree_data_sorted.h
+++ b/src/tree_data_sorted.h
@@ -88,6 +88,45 @@
void lyds_unlink(struct lyd_node **leader, struct lyd_node *node);
/**
+ * @brief Split the (leaf-)list in two.
+ *
+ * The second (leaf-)list is unlinked from the rest of the data nodes.
+ * The hash for the data nodes is removed.
+ *
+ * @param[in] leader First instance of (leaf-)list.
+ * @param[in] node Node in the (leaf-)list from which the second (leaf-)list will start.
+ * @param[out] next Data node located after the second (leaf-)list.
+ * The rest of the (leaf-)list nodes will belong under the second (leaf-)list.
+ */
+void lyds_split(struct lyd_node *leader, struct lyd_node *node, struct lyd_node **next);
+
+/**
+ * @brief Merge source (leaf-)list nodes into destination (leaf-)list nodes.
+ *
+ * Pointers in sibling data nodes (lyd_node) are modified.
+ * The hash for the data nodes will be adjusted.
+ *
+ * @param[in,out] leader_dst Destination (leaf-)list, first instance. It may not contain
+ * the lyds_tree metadata or BST. After merge @p leader_dst can be reset to new leader.
+ * @param[in] leader_src Source (leaf-)list, first instance. It may not contain the lyds_tree metadata or BST.
+ * @param[out] next Data node located after source (leaf-)list.
+ * On error, points to data node which failed to merge.
+ * @return LY_ERR value.
+ */
+LY_ERR lyds_merge(struct lyd_node **leader_dst, struct lyd_node *leader_src, struct lyd_node **next);
+
+/**
+ * @brief Compare (sort) 2 data nodes.
+ *
+ * @param[in] node1 The first node to compare.
+ * @param[in] node2 The second node to compare.
+ * @return Negative number if val1 < val2,
+ * @return Zero if val1 == val2,
+ * @return Positive number if val1 > val2.
+ */
+int lyds_compare_single(struct lyd_node *node1, struct lyd_node *node2);
+
+/**
* @brief Release the metadata including BST.
*
* No more nodes can be inserted after the function is executed.
@@ -103,15 +142,4 @@
*/
void lyds_free_tree(struct rb_node *rbt);
-/**
- * @brief Compare (sort) 2 data nodes.
- *
- * @param[in] node1 The first node to compare.
- * @param[in] node2 The second node to compare.
- * @return Negative number if val1 < val2,
- * @return Zero if val1 == val2,
- * @return Positive number if val1 > val2.
- */
-int lyds_compare_single(struct lyd_node *node1, struct lyd_node *node2);
-
#endif /* _LYDS_TREE_H_ */
diff --git a/tests/utests/data/test_tree_data_sorted.c b/tests/utests/data/test_tree_data_sorted.c
index f68efa2..8b06f4f 100644
--- a/tests/utests/data/test_tree_data_sorted.c
+++ b/tests/utests/data/test_tree_data_sorted.c
@@ -1173,6 +1173,324 @@
lyd_free_all(first);
}
+static void
+test_move_whole_list(void **state)
+{
+ const char *schema;
+ struct lys_module *mod;
+ struct lyd_node *src, *dst, *node, *first;
+
+ schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;"
+ "leaf-list tll {type string;}"
+ "container cn {"
+ " leaf head {type string;}"
+ " leaf-list ll {type string;}"
+ " leaf tail {type string;}"
+ "}}";
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+
+ /* move into the void: top-level, nothing will happen */
+ assert_int_equal(lyd_new_term(NULL, mod, "tll", "1", 0, &src), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(NULL, mod, "tll", "2", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_insert_sibling(src, node, NULL), LY_SUCCESS);
+ assert_int_equal(lyd_insert_sibling(NULL, src, &first), LY_SUCCESS);
+ assert_true(src && src->meta && get_rbt(src->meta) && src == first);
+ assert_string_equal(src->meta->name, META_NAME);
+ lyd_free_all(src);
+
+ /* move to childless parent: lyd_insert_child */
+ assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &src), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(src, mod, "ll", "1", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(src, mod, "ll", "2", 0, &node), LY_SUCCESS);
+ first = lyd_child(src);
+ lyd_unlink_siblings(first);
+ assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &dst), LY_SUCCESS);
+ assert_int_equal(lyd_insert_child(dst, first), LY_SUCCESS);
+ first = lyd_child(dst);
+ assert_true(first && first->meta && get_rbt(first->meta) && first->next);
+ assert_string_equal(first->meta->name, META_NAME);
+ assert_true(!lyd_child(src));
+ lyd_free_all(src);
+ lyd_free_all(dst);
+
+ /* move before the anchor */
+ assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &src), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(src, mod, "ll", "1", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(src, mod, "ll", "2", 0, &node), LY_SUCCESS);
+ first = lyd_child(src);
+ lyd_unlink_siblings(lyd_child(src));
+ assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &dst), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(dst, mod, "tail", "a", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_insert_sibling(lyd_child(dst), first, NULL), LY_SUCCESS);
+ first = lyd_child(dst);
+ assert_true(first && first->meta && get_rbt(first->meta) && first->next);
+ assert_string_equal(first->meta->name, META_NAME);
+ assert_true(!lyd_child(src));
+ lyd_free_all(src);
+ lyd_free_all(dst);
+
+ /* move to the last position */
+ assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &src), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(src, mod, "ll", "1", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(src, mod, "ll", "2", 0, &node), LY_SUCCESS);
+ first = lyd_child(src);
+ lyd_unlink_siblings(lyd_child(src));
+ assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &dst), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(dst, mod, "head", "a", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_insert_sibling(lyd_child(dst), first, NULL), LY_SUCCESS);
+ first = lyd_child(dst);
+ assert_true(first && first->next);
+ node = first->next;
+ assert_true(node && node->meta && get_rbt(node->meta) && node->next);
+ assert_string_equal(node->meta->name, META_NAME);
+ assert_true(!lyd_child(src));
+ lyd_free_all(src);
+ lyd_free_all(dst);
+}
+
+static void
+test_move_part_list(void **state)
+{
+ const char *schema;
+ struct lys_module *mod;
+ struct lyd_node *src, *dst, *node, *first;
+
+ schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;"
+ "leaf-list tll {type string;}"
+ "container cn {"
+ " leaf head {type string;}"
+ " leaf-list ll {type string;}"
+ " leaf tail {type string;}"
+ "}}";
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+
+ /* move into the void: top-level, first node is unlinked */
+ assert_int_equal(lyd_new_term(NULL, mod, "tll", "1", 0, &src), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(NULL, mod, "tll", "2", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_insert_sibling(src, node, NULL), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(NULL, mod, "tll", "3", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_insert_sibling(src, node, NULL), LY_SUCCESS);
+ assert_int_equal(lyd_insert_sibling(NULL, src->next, &first), LY_SUCCESS);
+ assert_true(src && src->meta && get_rbt(src->meta));
+ assert_string_equal(src->meta->name, META_NAME);
+ assert_true(src && src->next);
+ assert_string_equal(lyd_get_value(first), "2");
+ lyd_free_all(src);
+ lyd_free_all(first);
+
+ /* move into the void: inner node, node is unlinked */
+ assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &src), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(src, mod, "ll", "1", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(src, mod, "ll", "2", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(src, mod, "ll", "3", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_insert_sibling(NULL, lyd_child(src), &dst), LY_SUCCESS);
+ first = lyd_child(src);
+ assert_true(first && first->meta && get_rbt(first->meta) && first->next);
+ assert_string_equal(first->meta->name, META_NAME);
+ assert_true(dst && !dst->meta && !dst->next);
+ lyd_free_all(src);
+ lyd_free_all(dst);
+
+ /* move to childless parent: lyd_insert_child */
+ assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &src), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(src, mod, "ll", "1", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(src, mod, "ll", "2", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(src, mod, "ll", "3", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &dst), LY_SUCCESS);
+ assert_int_equal(lyd_insert_child(dst, lyd_child(src)), LY_SUCCESS);
+ first = lyd_child(src);
+ assert_true(first && first->meta && get_rbt(first->meta) && first->next);
+ assert_string_equal(first->meta->name, META_NAME);
+ first = lyd_child(dst);
+ assert_true(first && !first->meta);
+ lyd_free_all(src);
+ lyd_free_all(dst);
+
+ /* move before the anchor */
+ assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &src), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(src, mod, "ll", "1", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(src, mod, "ll", "2", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(src, mod, "ll", "3", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &dst), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(dst, mod, "tail", "a", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_insert_sibling(lyd_child(dst), lyd_child(src), NULL), LY_SUCCESS);
+ first = lyd_child(src);
+ assert_true(first && first->meta && get_rbt(first->meta) && first->next);
+ assert_string_equal(first->meta->name, META_NAME);
+ first = lyd_child(dst);
+ assert_true(first && !first->meta && first->next);
+ assert_string_equal(lyd_get_value(first), "1");
+ lyd_free_all(src);
+ lyd_free_all(dst);
+
+ /* move to the last position */
+ assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &src), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(src, mod, "ll", "1", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(src, mod, "ll", "2", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(src, mod, "ll", "3", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &dst), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(dst, mod, "head", "a", 0, &node), LY_SUCCESS);
+ assert_int_equal(lyd_insert_sibling(lyd_child(dst), lyd_child(src), NULL), LY_SUCCESS);
+ first = lyd_child(src);
+ assert_true(first && first->meta && get_rbt(first->meta) && first->next);
+ assert_string_equal(first->meta->name, META_NAME);
+ first = lyd_child(dst);
+ assert_true(first && first->next);
+ node = first->next;
+ assert_true(node && !node->meta && !node->next);
+ assert_string_equal(lyd_get_value(node), "1");
+ lyd_free_all(src);
+ lyd_free_all(dst);
+}
+
+static void
+test_merge_whole_list(void **state)
+{
+ const char *schema, *data;
+ char *check[] = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"};
+ struct lys_module *mod;
+ struct lyd_node *src, *dst, *first, *iter;
+ int i;
+
+ schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;"
+ "leaf-list ll {type uint32;}}";
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+
+ /* src with LYD_PARSE_ORDERED and dst with LYD_PARSE_ORDERED */
+ data = "{\"a:ll\":[1,2,5,6,9,10]}";
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, LYD_PARSE_ORDERED, LYD_VALIDATE_PRESENT, LY_SUCCESS, src);
+ assert_true(src && !src->meta);
+ data = "{\"a:ll\":[3,4,7,8]}";
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, LYD_PARSE_ORDERED, LYD_VALIDATE_PRESENT, LY_SUCCESS, dst);
+ assert_true(dst && !dst->meta);
+ assert_int_equal(lyd_insert_sibling(dst, src, &first), LY_SUCCESS);
+ assert_true(first->meta && get_rbt(first->meta));
+ assert_string_equal(first->meta->name, META_NAME);
+ i = 0;
+ LY_LIST_FOR(first, iter) {
+ assert_string_equal(lyd_get_value(iter), check[i]);
+ ++i;
+ }
+ lyd_free_all(dst);
+
+ /* src with LYD_PARSE_ORDERED dst with lyds_tree */
+ data = "{\"a:ll\":[1,2,5,6,9,10]}";
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, LYD_PARSE_ORDERED, LYD_VALIDATE_PRESENT, LY_SUCCESS, src);
+ assert_true(src && !src->meta);
+ data = "{\"a:ll\":[3,4,7,8]}";
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, dst);
+ assert_true(dst && dst->meta);
+ assert_string_equal(dst->meta->name, META_NAME);
+ assert_int_equal(lyd_insert_sibling(dst, src, &first), LY_SUCCESS);
+ assert_true(first->meta && get_rbt(first->meta) && !first->next->next->meta);
+ assert_string_equal(first->meta->name, META_NAME);
+ i = 0;
+ LY_LIST_FOR(first, iter) {
+ assert_string_equal(lyd_get_value(iter), check[i]);
+ ++i;
+ }
+ lyd_free_all(dst);
+
+ /* src with lyds_tree and dst with LYD_PARSE_ORDERED */
+ data = "{\"a:ll\":[1,2,5,6,9,10]}";
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, src);
+ assert_true(src && src->meta);
+ assert_string_equal(src->meta->name, META_NAME);
+ data = "{\"a:ll\":[3,4,7,8]}";
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, LYD_PARSE_ORDERED, LYD_VALIDATE_PRESENT, LY_SUCCESS, dst);
+ assert_true(dst && !dst->meta);
+ assert_int_equal(lyd_insert_sibling(dst, src, &first), LY_SUCCESS);
+ assert_true(first->meta && get_rbt(first->meta));
+ assert_string_equal(first->meta->name, META_NAME);
+ i = 0;
+ LY_LIST_FOR(first, iter) {
+ assert_string_equal(lyd_get_value(iter), check[i]);
+ ++i;
+ }
+ lyd_free_all(dst);
+
+ /* src with lyds_tree and dst with lyds_tree */
+ data = "{\"a:ll\":[1,2,5,6,9,10]}";
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, src);
+ assert_true(src && src->meta);
+ assert_string_equal(src->meta->name, META_NAME);
+ data = "{\"a:ll\":[3,4,7,8]}";
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, dst);
+ assert_true(dst && dst->meta);
+ assert_string_equal(dst->meta->name, META_NAME);
+ assert_int_equal(lyd_insert_sibling(dst, src, &first), LY_SUCCESS);
+ assert_true(first->meta && get_rbt(first->meta));
+ assert_string_equal(first->meta->name, META_NAME);
+ i = 0;
+ LY_LIST_FOR(first, iter) {
+ assert_string_equal(lyd_get_value(iter), check[i]);
+ ++i;
+ }
+ lyd_free_all(dst);
+}
+
+static void
+test_unlink_siblings(void **state)
+{
+ const char *schema, *data;
+ struct lys_module *mod;
+ struct lyd_node *cont, *node, *first;
+
+ schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;revision 2014-05-08;"
+ "container cn {"
+ " leaf-list ll {type uint32;}"
+ " leaf-list tail {type string;}"
+ "}}";
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+
+ assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(cont, mod, "ll", "3", 0, NULL), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(cont, mod, "ll", "1", 0, NULL), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(cont, mod, "ll", "2", 0, NULL), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(cont, mod, "tail", "a", 0, NULL), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(cont, mod, "tail", "b", 0, NULL), LY_SUCCESS);
+
+ node = lyd_child(cont)->next;
+ lyd_unlink_siblings(node);
+ first = lyd_child(cont);
+ assert_true(first && first->meta && !first->next);
+ assert_string_equal(first->meta->name, META_NAME);
+ assert_string_equal(lyd_get_value(first), "1");
+ assert_true(node && !node->meta && node->next && node->next->next && node->next->next->next);
+ assert_string_equal(lyd_get_value(node), "2");
+ assert_string_equal(lyd_get_value(node->next), "3");
+
+ lyd_free_all(node);
+ lyd_free_all(cont);
+
+ /* call lyds_split but no metadata is set */
+ data = "{\"a:cn\": {\"ll\":[1,2,3,4]} }";
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, LYD_PARSE_ORDERED, LYD_VALIDATE_PRESENT, LY_SUCCESS, cont);
+ first = lyd_child(cont);
+ assert_true(!first->meta);
+ node = lyd_child(cont)->next;
+ lyd_unlink_siblings(node);
+ assert_true(node && !node->meta && node->next);
+ assert_string_equal(lyd_get_value(node), "2");
+ assert_string_equal(lyd_get_value(node->next), "3");
+ assert_string_equal(lyd_get_value(node->next->next), "4");
+ lyd_free_all(node);
+ lyd_free_all(cont);
+
+ /* call lyds_split, metadata is set, two items */
+ assert_int_equal(lyd_new_inner(NULL, mod, "cn", 0, &cont), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(cont, mod, "ll", "1", 0, NULL), LY_SUCCESS);
+ assert_int_equal(lyd_new_term(cont, mod, "ll", "2", 0, NULL), LY_SUCCESS);
+ first = lyd_child(cont);
+ assert_true(first->meta);
+ node = lyd_child(cont)->next;
+ lyd_unlink_siblings(node);
+ assert_true(node && !node->meta);
+ assert_string_equal(lyd_get_value(node), "2");
+ lyd_free_all(node);
+ lyd_free_all(cont);
+}
+
int
main(void)
{
@@ -1208,6 +1526,10 @@
UTEST(test_print_data),
UTEST(test_manipulation_of_many_nodes),
UTEST(test_lyds_free_metadata),
+ UTEST(test_move_whole_list),
+ UTEST(test_move_part_list),
+ UTEST(test_merge_whole_list),
+ UTEST(test_unlink_siblings),
};
return cmocka_run_group_tests(tests, NULL, NULL);