tree data UPDATE xpath trim function
Refs #2148
diff --git a/src/tree_data.c b/src/tree_data.c
index 5676da8..809a257 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -3015,6 +3015,139 @@
return ret;
}
+/**
+ * @brief Hash table node equal callback.
+ */
+static ly_bool
+lyd_trim_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data))
+{
+ struct lyd_node *node1, *node2;
+
+ node1 = *(struct lyd_node **)val1_p;
+ node2 = *(struct lyd_node **)val2_p;
+
+ return node1 == node2;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_trim_xpath(struct lyd_node **tree, const char *xpath, const struct lyxp_var *vars)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct ly_ctx *ctx;
+ struct lyxp_set xp_set = {0};
+ struct lyxp_expr *exp = NULL;
+ struct lyd_node *node, *parent;
+ struct lyxp_set_hash_node hnode;
+ struct ly_ht *parent_ht = NULL;
+ struct ly_set free_set = {0};
+ uint32_t i, hash;
+ ly_bool is_result;
+
+ LY_CHECK_ARG_RET(NULL, tree, xpath, LY_EINVAL);
+
+ if (!*tree) {
+ /* nothing to do */
+ goto cleanup;
+ }
+
+ *tree = lyd_first_sibling(*tree);
+ ctx = (struct ly_ctx *)LYD_CTX(*tree);
+
+ /* parse expression */
+ ret = lyxp_expr_parse(ctx, xpath, 0, 1, &exp);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* evaluate expression */
+ ret = lyxp_eval(ctx, exp, NULL, LY_VALUE_JSON, NULL, *tree, *tree, *tree, vars, &xp_set, LYXP_IGNORE_WHEN);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* create hash table for all the parents of results */
+ parent_ht = lyht_new(32, sizeof *node, lyd_trim_equal_cb, NULL, 1);
+ LY_CHECK_GOTO(!parent_ht, cleanup);
+
+ for (i = 0; i < xp_set.used; ++i) {
+ if (xp_set.val.nodes[i].type != LYXP_NODE_ELEM) {
+ /* ignore */
+ continue;
+ }
+
+ for (parent = lyd_parent(xp_set.val.nodes[i].node); parent; parent = lyd_parent(parent)) {
+ /* add the parent into parent_ht */
+ ret = lyht_insert(parent_ht, &parent, parent->hash, NULL);
+ if (ret == LY_EEXIST) {
+ /* shared parent, we are done */
+ break;
+ }
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+ hnode.type = LYXP_NODE_ELEM;
+ LY_LIST_FOR(*tree, parent) {
+ LYD_TREE_DFS_BEGIN(parent, node) {
+ if (lysc_is_key(node->schema)) {
+ /* ignore */
+ goto next_iter;
+ }
+
+ /* check the results */
+ is_result = 0;
+ if (xp_set.ht) {
+ hnode.node = node;
+ hash = lyht_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
+ hash = lyht_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
+ hash = lyht_hash_multi(hash, NULL, 0);
+
+ if (!lyht_find(xp_set.ht, &hnode, hash, NULL)) {
+ is_result = 1;
+ }
+ } else {
+ /* not enough elements for a hash table */
+ for (i = 0; i < xp_set.used; ++i) {
+ if (xp_set.val.nodes[i].type != LYXP_NODE_ELEM) {
+ /* ignore */
+ continue;
+ }
+
+ if (xp_set.val.nodes[i].node == node) {
+ is_result = 1;
+ break;
+ }
+ }
+ }
+
+ if (is_result) {
+ /* keep the whole subtree if the node is in the results */
+ LYD_TREE_DFS_continue = 1;
+ } else if (lyht_find(parent_ht, &node, node->hash, NULL)) {
+ /* free the whole subtree if the node is not even among the selected parents */
+ ret = ly_set_add(&free_set, node, 1, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ LYD_TREE_DFS_continue = 1;
+ } /* else keep the parent node because a subtree is in the results */
+
+next_iter:
+ LYD_TREE_DFS_END(parent, node);
+ }
+ }
+
+ /* free */
+ for (i = 0; i < free_set.count; ++i) {
+ node = free_set.dnodes[i];
+ if (*tree == node) {
+ *tree = (*tree)->next;
+ }
+ lyd_free_tree(node);
+ }
+
+cleanup:
+ lyxp_set_free_content(&xp_set);
+ lyxp_expr_free(ctx, exp);
+ lyht_free(parent_ht, NULL);
+ ly_set_erase(&free_set, NULL);
+ return ret;
+}
+
LIBYANG_API_DEF LY_ERR
lyd_find_path(const struct lyd_node *ctx_node, const char *path, ly_bool output, struct lyd_node **match)
{