Coverage Report

Created: 2025-08-26 06:20

/src/libyang/src/tree_data_hash.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file tree_data_hash.c
3
 * @author Radek Krejci <rkrejci@cesnet.cz>
4
 * @brief Functions to manipulate with the data node's hashes.
5
 *
6
 * Copyright (c) 2019 CESNET, z.s.p.o.
7
 *
8
 * This source code is licensed under BSD 3-Clause License (the "License").
9
 * You may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     https://opensource.org/licenses/BSD-3-Clause
13
 */
14
15
#include <assert.h>
16
#include <stdint.h>
17
#include <stdlib.h>
18
#include <string.h>
19
20
#include "common.h"
21
#include "compat.h"
22
#include "hash_table.h"
23
#include "log.h"
24
#include "plugins_types.h"
25
#include "tree.h"
26
#include "tree_data.h"
27
#include "tree_schema.h"
28
29
LY_ERR
30
lyd_hash(struct lyd_node *node)
31
0
{
32
0
    struct lyd_node *iter;
33
0
    const void *hash_key;
34
0
    ly_bool dyn;
35
0
    size_t key_len;
36
37
0
    if (!node->schema) {
38
0
        return LY_SUCCESS;
39
0
    }
40
41
    /* hash always starts with the module and schema name */
42
0
    node->hash = dict_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
43
0
    node->hash = dict_hash_multi(node->hash, node->schema->name, strlen(node->schema->name));
44
45
0
    if (node->schema->nodetype == LYS_LIST) {
46
0
        if (node->schema->flags & LYS_KEYLESS) {
47
            /* key-less list simply adds its schema name again to the hash, just so that it differs from the first-instance hash */
48
0
            node->hash = dict_hash_multi(node->hash, node->schema->name, strlen(node->schema->name));
49
0
        } else {
50
0
            struct lyd_node_inner *list = (struct lyd_node_inner *)node;
51
52
            /* list hash is made up from its keys */
53
0
            for (iter = list->child; iter && (iter->schema->flags & LYS_KEY); iter = iter->next) {
54
0
                struct lyd_node_term *key = (struct lyd_node_term *)iter;
55
56
0
                hash_key = key->value.realtype->plugin->print(NULL, &key->value, LY_VALUE_LYB, NULL, &dyn, &key_len);
57
0
                node->hash = dict_hash_multi(node->hash, hash_key, key_len);
58
0
                if (dyn) {
59
0
                    free((void *)hash_key);
60
0
                }
61
0
            }
62
0
        }
63
0
    } else if (node->schema->nodetype == LYS_LEAFLIST) {
64
        /* leaf-list adds its hash key */
65
0
        struct lyd_node_term *llist = (struct lyd_node_term *)node;
66
67
0
        hash_key = llist->value.realtype->plugin->print(NULL, &llist->value, LY_VALUE_LYB, NULL, &dyn, &key_len);
68
0
        node->hash = dict_hash_multi(node->hash, hash_key, key_len);
69
0
        if (dyn) {
70
0
            free((void *)hash_key);
71
0
        }
72
0
    }
73
74
    /* finish the hash */
75
0
    node->hash = dict_hash_multi(node->hash, NULL, 0);
76
77
0
    return LY_SUCCESS;
78
0
}
79
80
/**
81
 * @brief Compare callback for values in hash table.
82
 *
83
 * Implementation of ::lyht_value_equal_cb.
84
 */
85
static ly_bool
86
lyd_hash_table_val_equal(void *val1_p, void *val2_p, ly_bool mod, void *UNUSED(cb_data))
87
0
{
88
0
    struct lyd_node *val1, *val2;
89
90
0
    val1 = *((struct lyd_node **)val1_p);
91
0
    val2 = *((struct lyd_node **)val2_p);
92
93
0
    if (mod) {
94
0
        if (val1 == val2) {
95
0
            return 1;
96
0
        } else {
97
0
            return 0;
98
0
        }
99
0
    }
100
101
0
    if (val1->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
102
        /* match on exact instance */
103
0
        if (!lyd_compare_single(val1, val2, 0)) {
104
0
            return 1;
105
0
        }
106
0
    } else if (val1->schema == val2->schema) {
107
        /* just schema match */
108
0
        return 1;
109
0
    }
110
0
    return 0;
111
0
}
112
113
/**
114
 * @brief Add single node into children hash table.
115
 *
116
 * @param[in] ht Children hash table.
117
 * @param[in] node Node to insert.
118
 * @param[in] empty_ht Whether we started with an empty HT meaning no nodes were inserted yet.
119
 * @return LY_ERR value.
120
 */
121
static LY_ERR
122
lyd_insert_hash_add(struct hash_table *ht, struct lyd_node *node, ly_bool empty_ht)
123
0
{
124
0
    uint32_t hash;
125
126
0
    assert(ht && node && node->schema);
127
128
    /* add node itself */
129
0
    if (lyht_insert(ht, &node, node->hash, NULL)) {
130
0
        LOGINT_RET(LYD_CTX(node));
131
0
    }
132
133
    /* add first instance of a (leaf-)list */
134
0
    if ((node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) &&
135
0
            (!node->prev->next || (node->prev->schema != node->schema))) {
136
        /* get the simple hash */
137
0
        hash = dict_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
138
0
        hash = dict_hash_multi(hash, node->schema->name, strlen(node->schema->name));
139
0
        hash = dict_hash_multi(hash, NULL, 0);
140
141
        /* remove any previous stored instance, only if we did not start with an empty HT */
142
0
        if (!empty_ht && node->next && (node->next->schema == node->schema)) {
143
0
            if (lyht_remove(ht, &node->next, hash)) {
144
0
                LOGINT_RET(LYD_CTX(node));
145
0
            }
146
0
        }
147
148
        /* in this case there would be the exact same value twice in the hash table, not supported (by the HT) */
149
0
        assert(hash != node->hash);
150
151
        /* insert this instance as the first (leaf-)list instance */
152
0
        if (lyht_insert(ht, &node, hash, NULL)) {
153
0
            LOGINT_RET(LYD_CTX(node));
154
0
        }
155
0
    }
156
157
0
    return LY_SUCCESS;
158
0
}
159
160
LY_ERR
161
lyd_insert_hash(struct lyd_node *node)
162
0
{
163
0
    struct lyd_node *iter;
164
0
    uint32_t u;
165
166
0
    if (!node->parent || !node->schema || !node->parent->schema) {
167
        /* nothing to do */
168
0
        return LY_SUCCESS;
169
0
    }
170
171
    /* create parent hash table if required, otherwise just add the new child */
172
0
    if (!node->parent->children_ht) {
173
        /* the hash table is created only when the number of children in a node exceeds the
174
         * defined minimal limit LYD_HT_MIN_ITEMS
175
         */
176
0
        u = 0;
177
0
        LY_LIST_FOR(node->parent->child, iter) {
178
0
            if (iter->schema) {
179
0
                ++u;
180
0
            }
181
0
        }
182
0
        if (u >= LYD_HT_MIN_ITEMS) {
183
            /* create hash table, insert all the children */
184
0
            node->parent->children_ht = lyht_new(1, sizeof(struct lyd_node *), lyd_hash_table_val_equal, NULL, 1);
185
0
            LY_LIST_FOR(node->parent->child, iter) {
186
0
                if (iter->schema) {
187
0
                    LY_CHECK_RET(lyd_insert_hash_add(node->parent->children_ht, iter, 1));
188
0
                }
189
0
            }
190
0
        }
191
0
    } else {
192
0
        LY_CHECK_RET(lyd_insert_hash_add(node->parent->children_ht, node, 0));
193
0
    }
194
195
0
    return LY_SUCCESS;
196
0
}
197
198
void
199
lyd_unlink_hash(struct lyd_node *node)
200
0
{
201
0
    uint32_t hash;
202
203
0
    if (!node->parent || !node->schema || !node->parent->schema || !node->parent->children_ht) {
204
        /* not in any HT */
205
0
        return;
206
0
    }
207
208
    /* remove from the parent HT */
209
0
    if (lyht_remove(node->parent->children_ht, &node, node->hash)) {
210
0
        LOGINT(LYD_CTX(node));
211
0
        return;
212
0
    }
213
214
    /* first instance of the (leaf-)list, needs to be removed from HT */
215
0
    if ((node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) && (!node->prev->next || (node->prev->schema != node->schema))) {
216
        /* get the simple hash */
217
0
        hash = dict_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
218
0
        hash = dict_hash_multi(hash, node->schema->name, strlen(node->schema->name));
219
0
        hash = dict_hash_multi(hash, NULL, 0);
220
221
        /* remove the instance */
222
0
        if (lyht_remove(node->parent->children_ht, &node, hash)) {
223
0
            LOGINT(LYD_CTX(node));
224
0
            return;
225
0
        }
226
227
        /* add the next instance */
228
0
        if (node->next && (node->next->schema == node->schema)) {
229
0
            if (lyht_insert(node->parent->children_ht, &node->next, hash, NULL)) {
230
0
                LOGINT(LYD_CTX(node));
231
0
                return;
232
0
            }
233
0
        }
234
0
    }
235
0
}