blob: 39c5fea542a1f4f71095428a305834335da1962d [file] [log] [blame]
Radek Krejcie7b95092019-05-15 11:03:07 +02001/**
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 */
Radek Krejci535ea9f2020-05-29 16:01:05 +020014
Michal Vaskob104f112020-07-17 09:54:54 +020015#include <assert.h>
Radek Krejci535ea9f2020-05-29 16:01:05 +020016#include <stdint.h>
17#include <stdlib.h>
18#include <string.h>
19
Radek Krejcie7b95092019-05-15 11:03:07 +020020#include "common.h"
Michal Vasko69730152020-10-09 16:30:07 +020021#include "compat.h"
Radek Krejcie7b95092019-05-15 11:03:07 +020022#include "hash_table.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020023#include "log.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020024#include "tree.h"
25#include "tree_data.h"
26#include "tree_schema.h"
Radek Krejci1f05b6a2019-07-18 16:15:06 +020027
28static void
Christian Hopps59618972021-02-01 05:01:35 -050029lyd_hash_keyless_list_dfs(const struct lyd_node *child, uint32_t *hash)
Radek Krejci1f05b6a2019-07-18 16:15:06 +020030{
Christian Hopps59618972021-02-01 05:01:35 -050031 const struct lyd_node *iter;
Radek Krejci1f05b6a2019-07-18 16:15:06 +020032
33 LY_LIST_FOR(child, iter) {
34 switch (iter->schema->nodetype) {
35 case LYS_CONTAINER:
36 case LYS_LIST:
Michal Vasko9e685082021-01-29 14:49:09 +010037 lyd_hash_keyless_list_dfs(lyd_child(iter), hash);
Radek Krejci1f05b6a2019-07-18 16:15:06 +020038 break;
39 case LYS_LEAFLIST:
40 case LYS_ANYXML:
41 case LYS_ANYDATA:
42 case LYS_LEAF:
43 *hash = dict_hash_multi(*hash, (char *)&iter->hash, sizeof iter->hash);
44 break;
45 default:
46 LOGINT(NULL);
47 }
48 }
49}
50
51LY_ERR
52lyd_hash(struct lyd_node *node)
53{
54 struct lyd_node *iter;
55
Michal Vasko52927e22020-03-16 17:26:14 +010056 if (!node->schema) {
57 return LY_SUCCESS;
58 }
59
Radek Krejci1f05b6a2019-07-18 16:15:06 +020060 node->hash = dict_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
61 node->hash = dict_hash_multi(node->hash, node->schema->name, strlen(node->schema->name));
62
63 if (node->schema->nodetype == LYS_LIST) {
Michal Vasko22df3f02020-08-24 13:29:22 +020064 struct lyd_node_inner *list = (struct lyd_node_inner *)node;
Radek Krejci0fe9b512019-07-26 17:51:05 +020065 if (!(node->schema->flags & LYS_KEYLESS)) {
Radek Krejci1f05b6a2019-07-18 16:15:06 +020066 /* list's hash is made of its keys */
Michal Vaskoba99a3e2020-08-18 15:50:05 +020067 for (iter = list->child; iter && (iter->schema->flags & LYS_KEY); iter = iter->next) {
Michal Vaskob7be7a82020-08-20 09:09:04 +020068 const char *value = LYD_CANON_VALUE(iter);
Radek Krejci1f05b6a2019-07-18 16:15:06 +020069 node->hash = dict_hash_multi(node->hash, value, strlen(value));
Radek Krejci1f05b6a2019-07-18 16:15:06 +020070 }
71 } else {
72 /* keyless status list */
73 lyd_hash_keyless_list_dfs(list->child, &node->hash);
74 }
75 } else if (node->schema->nodetype == LYS_LEAFLIST) {
Michal Vaskob7be7a82020-08-20 09:09:04 +020076 const char *value = LYD_CANON_VALUE(node);
Radek Krejci1f05b6a2019-07-18 16:15:06 +020077 node->hash = dict_hash_multi(node->hash, value, strlen(value));
Radek Krejci1f05b6a2019-07-18 16:15:06 +020078 }
79 /* finish the hash */
80 node->hash = dict_hash_multi(node->hash, NULL, 0);
81
82 return LY_SUCCESS;
83}
84
Radek Krejci857189e2020-09-01 13:26:36 +020085/**
86 * @brief Compare callback for values in hash table.
87 *
Michal Vasko62524a92021-02-26 10:08:50 +010088 * Implementation of ::lyht_value_equal_cb.
Radek Krejci857189e2020-09-01 13:26:36 +020089 */
90static ly_bool
91lyd_hash_table_val_equal(void *val1_p, void *val2_p, ly_bool mod, void *UNUSED(cb_data))
Radek Krejci1f05b6a2019-07-18 16:15:06 +020092{
93 struct lyd_node *val1, *val2;
94
95 val1 = *((struct lyd_node **)val1_p);
96 val2 = *((struct lyd_node **)val2_p);
97
98 if (mod) {
99 if (val1 == val2) {
100 return 1;
101 } else {
102 return 0;
103 }
104 }
105
Michal Vaskoda859032020-07-14 12:20:14 +0200106 if (val1->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
107 /* match on exact instance */
Michal Vasko8f359bf2020-07-28 10:41:15 +0200108 if (!lyd_compare_single(val1, val2, 0)) {
Michal Vaskoda859032020-07-14 12:20:14 +0200109 return 1;
110 }
111 } else if (val1->schema == val2->schema) {
112 /* just schema match */
Radek Krejci1f05b6a2019-07-18 16:15:06 +0200113 return 1;
Radek Krejci1f05b6a2019-07-18 16:15:06 +0200114 }
Michal Vaskoda859032020-07-14 12:20:14 +0200115 return 0;
Radek Krejci1f05b6a2019-07-18 16:15:06 +0200116}
117
Michal Vaskob104f112020-07-17 09:54:54 +0200118/**
Michal Vasko47718292021-02-26 10:12:44 +0100119 * @brief Comparison callback for hash table that never considers 2 values equal.
120 *
121 * Implementation of ::lyht_value_equal_cb.
122 */
123static ly_bool
124lyd_not_equal_value_cb(void *UNUSED(val1_p), void *UNUSED(val2_p), ly_bool UNUSED(mod), void *UNUSED(cb_data))
125{
126 return 0;
127}
128
129/**
Michal Vaskob104f112020-07-17 09:54:54 +0200130 * @brief Add single node into children hash table.
131 *
132 * @param[in] ht Children hash table.
133 * @param[in] node Node to insert.
134 * @param[in] empty_ht Whether we started with an empty HT meaning no nodes were inserted yet.
135 * @return LY_ERR value.
136 */
137static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200138lyd_insert_hash_add(struct hash_table *ht, struct lyd_node *node, ly_bool empty_ht)
Michal Vaskob104f112020-07-17 09:54:54 +0200139{
Michal Vasko47718292021-02-26 10:12:44 +0100140 LY_ERR rc = LY_SUCCESS;
141 lyht_value_equal_cb orig_cb = NULL;
Michal Vaskob104f112020-07-17 09:54:54 +0200142 uint32_t hash;
143
144 assert(ht && node && node->schema);
145
146 /* add node itself */
147 if (lyht_insert(ht, &node, node->hash, NULL)) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200148 LOGINT(LYD_CTX(node));
Michal Vasko47718292021-02-26 10:12:44 +0100149 rc = LY_EINT;
150 goto cleanup;
Michal Vaskob104f112020-07-17 09:54:54 +0200151 }
152
153 /* add first instance of a (leaf-)list */
Michal Vasko69730152020-10-09 16:30:07 +0200154 if ((node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) &&
155 (!node->prev->next || (node->prev->schema != node->schema))) {
Michal Vaskob104f112020-07-17 09:54:54 +0200156 /* get the simple hash */
157 hash = dict_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
158 hash = dict_hash_multi(hash, node->schema->name, strlen(node->schema->name));
159 hash = dict_hash_multi(hash, NULL, 0);
160
161 /* remove any previous stored instance, only if we did not start with an empty HT */
162 if (!empty_ht && node->next && (node->next->schema == node->schema)) {
163 if (lyht_remove(ht, &node->next, hash)) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200164 LOGINT(LYD_CTX(node));
Michal Vasko47718292021-02-26 10:12:44 +0100165 rc = LY_EINT;
166 goto cleanup;
Michal Vaskob104f112020-07-17 09:54:54 +0200167 }
168 }
169
Michal Vasko47718292021-02-26 10:12:44 +0100170 if (hash == node->hash) {
171 /* special case, key-less list with no children hash is equal to the first instance list hash */
172 assert((node->schema->nodetype == LYS_LIST) && (node->schema->flags & LYS_KEYLESS) && !lyd_child(node));
173
174 /* we must allow exact duplicates in the hash table */
175 orig_cb = lyht_set_cb(ht, lyd_not_equal_value_cb);
176 }
177
Michal Vaskob104f112020-07-17 09:54:54 +0200178 /* insert this instance as the first (leaf-)list instance */
179 if (lyht_insert(ht, &node, hash, NULL)) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200180 LOGINT(LYD_CTX(node));
Michal Vasko47718292021-02-26 10:12:44 +0100181 rc = LY_EINT;
182 goto cleanup;
Michal Vaskob104f112020-07-17 09:54:54 +0200183 }
184 }
185
Michal Vasko47718292021-02-26 10:12:44 +0100186cleanup:
187 if (orig_cb) {
188 lyht_set_cb(ht, orig_cb);
189 }
190 return rc;
Michal Vaskob104f112020-07-17 09:54:54 +0200191}
192
Radek Krejci1f05b6a2019-07-18 16:15:06 +0200193LY_ERR
194lyd_insert_hash(struct lyd_node *node)
195{
196 struct lyd_node *iter;
Michal Vaskob104f112020-07-17 09:54:54 +0200197 uint32_t u;
Radek Krejci1f05b6a2019-07-18 16:15:06 +0200198
Michal Vasko52927e22020-03-16 17:26:14 +0100199 if (!node->parent || !node->schema || !node->parent->schema) {
Radek Krejci1f05b6a2019-07-18 16:15:06 +0200200 /* nothing to do */
201 return LY_SUCCESS;
202 }
203
204 /* create parent hash table if required, otherwise just add the new child */
205 if (!node->parent->children_ht) {
Radek Krejci1f05b6a2019-07-18 16:15:06 +0200206 /* the hash table is created only when the number of children in a node exceeds the
Michal Vasko60ea6352020-06-29 13:39:39 +0200207 * defined minimal limit LYD_HT_MIN_ITEMS
Radek Krejci1f05b6a2019-07-18 16:15:06 +0200208 */
Michal Vaskob104f112020-07-17 09:54:54 +0200209 u = 0;
210 LY_LIST_FOR(node->parent->child, iter) {
211 if (iter->schema) {
212 ++u;
213 }
214 }
Radek Krejci1f05b6a2019-07-18 16:15:06 +0200215 if (u >= LYD_HT_MIN_ITEMS) {
216 /* create hash table, insert all the children */
217 node->parent->children_ht = lyht_new(1, sizeof(struct lyd_node *), lyd_hash_table_val_equal, NULL, 1);
218 LY_LIST_FOR(node->parent->child, iter) {
Michal Vaskob104f112020-07-17 09:54:54 +0200219 if (iter->schema) {
220 LY_CHECK_RET(lyd_insert_hash_add(node->parent->children_ht, iter, 1));
Radek Krejci1f05b6a2019-07-18 16:15:06 +0200221 }
222 }
223 }
224 } else {
Michal Vaskob104f112020-07-17 09:54:54 +0200225 LY_CHECK_RET(lyd_insert_hash_add(node->parent->children_ht, node, 0));
Radek Krejci1f05b6a2019-07-18 16:15:06 +0200226 }
227
228 return LY_SUCCESS;
229}
Radek Krejcie7b95092019-05-15 11:03:07 +0200230
231void
232lyd_unlink_hash(struct lyd_node *node)
233{
Michal Vaskob104f112020-07-17 09:54:54 +0200234 uint32_t hash;
235
236 if (!node->parent || !node->schema || !node->parent->schema || !node->parent->children_ht) {
237 /* not in any HT */
238 return;
239 }
240
241 /* remove from the parent HT */
242 if (lyht_remove(node->parent->children_ht, &node, node->hash)) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200243 LOGINT(LYD_CTX(node));
Michal Vaskob104f112020-07-17 09:54:54 +0200244 return;
245 }
246
247 /* first instance of the (leaf-)list, needs to be removed from HT */
248 if ((node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) && (!node->prev->next || (node->prev->schema != node->schema))) {
249 /* get the simple hash */
250 hash = dict_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
251 hash = dict_hash_multi(hash, node->schema->name, strlen(node->schema->name));
252 hash = dict_hash_multi(hash, NULL, 0);
253
254 /* remove the instance */
255 if (lyht_remove(node->parent->children_ht, &node, hash)) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200256 LOGINT(LYD_CTX(node));
Michal Vaskob104f112020-07-17 09:54:54 +0200257 return;
258 }
259
260 /* add the next instance */
261 if (node->next && (node->next->schema == node->schema)) {
262 if (lyht_insert(node->parent->children_ht, &node->next, hash, NULL)) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200263 LOGINT(LYD_CTX(node));
Michal Vaskob104f112020-07-17 09:54:54 +0200264 return;
265 }
266 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200267 }
268}