blob: a27f5143ed85d1bfeb3d39271247d1198a067da6 [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
Radek Krejci1f05b6a2019-07-18 16:15:06 +020028LY_ERR
29lyd_hash(struct lyd_node *node)
30{
31 struct lyd_node *iter;
32
Michal Vasko52927e22020-03-16 17:26:14 +010033 if (!node->schema) {
34 return LY_SUCCESS;
35 }
36
Michal Vaskoe78faec2021-04-08 17:24:43 +020037 /* hash always starts with the module and schema name */
Radek Krejci1f05b6a2019-07-18 16:15:06 +020038 node->hash = dict_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
39 node->hash = dict_hash_multi(node->hash, node->schema->name, strlen(node->schema->name));
40
41 if (node->schema->nodetype == LYS_LIST) {
Michal Vaskoe78faec2021-04-08 17:24:43 +020042 if (node->schema->flags & LYS_KEYLESS) {
43 /* key-less list simply adds its schema name again to the hash, just so that it differs from the first-instance hash */
44 node->hash = dict_hash_multi(node->hash, node->schema->name, strlen(node->schema->name));
45 } else {
46 struct lyd_node_inner *list = (struct lyd_node_inner *)node;
47
48 /* list hash is made up from its keys */
Michal Vaskoba99a3e2020-08-18 15:50:05 +020049 for (iter = list->child; iter && (iter->schema->flags & LYS_KEY); iter = iter->next) {
Michal Vaskob7be7a82020-08-20 09:09:04 +020050 const char *value = LYD_CANON_VALUE(iter);
Radek Krejci1f05b6a2019-07-18 16:15:06 +020051 node->hash = dict_hash_multi(node->hash, value, strlen(value));
Radek Krejci1f05b6a2019-07-18 16:15:06 +020052 }
Radek Krejci1f05b6a2019-07-18 16:15:06 +020053 }
54 } else if (node->schema->nodetype == LYS_LEAFLIST) {
Michal Vaskoe78faec2021-04-08 17:24:43 +020055 /* leaf-list adds its value */
Michal Vaskob7be7a82020-08-20 09:09:04 +020056 const char *value = LYD_CANON_VALUE(node);
Radek Krejci1f05b6a2019-07-18 16:15:06 +020057 node->hash = dict_hash_multi(node->hash, value, strlen(value));
Radek Krejci1f05b6a2019-07-18 16:15:06 +020058 }
Michal Vaskoe78faec2021-04-08 17:24:43 +020059
Radek Krejci1f05b6a2019-07-18 16:15:06 +020060 /* finish the hash */
61 node->hash = dict_hash_multi(node->hash, NULL, 0);
62
63 return LY_SUCCESS;
64}
65
Radek Krejci857189e2020-09-01 13:26:36 +020066/**
67 * @brief Compare callback for values in hash table.
68 *
Michal Vasko62524a92021-02-26 10:08:50 +010069 * Implementation of ::lyht_value_equal_cb.
Radek Krejci857189e2020-09-01 13:26:36 +020070 */
71static ly_bool
72lyd_hash_table_val_equal(void *val1_p, void *val2_p, ly_bool mod, void *UNUSED(cb_data))
Radek Krejci1f05b6a2019-07-18 16:15:06 +020073{
74 struct lyd_node *val1, *val2;
75
76 val1 = *((struct lyd_node **)val1_p);
77 val2 = *((struct lyd_node **)val2_p);
78
79 if (mod) {
80 if (val1 == val2) {
81 return 1;
82 } else {
83 return 0;
84 }
85 }
86
Michal Vaskoda859032020-07-14 12:20:14 +020087 if (val1->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
88 /* match on exact instance */
Michal Vasko8f359bf2020-07-28 10:41:15 +020089 if (!lyd_compare_single(val1, val2, 0)) {
Michal Vaskoda859032020-07-14 12:20:14 +020090 return 1;
91 }
92 } else if (val1->schema == val2->schema) {
93 /* just schema match */
Radek Krejci1f05b6a2019-07-18 16:15:06 +020094 return 1;
Radek Krejci1f05b6a2019-07-18 16:15:06 +020095 }
Michal Vaskoda859032020-07-14 12:20:14 +020096 return 0;
Radek Krejci1f05b6a2019-07-18 16:15:06 +020097}
98
Michal Vaskob104f112020-07-17 09:54:54 +020099/**
100 * @brief Add single node into children hash table.
101 *
102 * @param[in] ht Children hash table.
103 * @param[in] node Node to insert.
104 * @param[in] empty_ht Whether we started with an empty HT meaning no nodes were inserted yet.
105 * @return LY_ERR value.
106 */
107static LY_ERR
Radek Krejci857189e2020-09-01 13:26:36 +0200108lyd_insert_hash_add(struct hash_table *ht, struct lyd_node *node, ly_bool empty_ht)
Michal Vaskob104f112020-07-17 09:54:54 +0200109{
110 uint32_t hash;
111
112 assert(ht && node && node->schema);
113
114 /* add node itself */
115 if (lyht_insert(ht, &node, node->hash, NULL)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200116 LOGINT_RET(LYD_CTX(node));
Michal Vaskob104f112020-07-17 09:54:54 +0200117 }
118
119 /* add first instance of a (leaf-)list */
Michal Vasko69730152020-10-09 16:30:07 +0200120 if ((node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) &&
121 (!node->prev->next || (node->prev->schema != node->schema))) {
Michal Vaskob104f112020-07-17 09:54:54 +0200122 /* get the simple hash */
123 hash = dict_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
124 hash = dict_hash_multi(hash, node->schema->name, strlen(node->schema->name));
125 hash = dict_hash_multi(hash, NULL, 0);
126
127 /* remove any previous stored instance, only if we did not start with an empty HT */
128 if (!empty_ht && node->next && (node->next->schema == node->schema)) {
129 if (lyht_remove(ht, &node->next, hash)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200130 LOGINT_RET(LYD_CTX(node));
Michal Vaskob104f112020-07-17 09:54:54 +0200131 }
132 }
133
Michal Vaskoe78faec2021-04-08 17:24:43 +0200134 /* in this case there would be the exact same value twice in the hash table, not supported (by the HT) */
135 assert(hash != node->hash);
Michal Vasko47718292021-02-26 10:12:44 +0100136
Michal Vaskob104f112020-07-17 09:54:54 +0200137 /* insert this instance as the first (leaf-)list instance */
138 if (lyht_insert(ht, &node, hash, NULL)) {
Michal Vaskoe78faec2021-04-08 17:24:43 +0200139 LOGINT_RET(LYD_CTX(node));
Michal Vaskob104f112020-07-17 09:54:54 +0200140 }
141 }
142
Michal Vaskoe78faec2021-04-08 17:24:43 +0200143 return LY_SUCCESS;
Michal Vaskob104f112020-07-17 09:54:54 +0200144}
145
Radek Krejci1f05b6a2019-07-18 16:15:06 +0200146LY_ERR
147lyd_insert_hash(struct lyd_node *node)
148{
149 struct lyd_node *iter;
Michal Vaskob104f112020-07-17 09:54:54 +0200150 uint32_t u;
Radek Krejci1f05b6a2019-07-18 16:15:06 +0200151
Michal Vasko52927e22020-03-16 17:26:14 +0100152 if (!node->parent || !node->schema || !node->parent->schema) {
Radek Krejci1f05b6a2019-07-18 16:15:06 +0200153 /* nothing to do */
154 return LY_SUCCESS;
155 }
156
157 /* create parent hash table if required, otherwise just add the new child */
158 if (!node->parent->children_ht) {
Radek Krejci1f05b6a2019-07-18 16:15:06 +0200159 /* the hash table is created only when the number of children in a node exceeds the
Michal Vasko60ea6352020-06-29 13:39:39 +0200160 * defined minimal limit LYD_HT_MIN_ITEMS
Radek Krejci1f05b6a2019-07-18 16:15:06 +0200161 */
Michal Vaskob104f112020-07-17 09:54:54 +0200162 u = 0;
163 LY_LIST_FOR(node->parent->child, iter) {
164 if (iter->schema) {
165 ++u;
166 }
167 }
Radek Krejci1f05b6a2019-07-18 16:15:06 +0200168 if (u >= LYD_HT_MIN_ITEMS) {
169 /* create hash table, insert all the children */
170 node->parent->children_ht = lyht_new(1, sizeof(struct lyd_node *), lyd_hash_table_val_equal, NULL, 1);
171 LY_LIST_FOR(node->parent->child, iter) {
Michal Vaskob104f112020-07-17 09:54:54 +0200172 if (iter->schema) {
173 LY_CHECK_RET(lyd_insert_hash_add(node->parent->children_ht, iter, 1));
Radek Krejci1f05b6a2019-07-18 16:15:06 +0200174 }
175 }
176 }
177 } else {
Michal Vaskob104f112020-07-17 09:54:54 +0200178 LY_CHECK_RET(lyd_insert_hash_add(node->parent->children_ht, node, 0));
Radek Krejci1f05b6a2019-07-18 16:15:06 +0200179 }
180
181 return LY_SUCCESS;
182}
Radek Krejcie7b95092019-05-15 11:03:07 +0200183
184void
185lyd_unlink_hash(struct lyd_node *node)
186{
Michal Vaskob104f112020-07-17 09:54:54 +0200187 uint32_t hash;
188
189 if (!node->parent || !node->schema || !node->parent->schema || !node->parent->children_ht) {
190 /* not in any HT */
191 return;
192 }
193
194 /* remove from the parent HT */
195 if (lyht_remove(node->parent->children_ht, &node, node->hash)) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200196 LOGINT(LYD_CTX(node));
Michal Vaskob104f112020-07-17 09:54:54 +0200197 return;
198 }
199
200 /* first instance of the (leaf-)list, needs to be removed from HT */
201 if ((node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) && (!node->prev->next || (node->prev->schema != node->schema))) {
202 /* get the simple hash */
203 hash = dict_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
204 hash = dict_hash_multi(hash, node->schema->name, strlen(node->schema->name));
205 hash = dict_hash_multi(hash, NULL, 0);
206
207 /* remove the instance */
208 if (lyht_remove(node->parent->children_ht, &node, hash)) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200209 LOGINT(LYD_CTX(node));
Michal Vaskob104f112020-07-17 09:54:54 +0200210 return;
211 }
212
213 /* add the next instance */
214 if (node->next && (node->next->schema == node->schema)) {
215 if (lyht_insert(node->parent->children_ht, &node->next, hash, NULL)) {
Michal Vaskob7be7a82020-08-20 09:09:04 +0200216 LOGINT(LYD_CTX(node));
Michal Vaskob104f112020-07-17 09:54:54 +0200217 return;
218 }
219 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200220 }
221}