/src/libyang/src/printer_lyb.c
Line | Count | Source |
1 | | /** |
2 | | * @file printer_lyb.c |
3 | | * @author Michal Vasko <mvasko@cesnet.cz> |
4 | | * @brief LYB printer for libyang data structure |
5 | | * |
6 | | * Copyright (c) 2020 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 "lyb.h" |
16 | | |
17 | | #include <assert.h> |
18 | | #include <stdint.h> |
19 | | #include <stdio.h> |
20 | | #include <stdlib.h> |
21 | | #include <string.h> |
22 | | |
23 | | #include "common.h" |
24 | | #include "compat.h" |
25 | | #include "context.h" |
26 | | #include "hash_table.h" |
27 | | #include "log.h" |
28 | | #include "out.h" |
29 | | #include "out_internal.h" |
30 | | #include "printer_data.h" |
31 | | #include "printer_internal.h" |
32 | | #include "set.h" |
33 | | #include "tree.h" |
34 | | #include "tree_data.h" |
35 | | #include "tree_data_internal.h" |
36 | | #include "tree_edit.h" |
37 | | #include "tree_schema.h" |
38 | | #include "tree_schema_internal.h" |
39 | | #include "xml.h" |
40 | | |
41 | | /** |
42 | | * @brief Hash table equal callback for checking hash equality only. |
43 | | * |
44 | | * Implementation of ::lyht_value_equal_cb. |
45 | | */ |
46 | | static ly_bool |
47 | | lyb_hash_equal_cb(void *UNUSED(val1_p), void *UNUSED(val2_p), ly_bool UNUSED(mod), void *UNUSED(cb_data)) |
48 | 0 | { |
49 | | /* for this purpose, if hash matches, the value does also, we do not want 2 values to have the same hash */ |
50 | 0 | return 1; |
51 | 0 | } |
52 | | |
53 | | /** |
54 | | * @brief Hash table equal callback for checking value pointer equality only. |
55 | | * |
56 | | * Implementation of ::lyht_value_equal_cb. |
57 | | */ |
58 | | static ly_bool |
59 | | lyb_ptr_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data)) |
60 | 0 | { |
61 | 0 | struct lysc_node *val1 = *(struct lysc_node **)val1_p; |
62 | 0 | struct lysc_node *val2 = *(struct lysc_node **)val2_p; |
63 | |
|
64 | 0 | if (val1 == val2) { |
65 | 0 | return 1; |
66 | 0 | } |
67 | 0 | return 0; |
68 | 0 | } |
69 | | |
70 | | /** |
71 | | * @brief Check that sibling collision hash is safe to insert into hash table. |
72 | | * |
73 | | * @param[in] ht Hash table. |
74 | | * @param[in] sibling Hashed sibling. |
75 | | * @param[in] ht_col_id Sibling hash collision ID. |
76 | | * @param[in] compare_col_id Last collision ID to compare with. |
77 | | * @return LY_SUCCESS when the whole hash sequence does not collide, |
78 | | * @return LY_EEXIST when the whole hash sequence sollides. |
79 | | */ |
80 | | static LY_ERR |
81 | | lyb_hash_sequence_check(struct hash_table *ht, struct lysc_node *sibling, LYB_HASH ht_col_id, LYB_HASH compare_col_id) |
82 | 0 | { |
83 | 0 | struct lysc_node **col_node; |
84 | | |
85 | | /* get the first node inserted with last hash col ID ht_col_id */ |
86 | 0 | if (lyht_find(ht, &sibling, lyb_get_hash(sibling, ht_col_id), (void **)&col_node)) { |
87 | | /* there is none. valid situation */ |
88 | 0 | return LY_SUCCESS; |
89 | 0 | } |
90 | | |
91 | 0 | lyht_set_cb(ht, lyb_ptr_equal_cb); |
92 | 0 | do { |
93 | 0 | int64_t j; |
94 | 0 | for (j = (int64_t)compare_col_id; j > -1; --j) { |
95 | 0 | if (lyb_get_hash(sibling, j) != lyb_get_hash(*col_node, j)) { |
96 | | /* one non-colliding hash */ |
97 | 0 | break; |
98 | 0 | } |
99 | 0 | } |
100 | 0 | if (j == -1) { |
101 | | /* all whole hash sequences of nodes inserted with last hash col ID compare_col_id collide */ |
102 | 0 | lyht_set_cb(ht, lyb_hash_equal_cb); |
103 | 0 | return LY_EEXIST; |
104 | 0 | } |
105 | | |
106 | | /* get next node inserted with last hash col ID ht_col_id */ |
107 | 0 | } while (!lyht_find_next(ht, col_node, lyb_get_hash(*col_node, ht_col_id), (void **)&col_node)); |
108 | | |
109 | 0 | lyht_set_cb(ht, lyb_hash_equal_cb); |
110 | 0 | return LY_SUCCESS; |
111 | 0 | } |
112 | | |
113 | | /** |
114 | | * @brief Hash all the siblings and add them also into a separate hash table. |
115 | | * |
116 | | * @param[in] sibling Any sibling in all the siblings on one level. |
117 | | * @param[out] ht_p Created hash table. |
118 | | * @return LY_ERR value. |
119 | | */ |
120 | | static LY_ERR |
121 | | lyb_hash_siblings(struct lysc_node *sibling, struct hash_table **ht_p) |
122 | 0 | { |
123 | 0 | struct hash_table *ht; |
124 | 0 | const struct lysc_node *parent; |
125 | 0 | const struct lys_module *mod; |
126 | 0 | LYB_HASH i; |
127 | 0 | uint32_t getnext_opts; |
128 | |
|
129 | 0 | ht = lyht_new(1, sizeof(struct lysc_node *), lyb_hash_equal_cb, NULL, 1); |
130 | 0 | LY_CHECK_ERR_RET(!ht, LOGMEM(sibling->module->ctx), LY_EMEM); |
131 | |
|
132 | 0 | getnext_opts = 0; |
133 | 0 | if (sibling->flags & LYS_IS_OUTPUT) { |
134 | 0 | getnext_opts = LYS_GETNEXT_OUTPUT; |
135 | 0 | } |
136 | |
|
137 | 0 | parent = lysc_data_parent(sibling); |
138 | 0 | mod = sibling->module; |
139 | |
|
140 | 0 | sibling = NULL; |
141 | 0 | while ((sibling = (struct lysc_node *)lys_getnext(sibling, parent, mod->compiled, getnext_opts))) { |
142 | | /* find the first non-colliding hash (or specifically non-colliding hash sequence) */ |
143 | 0 | for (i = 0; i < LYB_HASH_BITS; ++i) { |
144 | | /* check that we are not colliding with nodes inserted with a lower collision ID than ours */ |
145 | 0 | int64_t j; |
146 | 0 | for (j = (int64_t)i - 1; j > -1; --j) { |
147 | 0 | if (lyb_hash_sequence_check(ht, sibling, (LYB_HASH)j, i)) { |
148 | 0 | break; |
149 | 0 | } |
150 | 0 | } |
151 | 0 | if (j > -1) { |
152 | | /* some check failed, we must use a higher collision ID */ |
153 | 0 | continue; |
154 | 0 | } |
155 | | |
156 | | /* try to insert node with the current collision ID */ |
157 | 0 | if (!lyht_insert_with_resize_cb(ht, &sibling, lyb_get_hash(sibling, i), lyb_ptr_equal_cb, NULL)) { |
158 | | /* success, no collision */ |
159 | 0 | break; |
160 | 0 | } |
161 | | |
162 | | /* make sure we really cannot insert it with this hash col ID (meaning the whole hash sequence is colliding) */ |
163 | 0 | if (i && !lyb_hash_sequence_check(ht, sibling, i, i)) { |
164 | | /* it can be inserted after all, even though there is already a node with the same last collision ID */ |
165 | 0 | lyht_set_cb(ht, lyb_ptr_equal_cb); |
166 | 0 | if (lyht_insert(ht, &sibling, lyb_get_hash(sibling, i), NULL)) { |
167 | 0 | LOGINT(sibling->module->ctx); |
168 | 0 | lyht_set_cb(ht, lyb_hash_equal_cb); |
169 | 0 | lyht_free(ht); |
170 | 0 | return LY_EINT; |
171 | 0 | } |
172 | 0 | lyht_set_cb(ht, lyb_hash_equal_cb); |
173 | 0 | break; |
174 | 0 | } |
175 | | /* there is still another colliding schema node with the same hash sequence, try higher collision ID */ |
176 | 0 | } |
177 | | |
178 | 0 | if (i == LYB_HASH_BITS) { |
179 | | /* wow */ |
180 | 0 | LOGINT(sibling->module->ctx); |
181 | 0 | lyht_free(ht); |
182 | 0 | return LY_EINT; |
183 | 0 | } |
184 | 0 | } |
185 | | |
186 | | /* change val equal callback so that the HT is usable for finding value hashes */ |
187 | 0 | lyht_set_cb(ht, lyb_ptr_equal_cb); |
188 | |
|
189 | 0 | *ht_p = ht; |
190 | 0 | return LY_SUCCESS; |
191 | 0 | } |
192 | | |
193 | | /** |
194 | | * @brief Find node hash in a hash table. |
195 | | * |
196 | | * @param[in] ht Hash table to search in. |
197 | | * @param[in] node Node to find. |
198 | | * @param[out] hash_p First non-colliding hash found. |
199 | | * @return LY_ERR value. |
200 | | */ |
201 | | static LY_ERR |
202 | | lyb_hash_find(struct hash_table *ht, struct lysc_node *node, LYB_HASH *hash_p) |
203 | 0 | { |
204 | 0 | LYB_HASH hash; |
205 | 0 | uint32_t i; |
206 | |
|
207 | 0 | for (i = 0; i < LYB_HASH_BITS; ++i) { |
208 | 0 | hash = lyb_get_hash(node, i); |
209 | 0 | if (!hash) { |
210 | 0 | LOGINT_RET(node->module->ctx); |
211 | 0 | } |
212 | | |
213 | 0 | if (!lyht_find(ht, &node, hash, NULL)) { |
214 | | /* success, no collision */ |
215 | 0 | break; |
216 | 0 | } |
217 | 0 | } |
218 | | /* cannot happen, we already calculated the hash */ |
219 | 0 | if (i == LYB_HASH_BITS) { |
220 | 0 | LOGINT_RET(node->module->ctx); |
221 | 0 | } |
222 | | |
223 | 0 | *hash_p = hash; |
224 | 0 | return LY_SUCCESS; |
225 | 0 | } |
226 | | |
227 | | /** |
228 | | * @brief Write LYB data fully handling the metadata. |
229 | | * |
230 | | * @param[in] out Out structure. |
231 | | * @param[in] buf Source buffer. |
232 | | * @param[in] count Number of bytes to write. |
233 | | * @param[in] lybctx LYB context. |
234 | | * @return LY_ERR value. |
235 | | */ |
236 | | static LY_ERR |
237 | | lyb_write(struct ly_out *out, const uint8_t *buf, size_t count, struct lylyb_ctx *lybctx) |
238 | 0 | { |
239 | 0 | LY_ARRAY_COUNT_TYPE u; |
240 | 0 | struct lyd_lyb_subtree *full, *iter; |
241 | 0 | size_t to_write; |
242 | 0 | uint8_t meta_buf[LYB_META_BYTES]; |
243 | |
|
244 | 0 | while (1) { |
245 | | /* check for full data chunks */ |
246 | 0 | to_write = count; |
247 | 0 | full = NULL; |
248 | 0 | LY_ARRAY_FOR(lybctx->subtrees, u) { |
249 | | /* we want the innermost chunks resolved first, so replace previous full chunks */ |
250 | 0 | if (lybctx->subtrees[u].written + to_write >= LYB_SIZE_MAX) { |
251 | | /* full chunk, do not write more than allowed */ |
252 | 0 | to_write = LYB_SIZE_MAX - lybctx->subtrees[u].written; |
253 | 0 | full = &lybctx->subtrees[u]; |
254 | 0 | } |
255 | 0 | } |
256 | |
|
257 | 0 | if (!full && !count) { |
258 | 0 | break; |
259 | 0 | } |
260 | | |
261 | | /* we are actually writing some data, not just finishing another chunk */ |
262 | 0 | if (to_write) { |
263 | 0 | LY_CHECK_RET(ly_write_(out, (char *)buf, to_write)); |
264 | |
|
265 | 0 | LY_ARRAY_FOR(lybctx->subtrees, u) { |
266 | | /* increase all written counters */ |
267 | 0 | lybctx->subtrees[u].written += to_write; |
268 | 0 | assert(lybctx->subtrees[u].written <= LYB_SIZE_MAX); |
269 | 0 | } |
270 | | /* decrease count/buf */ |
271 | 0 | count -= to_write; |
272 | 0 | buf += to_write; |
273 | 0 | } |
274 | | |
275 | 0 | if (full) { |
276 | | /* write the meta information (inner chunk count and chunk size) */ |
277 | 0 | meta_buf[0] = full->written & LYB_BYTE_MASK; |
278 | 0 | meta_buf[1] = full->inner_chunks & LYB_BYTE_MASK; |
279 | 0 | LY_CHECK_RET(ly_write_skipped(out, full->position, (char *)meta_buf, LYB_META_BYTES)); |
280 | | |
281 | | /* zero written and inner chunks */ |
282 | 0 | full->written = 0; |
283 | 0 | full->inner_chunks = 0; |
284 | | |
285 | | /* skip space for another chunk size */ |
286 | 0 | LY_CHECK_RET(ly_write_skip(out, LYB_META_BYTES, &full->position)); |
287 | | |
288 | | /* increase inner chunk count */ |
289 | 0 | for (iter = &lybctx->subtrees[0]; iter != full; ++iter) { |
290 | 0 | if (iter->inner_chunks == LYB_INCHUNK_MAX) { |
291 | 0 | LOGINT(lybctx->ctx); |
292 | 0 | return LY_EINT; |
293 | 0 | } |
294 | 0 | ++iter->inner_chunks; |
295 | 0 | } |
296 | 0 | } |
297 | 0 | } |
298 | | |
299 | 0 | return LY_SUCCESS; |
300 | 0 | } |
301 | | |
302 | | /** |
303 | | * @brief Stop the current subtree - write its final metadata. |
304 | | * |
305 | | * @param[in] out Out structure. |
306 | | * @param[in] lybctx LYB context. |
307 | | * @return LY_ERR value. |
308 | | */ |
309 | | static LY_ERR |
310 | | lyb_write_stop_subtree(struct ly_out *out, struct lylyb_ctx *lybctx) |
311 | 0 | { |
312 | 0 | uint8_t meta_buf[LYB_META_BYTES]; |
313 | | |
314 | | /* write the meta chunk information */ |
315 | 0 | meta_buf[0] = LYB_LAST_SUBTREE(lybctx).written & LYB_BYTE_MASK; |
316 | 0 | meta_buf[1] = LYB_LAST_SUBTREE(lybctx).inner_chunks & LYB_BYTE_MASK; |
317 | 0 | LY_CHECK_RET(ly_write_skipped(out, LYB_LAST_SUBTREE(lybctx).position, (char *)&meta_buf, LYB_META_BYTES)); |
318 | |
|
319 | 0 | LY_ARRAY_DECREMENT(lybctx->subtrees); |
320 | 0 | return LY_SUCCESS; |
321 | 0 | } |
322 | | |
323 | | /** |
324 | | * @brief Start a new subtree - skip bytes for its metadata. |
325 | | * |
326 | | * @param[in] out Out structure. |
327 | | * @param[in] lybctx LYB context. |
328 | | * @return LY_ERR value. |
329 | | */ |
330 | | static LY_ERR |
331 | | lyb_write_start_subtree(struct ly_out *out, struct lylyb_ctx *lybctx) |
332 | 0 | { |
333 | 0 | LY_ARRAY_COUNT_TYPE u; |
334 | |
|
335 | 0 | u = LY_ARRAY_COUNT(lybctx->subtrees); |
336 | 0 | if (u == lybctx->subtree_size) { |
337 | 0 | LY_ARRAY_CREATE_RET(lybctx->ctx, lybctx->subtrees, u + LYB_SUBTREE_STEP, LY_EMEM); |
338 | 0 | lybctx->subtree_size = u + LYB_SUBTREE_STEP; |
339 | 0 | } |
340 | | |
341 | 0 | LY_ARRAY_INCREMENT(lybctx->subtrees); |
342 | 0 | LYB_LAST_SUBTREE(lybctx).written = 0; |
343 | 0 | LYB_LAST_SUBTREE(lybctx).inner_chunks = 0; |
344 | | |
345 | | /* another inner chunk */ |
346 | 0 | for (u = 0; u < LY_ARRAY_COUNT(lybctx->subtrees) - 1; ++u) { |
347 | 0 | if (lybctx->subtrees[u].inner_chunks == LYB_INCHUNK_MAX) { |
348 | 0 | LOGINT(lybctx->ctx); |
349 | 0 | return LY_EINT; |
350 | 0 | } |
351 | 0 | ++lybctx->subtrees[u].inner_chunks; |
352 | 0 | } |
353 | | |
354 | 0 | LY_CHECK_RET(ly_write_skip(out, LYB_META_BYTES, &LYB_LAST_SUBTREE(lybctx).position)); |
355 | |
|
356 | 0 | return LY_SUCCESS; |
357 | 0 | } |
358 | | |
359 | | /** |
360 | | * @brief Write a number. |
361 | | * |
362 | | * @param[in] num Number to write. |
363 | | * @param[in] bytes Actual accessible bytes of @p num. |
364 | | * @param[in] out Out structure. |
365 | | * @param[in] lybctx LYB context. |
366 | | * @return LY_ERR value. |
367 | | */ |
368 | | static LY_ERR |
369 | | lyb_write_number(uint64_t num, size_t bytes, struct ly_out *out, struct lylyb_ctx *lybctx) |
370 | 0 | { |
371 | | /* correct byte order */ |
372 | 0 | num = htole64(num); |
373 | |
|
374 | 0 | return lyb_write(out, (uint8_t *)&num, bytes, lybctx); |
375 | 0 | } |
376 | | |
377 | | /** |
378 | | * @brief Write a string. |
379 | | * |
380 | | * @param[in] str String to write. |
381 | | * @param[in] str_len Length of @p str. |
382 | | * @param[in] with_length Whether to precede the string with its length. |
383 | | * @param[in] out Out structure. |
384 | | * @param[in] lybctx LYB context. |
385 | | * @return LY_ERR value. |
386 | | */ |
387 | | static LY_ERR |
388 | | lyb_write_string(const char *str, size_t str_len, ly_bool with_length, struct ly_out *out, struct lylyb_ctx *lybctx) |
389 | 0 | { |
390 | 0 | if (!str) { |
391 | 0 | str = ""; |
392 | 0 | LY_CHECK_ERR_RET(str_len, LOGINT(lybctx->ctx), LY_EINT); |
393 | 0 | } |
394 | 0 | if (!str_len) { |
395 | 0 | str_len = strlen(str); |
396 | 0 | } |
397 | |
|
398 | 0 | if (with_length) { |
399 | | /* print length on 2 bytes */ |
400 | 0 | if (str_len > UINT16_MAX) { |
401 | 0 | LOGINT(lybctx->ctx); |
402 | 0 | return LY_EINT; |
403 | 0 | } |
404 | 0 | LY_CHECK_RET(lyb_write_number(str_len, 2, out, lybctx)); |
405 | 0 | } |
406 | | |
407 | 0 | LY_CHECK_RET(lyb_write(out, (const uint8_t *)str, str_len, lybctx)); |
408 | |
|
409 | 0 | return LY_SUCCESS; |
410 | 0 | } |
411 | | |
412 | | /** |
413 | | * @brief Print YANG module info. |
414 | | * |
415 | | * @param[in] out Out structure. |
416 | | * @param[in] mod Module to print. |
417 | | * @param[in] lybctx LYB context. |
418 | | * @return LY_ERR value. |
419 | | */ |
420 | | static LY_ERR |
421 | | lyb_print_model(struct ly_out *out, const struct lys_module *mod, struct lylyb_ctx *lybctx) |
422 | 0 | { |
423 | 0 | uint16_t revision; |
424 | | |
425 | | /* model name length and model name */ |
426 | 0 | if (mod) { |
427 | 0 | LY_CHECK_RET(lyb_write_string(mod->name, 0, 1, out, lybctx)); |
428 | 0 | } else { |
429 | 0 | LY_CHECK_RET(lyb_write_string("", 0, 1, out, lybctx)); |
430 | 0 | } |
431 | | |
432 | | /* model revision as XXXX XXXX XXXX XXXX (2B) (year is offset from 2000) |
433 | | * YYYY YYYM MMMD DDDD */ |
434 | 0 | revision = 0; |
435 | 0 | if (mod && mod->revision) { |
436 | 0 | int r = atoi(mod->revision); |
437 | 0 | r -= LYB_REV_YEAR_OFFSET; |
438 | 0 | r <<= LYB_REV_YEAR_SHIFT; |
439 | |
|
440 | 0 | revision |= r; |
441 | |
|
442 | 0 | r = atoi(mod->revision + ly_strlen_const("YYYY-")); |
443 | 0 | r <<= LYB_REV_MONTH_SHIFT; |
444 | |
|
445 | 0 | revision |= r; |
446 | |
|
447 | 0 | r = atoi(mod->revision + ly_strlen_const("YYYY-MM-")); |
448 | |
|
449 | 0 | revision |= r; |
450 | 0 | } |
451 | 0 | LY_CHECK_RET(lyb_write_number(revision, sizeof revision, out, lybctx)); |
452 | |
|
453 | 0 | if (mod) { |
454 | | /* fill cached hashes, if not already */ |
455 | 0 | lyb_cache_module_hash(mod); |
456 | 0 | } |
457 | |
|
458 | 0 | return LY_SUCCESS; |
459 | 0 | } |
460 | | |
461 | | /** |
462 | | * @brief Print all used YANG modules. |
463 | | * |
464 | | * @param[in] out Out structure. |
465 | | * @param[in] root Data root. |
466 | | * @param[in] lybctx LYB context. |
467 | | * @return LY_ERR value. |
468 | | */ |
469 | | static LY_ERR |
470 | | lyb_print_data_models(struct ly_out *out, const struct lyd_node *root, struct lylyb_ctx *lybctx) |
471 | 0 | { |
472 | 0 | struct ly_set *set; |
473 | 0 | LY_ARRAY_COUNT_TYPE u; |
474 | 0 | LY_ERR ret = LY_SUCCESS; |
475 | 0 | struct lys_module *mod; |
476 | 0 | const struct lyd_node *node; |
477 | 0 | uint32_t i; |
478 | |
|
479 | 0 | LY_CHECK_RET(ly_set_new(&set)); |
480 | | |
481 | | /* collect all data node modules */ |
482 | 0 | LY_LIST_FOR(root, node) { |
483 | 0 | if (!node->schema) { |
484 | 0 | continue; |
485 | 0 | } |
486 | | |
487 | 0 | mod = node->schema->module; |
488 | 0 | ret = ly_set_add(set, mod, 0, NULL); |
489 | 0 | LY_CHECK_GOTO(ret, cleanup); |
490 | | |
491 | | /* add also their modules deviating or augmenting them */ |
492 | 0 | LY_ARRAY_FOR(mod->deviated_by, u) { |
493 | 0 | ret = ly_set_add(set, mod->deviated_by[u], 0, NULL); |
494 | 0 | LY_CHECK_GOTO(ret, cleanup); |
495 | 0 | } |
496 | 0 | LY_ARRAY_FOR(mod->augmented_by, u) { |
497 | 0 | ret = ly_set_add(set, mod->augmented_by[u], 0, NULL); |
498 | 0 | LY_CHECK_GOTO(ret, cleanup); |
499 | 0 | } |
500 | 0 | } |
501 | | |
502 | | /* now write module count on 2 bytes */ |
503 | 0 | LY_CHECK_GOTO(ret = lyb_write_number(set->count, 2, out, lybctx), cleanup); |
504 | | |
505 | | /* and all the used models */ |
506 | 0 | for (i = 0; i < set->count; ++i) { |
507 | 0 | LY_CHECK_GOTO(ret = lyb_print_model(out, set->objs[i], lybctx), cleanup); |
508 | 0 | } |
509 | | |
510 | 0 | cleanup: |
511 | 0 | ly_set_free(set, NULL); |
512 | 0 | return ret; |
513 | 0 | } |
514 | | |
515 | | /** |
516 | | * @brief Print LYB magic number. |
517 | | * |
518 | | * @param[in] out Out structure. |
519 | | * @return LY_ERR value. |
520 | | */ |
521 | | static LY_ERR |
522 | | lyb_print_magic_number(struct ly_out *out) |
523 | 0 | { |
524 | | /* 'l', 'y', 'b' - 0x6c7962 */ |
525 | 0 | char magic_number[] = {'l', 'y', 'b'}; |
526 | |
|
527 | 0 | LY_CHECK_RET(ly_write_(out, magic_number, 3)); |
528 | |
|
529 | 0 | return LY_SUCCESS; |
530 | 0 | } |
531 | | |
532 | | /** |
533 | | * @brief Print LYB header. |
534 | | * |
535 | | * @param[in] out Out structure. |
536 | | * @return LY_ERR value. |
537 | | */ |
538 | | static LY_ERR |
539 | | lyb_print_header(struct ly_out *out) |
540 | 0 | { |
541 | 0 | uint8_t byte = 0; |
542 | | |
543 | | /* version, future flags */ |
544 | 0 | byte |= LYB_VERSION_NUM; |
545 | |
|
546 | 0 | LY_CHECK_RET(ly_write_(out, (char *)&byte, 1)); |
547 | |
|
548 | 0 | return LY_SUCCESS; |
549 | 0 | } |
550 | | |
551 | | /** |
552 | | * @brief Print prefix data. |
553 | | * |
554 | | * @param[in] out Out structure. |
555 | | * @param[in] format Value prefix format. |
556 | | * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix). |
557 | | * @param[in] lybctx LYB context. |
558 | | * @return LY_ERR value. |
559 | | */ |
560 | | static LY_ERR |
561 | | lyb_print_prefix_data(struct ly_out *out, LY_VALUE_FORMAT format, const void *prefix_data, struct lylyb_ctx *lybctx) |
562 | 0 | { |
563 | 0 | const struct ly_set *set; |
564 | 0 | const struct lyxml_ns *ns; |
565 | 0 | uint32_t i; |
566 | |
|
567 | 0 | switch (format) { |
568 | 0 | case LY_VALUE_XML: |
569 | 0 | set = prefix_data; |
570 | 0 | if (!set) { |
571 | | /* no prefix data */ |
572 | 0 | i = 0; |
573 | 0 | LY_CHECK_RET(lyb_write(out, (uint8_t *)&i, 1, lybctx)); |
574 | 0 | break; |
575 | 0 | } |
576 | 0 | if (set->count > UINT8_MAX) { |
577 | 0 | LOGERR(lybctx->ctx, LY_EINT, "Maximum supported number of prefixes is %u.", UINT8_MAX); |
578 | 0 | return LY_EINT; |
579 | 0 | } |
580 | | |
581 | | /* write number of prefixes on 1 byte */ |
582 | 0 | LY_CHECK_RET(lyb_write(out, (uint8_t *)&set->count, 1, lybctx)); |
583 | | |
584 | | /* write all the prefixes */ |
585 | 0 | for (i = 0; i < set->count; ++i) { |
586 | 0 | ns = set->objs[i]; |
587 | | |
588 | | /* prefix */ |
589 | 0 | LY_CHECK_RET(lyb_write_string(ns->prefix, 0, 1, out, lybctx)); |
590 | | |
591 | | /* namespace */ |
592 | 0 | LY_CHECK_RET(lyb_write_string(ns->uri, 0, 1, out, lybctx)); |
593 | 0 | } |
594 | 0 | break; |
595 | 0 | case LY_VALUE_JSON: |
596 | 0 | case LY_VALUE_LYB: |
597 | | /* nothing to print */ |
598 | 0 | break; |
599 | 0 | default: |
600 | 0 | LOGINT_RET(lybctx->ctx); |
601 | 0 | } |
602 | | |
603 | 0 | return LY_SUCCESS; |
604 | 0 | } |
605 | | |
606 | | /** |
607 | | * @brief Print opaque node. |
608 | | * |
609 | | * @param[in] opaq Node to print. |
610 | | * @param[in] out Out structure. |
611 | | * @param[in] lybctx LYB context. |
612 | | * @return LY_ERR value. |
613 | | */ |
614 | | static LY_ERR |
615 | | lyb_print_opaq(struct lyd_node_opaq *opaq, struct ly_out *out, struct lylyb_ctx *lybctx) |
616 | 0 | { |
617 | | /* prefix */ |
618 | 0 | LY_CHECK_RET(lyb_write_string(opaq->name.prefix, 0, 1, out, lybctx)); |
619 | | |
620 | | /* module reference */ |
621 | 0 | LY_CHECK_RET(lyb_write_string(opaq->name.module_name, 0, 1, out, lybctx)); |
622 | | |
623 | | /* name */ |
624 | 0 | LY_CHECK_RET(lyb_write_string(opaq->name.name, 0, 1, out, lybctx)); |
625 | | |
626 | | /* format */ |
627 | 0 | LY_CHECK_RET(lyb_write_number(opaq->format, 1, out, lybctx)); |
628 | | |
629 | | /* value prefixes */ |
630 | 0 | LY_CHECK_RET(lyb_print_prefix_data(out, opaq->format, opaq->val_prefix_data, lybctx)); |
631 | | |
632 | | /* value */ |
633 | 0 | LY_CHECK_RET(lyb_write_string(opaq->value, 0, 0, out, lybctx)); |
634 | |
|
635 | 0 | return LY_SUCCESS; |
636 | 0 | } |
637 | | |
638 | | /** |
639 | | * @brief Print anydata node. |
640 | | * |
641 | | * @param[in] anydata Node to print. |
642 | | * @param[in] out Out structure. |
643 | | * @param[in] lybctx LYB context. |
644 | | * @return LY_ERR value. |
645 | | */ |
646 | | static LY_ERR |
647 | | lyb_print_anydata(struct lyd_node_any *anydata, struct ly_out *out, struct lylyb_ctx *lybctx) |
648 | 0 | { |
649 | 0 | LY_ERR ret = LY_SUCCESS; |
650 | 0 | LYD_ANYDATA_VALUETYPE value_type; |
651 | 0 | int len; |
652 | 0 | char *buf = NULL; |
653 | 0 | const char *str; |
654 | 0 | struct ly_out *out2 = NULL; |
655 | |
|
656 | 0 | if (anydata->value_type == LYD_ANYDATA_DATATREE) { |
657 | | /* will be printed as a nested LYB data tree */ |
658 | 0 | value_type = LYD_ANYDATA_LYB; |
659 | 0 | } else { |
660 | 0 | value_type = anydata->value_type; |
661 | 0 | } |
662 | | |
663 | | /* first byte is type */ |
664 | 0 | LY_CHECK_GOTO(ret = lyb_write(out, (uint8_t *)&value_type, sizeof value_type, lybctx), cleanup); |
665 | |
|
666 | 0 | if (anydata->value_type == LYD_ANYDATA_DATATREE) { |
667 | | /* print LYB data tree to memory */ |
668 | 0 | LY_CHECK_GOTO(ret = ly_out_new_memory(&buf, 0, &out2), cleanup); |
669 | 0 | LY_CHECK_GOTO(ret = lyb_print_data(out2, anydata->value.tree, LYD_PRINT_WITHSIBLINGS), cleanup); |
670 | |
|
671 | 0 | len = lyd_lyb_data_length(buf); |
672 | 0 | assert(len != -1); |
673 | 0 | str = buf; |
674 | 0 | } else if (anydata->value_type == LYD_ANYDATA_LYB) { |
675 | 0 | len = lyd_lyb_data_length(anydata->value.mem); |
676 | 0 | assert(len != -1); |
677 | 0 | str = anydata->value.mem; |
678 | 0 | } else { |
679 | 0 | len = strlen(anydata->value.str); |
680 | 0 | str = anydata->value.str; |
681 | 0 | } |
682 | | |
683 | | /* followed by the content */ |
684 | 0 | LY_CHECK_GOTO(ret = lyb_write_string(str, (size_t)len, 0, out, lybctx), cleanup); |
685 | |
|
686 | 0 | cleanup: |
687 | 0 | ly_out_free(out2, NULL, 1); |
688 | 0 | return ret; |
689 | 0 | } |
690 | | |
691 | | /** |
692 | | * @brief Print term node. |
693 | | * |
694 | | * @param[in] term Node to print. |
695 | | * @param[in] out Out structure. |
696 | | * @param[in] lybctx LYB context. |
697 | | * @return LY_ERR value. |
698 | | */ |
699 | | static LY_ERR |
700 | | lyb_print_term(struct lyd_node_term *term, struct ly_out *out, struct lylyb_ctx *lybctx) |
701 | 0 | { |
702 | | /* print the value */ |
703 | 0 | return lyb_write_string(lyd_get_value(&term->node), 0, 0, out, lybctx); |
704 | 0 | } |
705 | | |
706 | | /** |
707 | | * @brief Print YANG node metadata. |
708 | | * |
709 | | * @param[in] out Out structure. |
710 | | * @param[in] node Data node whose metadata to print. |
711 | | * @param[in] lybctx LYB context. |
712 | | * @return LY_ERR value. |
713 | | */ |
714 | | static LY_ERR |
715 | | lyb_print_metadata(struct ly_out *out, const struct lyd_node *node, struct lyd_lyb_ctx *lybctx) |
716 | 0 | { |
717 | 0 | uint8_t count = 0; |
718 | 0 | const struct lys_module *wd_mod = NULL; |
719 | 0 | struct lyd_meta *iter; |
720 | | |
721 | | /* with-defaults */ |
722 | 0 | if (node->schema->nodetype & LYD_NODE_TERM) { |
723 | 0 | if (((node->flags & LYD_DEFAULT) && (lybctx->print_options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG))) || |
724 | 0 | ((lybctx->print_options & LYD_PRINT_WD_ALL_TAG) && lyd_is_default(node))) { |
725 | | /* we have implicit OR explicit default node, print attribute only if context include with-defaults schema */ |
726 | 0 | wd_mod = ly_ctx_get_module_latest(node->schema->module->ctx, "ietf-netconf-with-defaults"); |
727 | 0 | } |
728 | 0 | } |
729 | | |
730 | | /* count metadata */ |
731 | 0 | if (wd_mod) { |
732 | 0 | ++count; |
733 | 0 | } |
734 | 0 | for (iter = node->meta; iter; iter = iter->next) { |
735 | 0 | if (count == UINT8_MAX) { |
736 | 0 | LOGERR(lybctx->lybctx->ctx, LY_EINT, "Maximum supported number of data node metadata is %u.", UINT8_MAX); |
737 | 0 | return LY_EINT; |
738 | 0 | } |
739 | 0 | ++count; |
740 | 0 | } |
741 | | |
742 | | /* write number of metadata on 1 byte */ |
743 | 0 | LY_CHECK_RET(lyb_write(out, &count, 1, lybctx->lybctx)); |
744 | |
|
745 | 0 | if (wd_mod) { |
746 | | /* write the "default" metadata */ |
747 | 0 | LY_CHECK_RET(lyb_write_start_subtree(out, lybctx->lybctx)); |
748 | 0 | LY_CHECK_RET(lyb_print_model(out, wd_mod, lybctx->lybctx)); |
749 | 0 | LY_CHECK_RET(lyb_write_string("default", 0, 1, out, lybctx->lybctx)); |
750 | 0 | LY_CHECK_RET(lyb_write_string("true", 0, 0, out, lybctx->lybctx)); |
751 | 0 | LY_CHECK_RET(lyb_write_stop_subtree(out, lybctx->lybctx)); |
752 | 0 | } |
753 | | |
754 | | /* write all the node metadata */ |
755 | 0 | LY_LIST_FOR(node->meta, iter) { |
756 | | /* each metadata is a subtree */ |
757 | 0 | LY_CHECK_RET(lyb_write_start_subtree(out, lybctx->lybctx)); |
758 | | |
759 | | /* model */ |
760 | 0 | LY_CHECK_RET(lyb_print_model(out, iter->annotation->module, lybctx->lybctx)); |
761 | | |
762 | | /* annotation name with length */ |
763 | 0 | LY_CHECK_RET(lyb_write_string(iter->name, 0, 1, out, lybctx->lybctx)); |
764 | | |
765 | | /* metadata value */ |
766 | 0 | LY_CHECK_RET(lyb_write_string(lyd_get_meta_value(iter), 0, 0, out, lybctx->lybctx)); |
767 | | |
768 | | /* finish metadata subtree */ |
769 | 0 | LY_CHECK_RET(lyb_write_stop_subtree(out, lybctx->lybctx)); |
770 | 0 | } |
771 | | |
772 | 0 | return LY_SUCCESS; |
773 | 0 | } |
774 | | |
775 | | /** |
776 | | * @brief Print opaque node attributes. |
777 | | * |
778 | | * @param[in] out Out structure. |
779 | | * @param[in] node Opaque node whose attributes to print. |
780 | | * @param[in] lybctx LYB context. |
781 | | * @return LY_ERR value. |
782 | | */ |
783 | | static LY_ERR |
784 | | lyb_print_attributes(struct ly_out *out, const struct lyd_node_opaq *node, struct lylyb_ctx *lybctx) |
785 | 0 | { |
786 | 0 | uint8_t count = 0; |
787 | 0 | struct lyd_attr *iter; |
788 | |
|
789 | 0 | for (iter = node->attr; iter; iter = iter->next) { |
790 | 0 | if (count == UINT8_MAX) { |
791 | 0 | LOGERR(lybctx->ctx, LY_EINT, "Maximum supported number of data node attributes is %u.", UINT8_MAX); |
792 | 0 | return LY_EINT; |
793 | 0 | } |
794 | 0 | ++count; |
795 | 0 | } |
796 | | |
797 | | /* write number of attributes on 1 byte */ |
798 | 0 | LY_CHECK_RET(lyb_write(out, &count, 1, lybctx)); |
799 | | |
800 | | /* write all the attributes */ |
801 | 0 | LY_LIST_FOR(node->attr, iter) { |
802 | | /* each attribute is a subtree */ |
803 | 0 | LY_CHECK_RET(lyb_write_start_subtree(out, lybctx)); |
804 | | |
805 | | /* prefix */ |
806 | 0 | LY_CHECK_RET(lyb_write_string(iter->name.prefix, 0, 1, out, lybctx)); |
807 | | |
808 | | /* namespace */ |
809 | 0 | LY_CHECK_RET(lyb_write_string(iter->name.module_name, 0, 1, out, lybctx)); |
810 | | |
811 | | /* name */ |
812 | 0 | LY_CHECK_RET(lyb_write_string(iter->name.name, 0, 1, out, lybctx)); |
813 | | |
814 | | /* format */ |
815 | 0 | LY_CHECK_RET(lyb_write_number(iter->format, 1, out, lybctx)); |
816 | | |
817 | | /* value prefixes */ |
818 | 0 | LY_CHECK_RET(lyb_print_prefix_data(out, iter->format, iter->val_prefix_data, lybctx)); |
819 | | |
820 | | /* value */ |
821 | 0 | LY_CHECK_RET(lyb_write_string(iter->value, 0, 0, out, lybctx)); |
822 | | |
823 | | /* finish attribute subtree */ |
824 | 0 | LY_CHECK_RET(lyb_write_stop_subtree(out, lybctx)); |
825 | 0 | } |
826 | | |
827 | 0 | return LY_SUCCESS; |
828 | 0 | } |
829 | | |
830 | | /** |
831 | | * @brief Print schema node hash. |
832 | | * |
833 | | * @param[in] out Out structure. |
834 | | * @param[in] schema Schema node whose hash to print. |
835 | | * @param[in,out] sibling_ht Cached hash table for these siblings, created if NULL. |
836 | | * @param[in] lybctx LYB context. |
837 | | * @return LY_ERR value. |
838 | | */ |
839 | | static LY_ERR |
840 | | lyb_print_schema_hash(struct ly_out *out, struct lysc_node *schema, struct hash_table **sibling_ht, struct lylyb_ctx *lybctx) |
841 | 0 | { |
842 | 0 | LY_ARRAY_COUNT_TYPE u; |
843 | 0 | uint32_t i; |
844 | 0 | LYB_HASH hash; |
845 | 0 | struct lyd_lyb_sib_ht *sib_ht; |
846 | 0 | struct lysc_node *first_sibling; |
847 | |
|
848 | 0 | if (!schema) { |
849 | | /* opaque node, write empty hash */ |
850 | 0 | hash = 0; |
851 | 0 | LY_CHECK_RET(lyb_write(out, &hash, sizeof hash, lybctx)); |
852 | 0 | return LY_SUCCESS; |
853 | 0 | } |
854 | | |
855 | | /* create whole sibling HT if not already created and saved */ |
856 | 0 | if (!*sibling_ht) { |
857 | | /* get first schema data sibling */ |
858 | 0 | first_sibling = (struct lysc_node *)lys_getnext(NULL, lysc_data_parent(schema), schema->module->compiled, |
859 | 0 | (schema->flags & LYS_IS_OUTPUT) ? LYS_GETNEXT_OUTPUT : 0); |
860 | 0 | LY_ARRAY_FOR(lybctx->sib_hts, u) { |
861 | 0 | if (lybctx->sib_hts[u].first_sibling == first_sibling) { |
862 | | /* we have already created a hash table for these siblings */ |
863 | 0 | *sibling_ht = lybctx->sib_hts[u].ht; |
864 | 0 | break; |
865 | 0 | } |
866 | 0 | } |
867 | |
|
868 | 0 | if (!*sibling_ht) { |
869 | | /* we must create sibling hash table */ |
870 | 0 | LY_CHECK_RET(lyb_hash_siblings(first_sibling, sibling_ht)); |
871 | | |
872 | | /* and save it */ |
873 | 0 | LY_ARRAY_NEW_RET(lybctx->ctx, lybctx->sib_hts, sib_ht, LY_EMEM); |
874 | |
|
875 | 0 | sib_ht->first_sibling = first_sibling; |
876 | 0 | sib_ht->ht = *sibling_ht; |
877 | 0 | } |
878 | 0 | } |
879 | | |
880 | | /* get our hash */ |
881 | 0 | LY_CHECK_RET(lyb_hash_find(*sibling_ht, schema, &hash)); |
882 | | |
883 | | /* write the hash */ |
884 | 0 | LY_CHECK_RET(lyb_write(out, &hash, sizeof hash, lybctx)); |
885 | |
|
886 | 0 | if (hash & LYB_HASH_COLLISION_ID) { |
887 | | /* no collision for this hash, we are done */ |
888 | 0 | return LY_SUCCESS; |
889 | 0 | } |
890 | | |
891 | | /* written hash was a collision, write also all the preceding hashes */ |
892 | 0 | for (i = 0; !(hash & (LYB_HASH_COLLISION_ID >> i)); ++i) {} |
893 | |
|
894 | 0 | for ( ; i; --i) { |
895 | 0 | hash = lyb_get_hash(schema, i - 1); |
896 | 0 | if (!hash) { |
897 | 0 | return LY_EINT; |
898 | 0 | } |
899 | 0 | assert(hash & (LYB_HASH_COLLISION_ID >> (i - 1))); |
900 | |
|
901 | 0 | LY_CHECK_RET(lyb_write(out, &hash, sizeof hash, lybctx)); |
902 | 0 | } |
903 | | |
904 | 0 | return LY_SUCCESS; |
905 | 0 | } |
906 | | |
907 | | /** |
908 | | * @brief Print data subtree. |
909 | | * |
910 | | * @param[in] out Out structure. |
911 | | * @param[in] node Root node of the subtree to print. |
912 | | * @param[in,out] sibling_ht Cached hash table for these data siblings, created if NULL. |
913 | | * @param[in] lybctx LYB context. |
914 | | * @return LY_ERR value. |
915 | | */ |
916 | | static LY_ERR |
917 | | lyb_print_subtree(struct ly_out *out, const struct lyd_node *node, struct hash_table **sibling_ht, struct lyd_lyb_ctx *lybctx) |
918 | 0 | { |
919 | 0 | struct hash_table *child_ht = NULL; |
920 | | |
921 | | /* register a new subtree */ |
922 | 0 | LY_CHECK_RET(lyb_write_start_subtree(out, lybctx->lybctx)); |
923 | | |
924 | | /* write model info first */ |
925 | 0 | if (!node->schema && !lyd_parent(node)) { |
926 | 0 | LY_CHECK_RET(lyb_print_model(out, NULL, lybctx->lybctx)); |
927 | 0 | } else if (node->schema && !lysc_data_parent(node->schema)) { |
928 | 0 | LY_CHECK_RET(lyb_print_model(out, node->schema->module, lybctx->lybctx)); |
929 | 0 | } |
930 | | |
931 | | /* write schema hash */ |
932 | 0 | LY_CHECK_RET(lyb_print_schema_hash(out, (struct lysc_node *)node->schema, sibling_ht, lybctx->lybctx)); |
933 | | |
934 | | /* write any metadata/attributes */ |
935 | 0 | if (node->schema) { |
936 | 0 | LY_CHECK_RET(lyb_print_metadata(out, node, lybctx)); |
937 | 0 | } else { |
938 | 0 | LY_CHECK_RET(lyb_print_attributes(out, (struct lyd_node_opaq *)node, lybctx->lybctx)); |
939 | 0 | } |
940 | | |
941 | | /* write node flags */ |
942 | 0 | LY_CHECK_RET(lyb_write_number(node->flags, sizeof node->flags, out, lybctx->lybctx)); |
943 | | |
944 | | /* write node content */ |
945 | 0 | if (!node->schema) { |
946 | 0 | LY_CHECK_RET(lyb_print_opaq((struct lyd_node_opaq *)node, out, lybctx->lybctx)); |
947 | 0 | } else if (node->schema->nodetype & LYD_NODE_INNER) { |
948 | | /* nothing to write */ |
949 | 0 | } else if (node->schema->nodetype & LYD_NODE_TERM) { |
950 | 0 | LY_CHECK_RET(lyb_print_term((struct lyd_node_term *)node, out, lybctx->lybctx)); |
951 | 0 | } else if (node->schema->nodetype & LYD_NODE_ANY) { |
952 | 0 | LY_CHECK_RET(lyb_print_anydata((struct lyd_node_any *)node, out, lybctx->lybctx)); |
953 | 0 | } else { |
954 | 0 | LOGINT_RET(lybctx->lybctx->ctx); |
955 | 0 | } |
956 | | |
957 | | /* recursively write all the descendants */ |
958 | 0 | LY_LIST_FOR(lyd_child(node), node) { |
959 | 0 | LY_CHECK_RET(lyb_print_subtree(out, node, &child_ht, lybctx)); |
960 | 0 | } |
961 | | |
962 | | /* finish this subtree */ |
963 | 0 | LY_CHECK_RET(lyb_write_stop_subtree(out, lybctx->lybctx)); |
964 | |
|
965 | 0 | return LY_SUCCESS; |
966 | 0 | } |
967 | | |
968 | | LY_ERR |
969 | | lyb_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options) |
970 | 0 | { |
971 | 0 | LY_ERR ret = LY_SUCCESS; |
972 | 0 | uint8_t zero = 0; |
973 | 0 | struct hash_table *top_sibling_ht = NULL; |
974 | 0 | const struct lys_module *prev_mod = NULL; |
975 | 0 | struct lyd_lyb_ctx *lybctx; |
976 | 0 | const struct ly_ctx *ctx = root ? LYD_CTX(root) : NULL; |
977 | |
|
978 | 0 | lybctx = calloc(1, sizeof *lybctx); |
979 | 0 | LY_CHECK_ERR_RET(!lybctx, LOGMEM(ctx), LY_EMEM); |
980 | 0 | lybctx->lybctx = calloc(1, sizeof *lybctx->lybctx); |
981 | 0 | LY_CHECK_ERR_RET(!lybctx, LOGMEM(ctx), LY_EMEM); |
982 | |
|
983 | 0 | lybctx->print_options = options; |
984 | 0 | if (root) { |
985 | 0 | lybctx->lybctx->ctx = ctx; |
986 | |
|
987 | 0 | if (root->schema && lysc_data_parent(root->schema)) { |
988 | 0 | LOGERR(lybctx->lybctx->ctx, LY_EINVAL, "LYB printer supports only printing top-level nodes."); |
989 | 0 | ret = LY_EINVAL; |
990 | 0 | goto cleanup; |
991 | 0 | } |
992 | 0 | } |
993 | | |
994 | | /* LYB magic number */ |
995 | 0 | LY_CHECK_GOTO(ret = lyb_print_magic_number(out), cleanup); |
996 | | |
997 | | /* LYB header */ |
998 | 0 | LY_CHECK_GOTO(ret = lyb_print_header(out), cleanup); |
999 | | |
1000 | | /* all used models */ |
1001 | 0 | LY_CHECK_GOTO(ret = lyb_print_data_models(out, root, lybctx->lybctx), cleanup); |
1002 | |
|
1003 | 0 | LY_LIST_FOR(root, root) { |
1004 | | /* do not reuse sibling hash tables from different modules */ |
1005 | 0 | if (!root->schema || (root->schema->module != prev_mod)) { |
1006 | 0 | top_sibling_ht = NULL; |
1007 | 0 | prev_mod = root->schema ? root->schema->module : NULL; |
1008 | 0 | } |
1009 | |
|
1010 | 0 | LY_CHECK_GOTO(ret = lyb_print_subtree(out, root, &top_sibling_ht, lybctx), cleanup); |
1011 | |
|
1012 | 0 | if (!(options & LYD_PRINT_WITHSIBLINGS)) { |
1013 | 0 | break; |
1014 | 0 | } |
1015 | 0 | } |
1016 | | |
1017 | | /* ending zero byte */ |
1018 | 0 | LY_CHECK_GOTO(ret = lyb_write(out, &zero, sizeof zero, lybctx->lybctx), cleanup); |
1019 | |
|
1020 | 0 | cleanup: |
1021 | 0 | lyd_lyb_ctx_free((struct lyd_ctx *)lybctx); |
1022 | 0 | return ret; |
1023 | 0 | } |