Blame src/printer_lyb.c

Packit 8fb591
/**
Packit 8fb591
 * @file printer_lyb.c
Packit 8fb591
 * @author Michal Vasko <mvasko@cesnet.cz>
Packit 8fb591
 * @brief LYB printer for libyang data structure
Packit 8fb591
 *
Packit 8fb591
 * Copyright (c) 2018 CESNET, z.s.p.o.
Packit 8fb591
 *
Packit 8fb591
 * This source code is licensed under BSD 3-Clause License (the "License").
Packit 8fb591
 * You may not use this file except in compliance with the License.
Packit 8fb591
 * You may obtain a copy of the License at
Packit 8fb591
 *
Packit 8fb591
 *     https://opensource.org/licenses/BSD-3-Clause
Packit 8fb591
 */
Packit 8fb591
Packit 8fb591
#include <stdlib.h>
Packit 8fb591
#include <stdio.h>
Packit 8fb591
#include <stdint.h>
Packit 8fb591
#include <string.h>
Packit 8fb591
#include <assert.h>
Packit 8fb591
#include <stdint.h>
Packit 8fb591
#ifdef __APPLE__
Packit 8fb591
# include <libkern/OSByteOrder.h>
Packit 8fb591
# define htole64(x) OSSwapHostToLittleInt64(x)
Packit 8fb591
#else
Packit 8fb591
# include <endian.h>
Packit 8fb591
#endif
Packit 8fb591
Packit 8fb591
#include "common.h"
Packit 8fb591
#include "printer.h"
Packit 8fb591
#include "tree_schema.h"
Packit 8fb591
#include "tree_data.h"
Packit 8fb591
#include "resolve.h"
Packit 8fb591
#include "tree_internal.h"
Packit 8fb591
Packit 8fb591
static int
Packit 8fb591
lyb_hash_equal_cb(void *UNUSED(val1_p), void *UNUSED(val2_p), int UNUSED(mod), void *UNUSED(cb_data))
Packit 8fb591
{
Packit 8fb591
    /* for this purpose, if hash matches, the value does also, we do not want 2 values to have the same hash */
Packit 8fb591
    return 1;
Packit 8fb591
}
Packit 8fb591
Packit 8fb591
static int
Packit 8fb591
lyb_ptr_equal_cb(void *val1_p, void *val2_p, int UNUSED(mod), void *UNUSED(cb_data))
Packit 8fb591
{
Packit 8fb591
    struct lys_node *val1 = *(struct lys_node **)val1_p;
Packit 8fb591
    struct lys_node *val2 = *(struct lys_node **)val2_p;
Packit 8fb591
Packit 8fb591
    if (val1 == val2) {
Packit 8fb591
        return 1;
Packit 8fb591
    }
Packit 8fb591
    return 0;
Packit 8fb591
}
Packit 8fb591
Packit 8fb591
/* check that sibling collision hash i is safe to insert into ht
Packit 8fb591
 * return: 0 - no whole hash sequence collision, 1 - whole hash sequence collision, -1 - fatal error
Packit 8fb591
 */
Packit 8fb591
static int
Packit 8fb591
lyb_hash_sequence_check(struct hash_table *ht, struct lys_node *sibling, int ht_col_id, int compare_col_id)
Packit 8fb591
{
Packit 8fb591
    int j;
Packit 8fb591
    struct lys_node **col_node;
Packit 8fb591
Packit 8fb591
    /* get the first node inserted with last hash col ID ht_col_id */
Packit 8fb591
    if (lyht_find(ht, &sibling, lyb_hash(sibling, ht_col_id), (void **)&col_node)) {
Packit 8fb591
        /* there is none. valid situation */
Packit 8fb591
        return 0;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    lyht_set_cb(ht, lyb_ptr_equal_cb);
Packit 8fb591
    do {
Packit 8fb591
        for (j = compare_col_id; j > -1; --j) {
Packit 8fb591
            if (lyb_hash(sibling, j) != lyb_hash(*col_node, j)) {
Packit 8fb591
                /* one non-colliding hash */
Packit 8fb591
                break;
Packit 8fb591
            }
Packit 8fb591
        }
Packit 8fb591
        if (j == -1) {
Packit 8fb591
            /* all whole hash sequences of nodes inserted with last hash col ID compare_col_id collide */
Packit 8fb591
            lyht_set_cb(ht, lyb_hash_equal_cb);
Packit 8fb591
            return 1;
Packit 8fb591
        }
Packit 8fb591
Packit 8fb591
        /* get next node inserted with last hash col ID ht_col_id */
Packit 8fb591
    } while (!lyht_find_next(ht, col_node, lyb_hash(*col_node, ht_col_id), (void **)&col_node));
Packit 8fb591
Packit 8fb591
    lyht_set_cb(ht, lyb_hash_equal_cb);
Packit 8fb591
    return 0;
Packit 8fb591
}
Packit 8fb591
Packit 8fb591
#ifndef NDEBUG
Packit 8fb591
Packit 8fb591
static int
Packit 8fb591
lyb_check_augment_collision(struct hash_table *ht, struct lys_node *aug1, struct lys_node *aug2)
Packit 8fb591
{
Packit 8fb591
    struct lys_node *iter1 = NULL, *iter2 = NULL;
Packit 8fb591
    int i, coliding = 0;
Packit 8fb591
    values_equal_cb cb = NULL;
Packit 8fb591
    LYB_HASH hash1, hash2;
Packit 8fb591
Packit 8fb591
    /* go throught combination of all nodes and check if coliding hash is used */
Packit 8fb591
    while ((iter1 = (struct lys_node *)lys_getnext(iter1, aug1, aug1->module, 0))) {
Packit 8fb591
        iter2 = NULL;
Packit 8fb591
        while ((iter2 = (struct lys_node *)lys_getnext(iter2, aug2, aug2->module, 0))) {
Packit 8fb591
            coliding = 0;
Packit 8fb591
            for (i = 0; i < LYB_HASH_BITS; i++) {
Packit 8fb591
                hash1 = lyb_hash(iter1, i);
Packit 8fb591
                hash2 = lyb_hash(iter2, i);
Packit 8fb591
                LY_CHECK_ERR_RETURN(!hash1 || !hash2, LOGINT(aug1->module->ctx), 0);
Packit 8fb591
Packit 8fb591
                if (hash1 == hash2) {
Packit 8fb591
                    coliding++;
Packit 8fb591
                    /* if one of values with coliding hash is in hash table, we have a problem */
Packit 8fb591
                    cb = lyht_set_cb(ht, lyb_ptr_equal_cb);
Packit 8fb591
                    if ((lyht_find(ht, &iter1, hash1, NULL) == 0) || (lyht_find(ht, &iter2, hash2, NULL) == 0)) {
Packit 8fb591
                        LOGWRN(aug1->module->ctx, "Augmentations from modules \"%s\" and \"%s\" have fatal hash collision.",
Packit 8fb591
                               iter1->module->name, iter2->module->name);
Packit 8fb591
                        LOGWRN(aug1->module->ctx, "It will cause no errors if module \"%s\" is always loaded before \"%s\".",
Packit 8fb591
                               iter1->module->name, iter2->module->name);
Packit 8fb591
                        lyht_set_cb(ht, cb);
Packit 8fb591
                        return 1;
Packit 8fb591
                    }
Packit 8fb591
                    lyht_set_cb(ht, cb);
Packit 8fb591
                }
Packit 8fb591
            }
Packit 8fb591
            LY_CHECK_ERR_RETURN(coliding == LYB_HASH_BITS, LOGINT(aug1->module->ctx), 1);
Packit 8fb591
        }
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    /* no used hashes with collision found */
Packit 8fb591
    return 0;
Packit 8fb591
}
Packit 8fb591
Packit 8fb591
static void
Packit 8fb591
lyb_check_augments(struct lys_node *parent, struct hash_table *ht, int options)
Packit 8fb591
{
Packit 8fb591
    struct lys_node *iter, *sibling = NULL, **augs = NULL;
Packit 8fb591
    void *ret;
Packit 8fb591
    int augs_size = 1, augs_found = 0, i, j, found;
Packit 8fb591
    struct lys_module *mod;
Packit 8fb591
Packit 8fb591
    assert(parent);
Packit 8fb591
    mod = lys_node_module(parent);
Packit 8fb591
Packit 8fb591
    augs = malloc(sizeof sibling * augs_size);
Packit 8fb591
    LY_CHECK_ERR_RETURN(!augs, LOGMEM(mod->ctx), );
Packit 8fb591
Packit 8fb591
    while ((sibling = (struct lys_node *)lys_getnext(sibling, parent, NULL, 0))) {
Packit 8fb591
        if (options & (LYD_OPT_RPC | LYD_OPT_RPCREPLY)) {
Packit 8fb591
            for (iter = lys_parent(sibling);
Packit 8fb591
                iter && (iter->nodetype & (LYS_USES | LYS_CASE | LYS_CHOICE));
Packit 8fb591
                iter = lys_parent(iter));
Packit 8fb591
Packit 8fb591
            if (iter && (((options & LYD_OPT_RPC) && (iter->nodetype == LYS_OUTPUT))
Packit 8fb591
                || ((options & LYD_OPT_RPCREPLY) && (iter->nodetype == LYS_INPUT)))) {
Packit 8fb591
                /* skip unused nodes */
Packit 8fb591
                continue;
Packit 8fb591
            }
Packit 8fb591
        }
Packit 8fb591
        /* build array of all augments from different modules */
Packit 8fb591
        if (sibling->parent->nodetype == LYS_AUGMENT && lys_node_module(sibling->parent) != mod) {
Packit 8fb591
            found = 0;
Packit 8fb591
            for (i = 0; i < augs_found; i++) {
Packit 8fb591
                if (lys_node_module(augs[i]) == lys_node_module(sibling)) {
Packit 8fb591
                    found = 1;
Packit 8fb591
                    break;
Packit 8fb591
                }
Packit 8fb591
            }
Packit 8fb591
            if (!found) {
Packit 8fb591
                if (augs_size == augs_found) {
Packit 8fb591
                    augs_size *= 2;
Packit 8fb591
                    ret = realloc(augs, sizeof sibling * augs_size);
Packit 8fb591
                    if (!ret) {
Packit 8fb591
                        LOGMEM(mod->ctx);
Packit 8fb591
                        free(augs);
Packit 8fb591
                        return;
Packit 8fb591
                    }
Packit 8fb591
                    augs = ret;
Packit 8fb591
                }
Packit 8fb591
                augs[augs_found] = sibling;
Packit 8fb591
                augs_found++;
Packit 8fb591
            }
Packit 8fb591
        }
Packit 8fb591
    }
Packit 8fb591
    /* check collisions for every pair */
Packit 8fb591
    for (i = 0; i < augs_found; i++) {
Packit 8fb591
        for (j = i + 1; j < augs_found; j++) {
Packit 8fb591
            if (lyb_check_augment_collision(ht, augs[i]->parent, augs[j]->parent)) {
Packit 8fb591
                free(augs);
Packit 8fb591
                return;
Packit 8fb591
            }
Packit 8fb591
        }
Packit 8fb591
    }
Packit 8fb591
    free(augs);
Packit 8fb591
    return;
Packit 8fb591
}
Packit 8fb591
Packit 8fb591
#endif
Packit 8fb591
Packit 8fb591
static struct hash_table *
Packit 8fb591
lyb_hash_siblings(struct lys_node *sibling, const struct lys_module **models, int mod_count, int options)
Packit 8fb591
{
Packit 8fb591
    struct hash_table *ht;
Packit 8fb591
    struct lys_node *parent, *iter;
Packit 8fb591
    const struct lys_module *mod;
Packit 8fb591
    int i, j;
Packit 8fb591
#ifndef NDEBUG
Packit 8fb591
    int aug_col = 0;
Packit 8fb591
    const struct lys_module *aug_mod = NULL;
Packit 8fb591
#endif
Packit 8fb591
Packit 8fb591
    ht = lyht_new(1, sizeof(struct lys_node *), lyb_hash_equal_cb, NULL, 1);
Packit 8fb591
    LY_CHECK_ERR_RETURN(!ht, LOGMEM(sibling->module->ctx), NULL);
Packit 8fb591
Packit 8fb591
    for (parent = lys_parent(sibling);
Packit 8fb591
         parent && (parent->nodetype & (LYS_USES | LYS_CHOICE | LYS_CASE | LYS_INPUT | LYS_OUTPUT));
Packit 8fb591
         parent = lys_parent(parent));
Packit 8fb591
    mod = lys_node_module(sibling);
Packit 8fb591
Packit 8fb591
    sibling = NULL;
Packit 8fb591
    while ((sibling = (struct lys_node *)lys_getnext(sibling, parent, mod, 0))) {
Packit 8fb591
        if (models && !lyb_has_schema_model(sibling, models, mod_count)) {
Packit 8fb591
            /* ignore models not present during printing */
Packit 8fb591
            continue;
Packit 8fb591
        }
Packit 8fb591
Packit 8fb591
        if (options & (LYD_OPT_RPC | LYD_OPT_RPCREPLY)) {
Packit 8fb591
            for (iter = lys_parent(sibling);
Packit 8fb591
                 iter && (iter->nodetype & (LYS_USES | LYS_CASE | LYS_CHOICE));
Packit 8fb591
                 iter = lys_parent(iter));
Packit 8fb591
Packit 8fb591
            if (iter && (((options & LYD_OPT_RPC) && (iter->nodetype == LYS_OUTPUT))
Packit 8fb591
                    || ((options & LYD_OPT_RPCREPLY) && (iter->nodetype == LYS_INPUT)))) {
Packit 8fb591
                /* skip unused nodes */
Packit 8fb591
                continue;
Packit 8fb591
            }
Packit 8fb591
        }
Packit 8fb591
Packit 8fb591
#ifndef NDEBUG
Packit 8fb591
        if (sibling->parent && sibling->parent->nodetype == LYS_AUGMENT && lys_node_module(sibling->parent) != mod) {
Packit 8fb591
            if (aug_mod && aug_mod != lys_node_module(sibling->parent)) {
Packit 8fb591
                aug_col = 1;
Packit 8fb591
            }
Packit 8fb591
            aug_mod = lys_node_module(sibling);
Packit 8fb591
        }
Packit 8fb591
#endif
Packit 8fb591
Packit 8fb591
        /* find the first non-colliding hash (or specifically non-colliding hash sequence) */
Packit 8fb591
        for (i = 0; i < LYB_HASH_BITS; ++i) {
Packit 8fb591
            /* check that we are not colliding with nodes inserted with a lower collision ID than ours */
Packit 8fb591
            for (j = i - 1; j > -1; --j) {
Packit 8fb591
                if (lyb_hash_sequence_check(ht, sibling, j, i)) {
Packit 8fb591
                    break;
Packit 8fb591
                }
Packit 8fb591
            }
Packit 8fb591
            if (j > -1) {
Packit 8fb591
                /* some check failed, we must use a higher collision ID */
Packit 8fb591
                continue;
Packit 8fb591
            }
Packit 8fb591
Packit 8fb591
            /* try to insert node with the current collision ID */
Packit 8fb591
            if (!lyht_insert_with_resize_cb(ht, &sibling, lyb_hash(sibling, i), lyb_ptr_equal_cb, NULL)) {
Packit 8fb591
                /* success, no collision */
Packit 8fb591
                break;
Packit 8fb591
            }
Packit 8fb591
Packit 8fb591
            /* make sure we really cannot insert it with this hash col ID (meaning the whole hash sequence is colliding) */
Packit 8fb591
            if (i && !lyb_hash_sequence_check(ht, sibling, i, i)) {
Packit 8fb591
                /* it can be inserted after all, even though there is already a node with the same last collision ID */
Packit 8fb591
                lyht_set_cb(ht, lyb_ptr_equal_cb);
Packit 8fb591
                if (lyht_insert(ht, &sibling, lyb_hash(sibling, i), NULL)) {
Packit 8fb591
                    lyht_set_cb(ht, lyb_hash_equal_cb);
Packit 8fb591
                    LOGINT(sibling->module->ctx);
Packit 8fb591
                    lyht_free(ht);
Packit 8fb591
                    return NULL;
Packit 8fb591
                }
Packit 8fb591
                lyht_set_cb(ht, lyb_hash_equal_cb);
Packit 8fb591
                break;
Packit 8fb591
            }
Packit 8fb591
            /* there is still another colliding schema node with the same hash sequence, try higher collision ID */
Packit 8fb591
        }
Packit 8fb591
Packit 8fb591
        if (i == LYB_HASH_BITS) {
Packit 8fb591
            /* wow */
Packit 8fb591
            LOGINT(sibling->module->ctx);
Packit 8fb591
            lyht_free(ht);
Packit 8fb591
            return NULL;
Packit 8fb591
        }
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
#ifndef NDEBUG
Packit 8fb591
    if (aug_col) {
Packit 8fb591
        lyb_check_augments(parent, ht, options);
Packit 8fb591
    }
Packit 8fb591
#endif
Packit 8fb591
Packit 8fb591
    /* change val equal callback so that the HT is usable for finding value hashes */
Packit 8fb591
    lyht_set_cb(ht, lyb_ptr_equal_cb);
Packit 8fb591
Packit 8fb591
    return ht;
Packit 8fb591
}
Packit 8fb591
Packit 8fb591
static LYB_HASH
Packit 8fb591
lyb_hash_find(struct hash_table *ht, struct lys_node *node)
Packit 8fb591
{
Packit 8fb591
    LYB_HASH hash;
Packit 8fb591
    uint32_t i;
Packit 8fb591
Packit 8fb591
    for (i = 0; i < LYB_HASH_BITS; ++i) {
Packit 8fb591
        hash = lyb_hash(node, i);
Packit 8fb591
        if (!hash) {
Packit 8fb591
            LOGINT(node->module->ctx);
Packit 8fb591
            return 0;
Packit 8fb591
        }
Packit 8fb591
Packit 8fb591
        if (!lyht_find(ht, &node, hash, NULL)) {
Packit 8fb591
            /* success, no collision */
Packit 8fb591
            break;
Packit 8fb591
        }
Packit 8fb591
    }
Packit 8fb591
    /* cannot happen, we already calculated the hash */
Packit 8fb591
    if (i == LYB_HASH_BITS) {
Packit 8fb591
        LOGINT(node->module->ctx);
Packit 8fb591
        return 0;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    return hash;
Packit 8fb591
}
Packit 8fb591
Packit 8fb591
/* writing function handles writing size information */
Packit 8fb591
static int
Packit 8fb591
lyb_write(struct lyout *out, const uint8_t *buf, size_t count, struct lyb_state *lybs)
Packit 8fb591
{
Packit 8fb591
    int ret = 0, i, full_chunk_i;
Packit 8fb591
    size_t r, to_write;
Packit 8fb591
    uint8_t meta_buf[LYB_META_BYTES];
Packit 8fb591
Packit 8fb591
    assert(out && lybs);
Packit 8fb591
Packit 8fb591
    while (1) {
Packit 8fb591
        /* check for full data chunks */
Packit 8fb591
        to_write = count;
Packit 8fb591
        full_chunk_i = -1;
Packit 8fb591
        for (i = 0; i < lybs->used; ++i) {
Packit 8fb591
            /* we want the innermost chunks resolved first, so replace previous full chunks */
Packit 8fb591
            if (lybs->written[i] + to_write >= LYB_SIZE_MAX) {
Packit 8fb591
                /* full chunk, do not write more than allowed */
Packit 8fb591
                to_write = LYB_SIZE_MAX - lybs->written[i];
Packit 8fb591
                full_chunk_i = i;
Packit 8fb591
            }
Packit 8fb591
        }
Packit 8fb591
Packit 8fb591
        if ((full_chunk_i == -1) && !count) {
Packit 8fb591
            break;
Packit 8fb591
        }
Packit 8fb591
Packit 8fb591
        /* we are actually writing some data, not just finishing another chunk */
Packit 8fb591
        if (to_write) {
Packit 8fb591
            r = ly_write(out, (char *)buf, to_write);
Packit 8fb591
            if (r < to_write) {
Packit 8fb591
                return -1;
Packit 8fb591
            }
Packit 8fb591
Packit 8fb591
            for (i = 0; i < lybs->used; ++i) {
Packit 8fb591
                /* increase all written counters */
Packit 8fb591
                lybs->written[i] += r;
Packit 8fb591
                assert(lybs->written[i] <= LYB_SIZE_MAX);
Packit 8fb591
            }
Packit 8fb591
            /* decrease count/buf */
Packit 8fb591
            count -= r;
Packit 8fb591
            buf += r;
Packit 8fb591
Packit 8fb591
            ret += r;
Packit 8fb591
        }
Packit 8fb591
Packit 8fb591
        if (full_chunk_i > -1) {
Packit 8fb591
            /* write the meta information (inner chunk count and chunk size) */
Packit 8fb591
            meta_buf[0] = lybs->written[full_chunk_i] & 0xFF;
Packit 8fb591
            meta_buf[1] = lybs->inner_chunks[full_chunk_i] & 0xFF;
Packit 8fb591
Packit 8fb591
            r = ly_write_skipped(out, lybs->position[full_chunk_i], (char *)meta_buf, LYB_META_BYTES);
Packit 8fb591
            if (r < LYB_META_BYTES) {
Packit 8fb591
                return -1;
Packit 8fb591
            }
Packit 8fb591
Packit 8fb591
            /* zero written and inner chunks */
Packit 8fb591
            lybs->written[full_chunk_i] = 0;
Packit 8fb591
            lybs->inner_chunks[full_chunk_i] = 0;
Packit 8fb591
Packit 8fb591
            /* skip space for another chunk size */
Packit 8fb591
            r = ly_write_skip(out, LYB_META_BYTES, &lybs->position[full_chunk_i]);
Packit 8fb591
            if (r < LYB_META_BYTES) {
Packit 8fb591
                return -1;
Packit 8fb591
            }
Packit 8fb591
Packit 8fb591
            ret += r;
Packit 8fb591
Packit 8fb591
            /* increase inner chunk count */
Packit 8fb591
            for (i = 0; i < full_chunk_i; ++i) {
Packit 8fb591
                if (lybs->inner_chunks[i] == LYB_INCHUNK_MAX) {
Packit 8fb591
                    LOGINT(NULL);
Packit 8fb591
                    return -1;
Packit 8fb591
                }
Packit 8fb591
                ++lybs->inner_chunks[i];
Packit 8fb591
            }
Packit 8fb591
        }
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    return ret;
Packit 8fb591
}
Packit 8fb591
Packit 8fb591
static int
Packit 8fb591
lyb_write_stop_subtree(struct lyout *out, struct lyb_state *lybs)
Packit 8fb591
{
Packit 8fb591
    int r;
Packit 8fb591
    uint8_t meta_buf[LYB_META_BYTES];
Packit 8fb591
Packit 8fb591
    /* write the meta chunk information */
Packit 8fb591
    meta_buf[0] = lybs->written[lybs->used - 1] & 0xFF;
Packit 8fb591
    meta_buf[1] = lybs->inner_chunks[lybs->used - 1] & 0xFF;
Packit 8fb591
Packit 8fb591
    r = ly_write_skipped(out, lybs->position[lybs->used - 1], (char *)&meta_buf, LYB_META_BYTES);
Packit 8fb591
    if (r < LYB_META_BYTES) {
Packit 8fb591
        return -1;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    --lybs->used;
Packit 8fb591
    return 0;
Packit 8fb591
}
Packit 8fb591
Packit 8fb591
static int
Packit 8fb591
lyb_write_start_subtree(struct lyout *out, struct lyb_state *lybs)
Packit 8fb591
{
Packit 8fb591
    int i;
Packit 8fb591
Packit 8fb591
    if (lybs->used == lybs->size) {
Packit 8fb591
        lybs->size += LYB_STATE_STEP;
Packit 8fb591
        lybs->written = ly_realloc(lybs->written, lybs->size * sizeof *lybs->written);
Packit 8fb591
        lybs->position = ly_realloc(lybs->position, lybs->size * sizeof *lybs->position);
Packit 8fb591
        lybs->inner_chunks = ly_realloc(lybs->inner_chunks, lybs->size * sizeof *lybs->inner_chunks);
Packit 8fb591
        LY_CHECK_ERR_RETURN(!lybs->written || !lybs->position || !lybs->inner_chunks, LOGMEM(NULL), -1);
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    ++lybs->used;
Packit 8fb591
    lybs->written[lybs->used - 1] = 0;
Packit 8fb591
    lybs->inner_chunks[lybs->used - 1] = 0;
Packit 8fb591
Packit 8fb591
    /* another inner chunk */
Packit 8fb591
    for (i = 0; i < lybs->used - 1; ++i) {
Packit 8fb591
        if (lybs->inner_chunks[i] == LYB_INCHUNK_MAX) {
Packit 8fb591
            LOGINT(NULL);
Packit 8fb591
            return -1;
Packit 8fb591
        }
Packit 8fb591
        ++lybs->inner_chunks[i];
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    return ly_write_skip(out, LYB_META_BYTES, &lybs->position[lybs->used - 1]);
Packit 8fb591
}
Packit 8fb591
Packit 8fb591
static int
Packit 8fb591
lyb_write_number(uint64_t num, size_t bytes, struct lyout *out, struct lyb_state *lybs)
Packit 8fb591
{
Packit 8fb591
    int ret = 0;
Packit 8fb591
    size_t i;
Packit 8fb591
    uint8_t byte;
Packit 8fb591
Packit 8fb591
    num = htole64(num);
Packit 8fb591
    for (i = 0; i < bytes; ++i) {
Packit 8fb591
        byte = *(((uint8_t *)&num) + i);
Packit 8fb591
        ret += lyb_write(out, &byte, 1, lybs);
Packit 8fb591
        if (ret < 0) {
Packit 8fb591
            break;
Packit 8fb591
        }
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    return ret;
Packit 8fb591
}
Packit 8fb591
Packit 8fb591
static int
Packit 8fb591
lyb_write_enum(uint32_t enum_idx, uint32_t count, struct lyout *out, struct lyb_state *lybs)
Packit 8fb591
{
Packit 8fb591
    size_t bytes;
Packit 8fb591
Packit 8fb591
    if (count < (1 << 8)) {
Packit 8fb591
        bytes = 1;
Packit 8fb591
    } else if (count < (1 << 16)) {
Packit 8fb591
        bytes = 2;
Packit 8fb591
    } else if (count < (1 << 24)) {
Packit 8fb591
        bytes = 3;
Packit 8fb591
    } else {
Packit 8fb591
        bytes = 4;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    return lyb_write_number(enum_idx, bytes, out, lybs);
Packit 8fb591
}
Packit 8fb591
Packit 8fb591
static int
Packit 8fb591
lyb_write_string(const char *str, size_t str_len, int with_length, struct lyout *out, struct lyb_state *lybs)
Packit 8fb591
{
Packit 8fb591
    int r, ret = 0;
Packit 8fb591
Packit 8fb591
    if (!str_len) {
Packit 8fb591
        str_len = strlen(str);
Packit 8fb591
    }
Packit 8fb591
    if (str_len > UINT16_MAX) {
Packit 8fb591
        LOGINT(NULL);
Packit 8fb591
        return -1;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    if (with_length) {
Packit 8fb591
        /* print length on 2 bytes */
Packit 8fb591
        ret += (r = lyb_write_number(str_len, 2, out, lybs));
Packit 8fb591
        if (r < 0) {
Packit 8fb591
            return -1;
Packit 8fb591
        }
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    ret += (r = lyb_write(out, (const uint8_t *)str, str_len, lybs));
Packit 8fb591
    if (r < 0) {
Packit 8fb591
        return -1;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    return ret;
Packit 8fb591
}
Packit 8fb591
Packit 8fb591
static int
Packit 8fb591
lyb_print_model(struct lyout *out, const struct lys_module *mod, struct lyb_state *lybs)
Packit 8fb591
{
Packit 8fb591
    int r, ret = 0;
Packit 8fb591
    uint16_t revision;
Packit 8fb591
Packit 8fb591
    /* model name length and model name */
Packit 8fb591
    ret += (r = lyb_write_string(mod->name, 0, 1, out, lybs));
Packit 8fb591
    if (r < 0) {
Packit 8fb591
        return -1;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    /* model revision as XXXX XXXX XXXX XXXX (2B) (year is offset from 2000)
Packit 8fb591
     *                   YYYY YYYM MMMD DDDD */
Packit 8fb591
    revision = 0;
Packit 8fb591
    if (mod->rev_size) {
Packit 8fb591
        r = atoi(mod->rev[0].date);
Packit 8fb591
        r -= 2000;
Packit 8fb591
        r <<= 9;
Packit 8fb591
Packit 8fb591
        revision |= r;
Packit 8fb591
Packit 8fb591
        r = atoi(mod->rev[0].date + 5);
Packit 8fb591
        r <<= 5;
Packit 8fb591
Packit 8fb591
        revision |= r;
Packit 8fb591
Packit 8fb591
        r = atoi(mod->rev[0].date + 8);
Packit 8fb591
Packit 8fb591
        revision |= r;
Packit 8fb591
    }
Packit 8fb591
    ret += (r = lyb_write_number(revision, sizeof revision, out, lybs));
Packit 8fb591
    if (r < 0) {
Packit 8fb591
        return -1;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    return ret;
Packit 8fb591
}
Packit 8fb591
Packit 8fb591
static int
Packit 8fb591
is_added_model(const struct lys_module **models, size_t mod_count, const struct lys_module *mod)
Packit 8fb591
{
Packit 8fb591
    size_t i;
Packit 8fb591
Packit 8fb591
    for (i = 0; i < mod_count; ++i) {
Packit 8fb591
        if (models[i] == mod) {
Packit 8fb591
            return 1;
Packit 8fb591
        }
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    return 0;
Packit 8fb591
}
Packit 8fb591
Packit 8fb591
static void
Packit 8fb591
add_model(const struct lys_module ***models, size_t *mod_count, const struct lys_module *mod)
Packit 8fb591
{
Packit 8fb591
    if (is_added_model(*models, *mod_count, mod)) {
Packit 8fb591
        return;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    *models = ly_realloc(*models, ++(*mod_count) * sizeof **models);
Packit 8fb591
    (*models)[*mod_count - 1] = mod;
Packit 8fb591
}
Packit 8fb591
Packit 8fb591
static int
Packit 8fb591
lyb_print_data_models(struct lyout *out, const struct lyd_node *root, struct lyb_state *lybs)
Packit 8fb591
{
Packit 8fb591
    int ret = 0;
Packit 8fb591
    const struct lys_module **models = NULL, *mod;
Packit 8fb591
    const struct lys_submodule *submod;
Packit 8fb591
    const struct lyd_node *node;
Packit 8fb591
    size_t mod_count = 0;
Packit 8fb591
    uint32_t idx = 0, i, j;
Packit 8fb591
Packit 8fb591
    /* first, collect all data node modules */
Packit 8fb591
    LY_TREE_FOR(root, node) {
Packit 8fb591
        mod = lyd_node_module(node);
Packit 8fb591
        add_model(&models, &mod_count, mod);
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    if (root) {
Packit 8fb591
        /* then add all models augmenting or deviating the used models */
Packit 8fb591
        idx = ly_ctx_internal_modules_count(root->schema->module->ctx);
Packit 8fb591
        while ((mod = ly_ctx_get_module_iter(root->schema->module->ctx, &idx))) {
Packit 8fb591
            if (!mod->implemented) {
Packit 8fb591
next_mod:
Packit 8fb591
                continue;
Packit 8fb591
            }
Packit 8fb591
Packit 8fb591
            for (i = 0; i < mod->deviation_size; ++i) {
Packit 8fb591
                if (mod->deviation[i].orig_node && is_added_model(models, mod_count, lys_node_module(mod->deviation[i].orig_node))) {
Packit 8fb591
                    add_model(&models, &mod_count, mod);
Packit 8fb591
                    goto next_mod;
Packit 8fb591
                }
Packit 8fb591
            }
Packit 8fb591
            for (i = 0; i < mod->augment_size; ++i) {
Packit 8fb591
                if (is_added_model(models, mod_count, lys_node_module(mod->augment[i].target))) {
Packit 8fb591
                    add_model(&models, &mod_count, mod);
Packit 8fb591
                    goto next_mod;
Packit 8fb591
                }
Packit 8fb591
            }
Packit 8fb591
Packit 8fb591
            /* submodules */
Packit 8fb591
            for (j = 0; j < mod->inc_size; ++j) {
Packit 8fb591
                submod = mod->inc[j].submodule;
Packit 8fb591
Packit 8fb591
                for (i = 0; i < submod->deviation_size; ++i) {
Packit 8fb591
                    if (submod->deviation[i].orig_node && is_added_model(models, mod_count, lys_node_module(submod->deviation[i].orig_node))) {
Packit 8fb591
                        add_model(&models, &mod_count, mod);
Packit 8fb591
                        goto next_mod;
Packit 8fb591
                    }
Packit 8fb591
                }
Packit 8fb591
                for (i = 0; i < submod->augment_size; ++i) {
Packit 8fb591
                    if (is_added_model(models, mod_count, lys_node_module(submod->augment[i].target))) {
Packit 8fb591
                        add_model(&models, &mod_count, mod);
Packit 8fb591
                        goto next_mod;
Packit 8fb591
                    }
Packit 8fb591
                }
Packit 8fb591
            }
Packit 8fb591
        }
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    /* now write module count on 2 bytes */
Packit 8fb591
    ret += lyb_write_number(mod_count, 2, out, lybs);
Packit 8fb591
Packit 8fb591
    /* and all the used models */
Packit 8fb591
    for (i = 0; i < mod_count; ++i) {
Packit 8fb591
        ret += lyb_print_model(out, models[i], lybs);
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    free(models);
Packit 8fb591
    return ret;
Packit 8fb591
}
Packit 8fb591
Packit 8fb591
static int
Packit 8fb591
lyb_print_magic_number(struct lyout *out)
Packit 8fb591
{
Packit 8fb591
    uint32_t magic_number;
Packit 8fb591
Packit 8fb591
    /* 'l', 'y', 'b' - 0x6c7962 */
Packit 8fb591
    ((char *)&magic_number)[0] = 'l';
Packit 8fb591
    ((char *)&magic_number)[1] = 'y';
Packit 8fb591
    ((char *)&magic_number)[2] = 'b';
Packit 8fb591
Packit 8fb591
    return ly_write(out, (char *)&magic_number, 3);
Packit 8fb591
}
Packit 8fb591
Packit 8fb591
static int
Packit 8fb591
lyb_print_header(struct lyout *out)
Packit 8fb591
{
Packit 8fb591
    int ret = 0;
Packit 8fb591
    uint8_t byte = 0;
Packit 8fb591
Packit 8fb591
    /* TODO version, some other flags? */
Packit 8fb591
    ret += ly_write(out, (char *)&byte, sizeof byte);
Packit 8fb591
Packit 8fb591
    return ret;
Packit 8fb591
}
Packit 8fb591
Packit 8fb591
static int
Packit 8fb591
lyb_print_anydata(struct lyd_node_anydata *anydata, struct lyout *out, struct lyb_state *lybs)
Packit 8fb591
{
Packit 8fb591
    int ret = 0, len;
Packit 8fb591
    char *buf;
Packit 8fb591
    LYD_ANYDATA_VALUETYPE type;
Packit 8fb591
Packit 8fb591
    if (anydata->value_type == LYD_ANYDATA_XML) {
Packit 8fb591
        /* transform XML into CONSTSTRING */
Packit 8fb591
        lyxml_print_mem(&buf, anydata->value.xml, LYXML_PRINT_SIBLINGS);
Packit 8fb591
        lyxml_free(anydata->schema->module->ctx, anydata->value.xml);
Packit 8fb591
Packit 8fb591
        anydata->value_type = LYD_ANYDATA_CONSTSTRING;
Packit 8fb591
        anydata->value.str = lydict_insert_zc(anydata->schema->module->ctx, buf);
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    if (anydata->value_type == LYD_ANYDATA_DATATREE) {
Packit 8fb591
        /* that is the format used */
Packit 8fb591
        type = LYD_ANYDATA_LYB;
Packit 8fb591
    } else if (anydata->value_type & LYD_ANYDATA_STRING) {
Packit 8fb591
        /* dynamic value, only used for input */
Packit 8fb591
        LOGERR(anydata->schema->module->ctx, LY_EINT, "Unsupported anydata value type to print.");
Packit 8fb591
        return -1;
Packit 8fb591
    } else {
Packit 8fb591
        type = anydata->value_type;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    /* first byte is type */
Packit 8fb591
    ret += lyb_write(out, (uint8_t *)&type, sizeof type, lybs);
Packit 8fb591
Packit 8fb591
    /* followed by the content */
Packit 8fb591
    if (anydata->value_type == LYD_ANYDATA_DATATREE) {
Packit 8fb591
        ret += lyb_print_data(out, anydata->value.tree, 0);
Packit 8fb591
    } else if (anydata->value_type == LYD_ANYDATA_LYB) {
Packit 8fb591
        len = lyd_lyb_data_length(anydata->value.mem);
Packit 8fb591
        if (len > -1) {
Packit 8fb591
            ret += lyb_write_string(anydata->value.str, (size_t)len, 0, out, lybs);
Packit 8fb591
        }
Packit 8fb591
    } else {
Packit 8fb591
        ret += lyb_write_string(anydata->value.str, 0, 0, out, lybs);
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    return ret;
Packit 8fb591
}
Packit 8fb591
Packit 8fb591
static int
Packit 8fb591
lyb_print_value(const struct lys_type *type, const char *value_str, lyd_val value, LY_DATA_TYPE value_type,
Packit 8fb591
                uint8_t value_flags, uint8_t dflt, struct lyout *out, struct lyb_state *lybs)
Packit 8fb591
{
Packit 8fb591
    int ret = 0;
Packit 8fb591
    uint8_t byte = 0;
Packit 8fb591
    size_t count, i, bits_i;
Packit 8fb591
    LY_DATA_TYPE dtype;
Packit 8fb591
Packit 8fb591
    /* value type byte - ABCD DDDD
Packit 8fb591
     *
Packit 8fb591
     * A - dflt flag
Packit 8fb591
     * B - user type flag
Packit 8fb591
     * C - unres flag
Packit 8fb591
     * D (5b) - data type value
Packit 8fb591
     */
Packit 8fb591
    if (dflt) {
Packit 8fb591
        byte |= 0x80;
Packit 8fb591
    }
Packit 8fb591
    if (value_flags & LY_VALUE_USER) {
Packit 8fb591
        byte |= 0x40;
Packit 8fb591
    }
Packit 8fb591
    if (value_flags & LY_VALUE_UNRES) {
Packit 8fb591
        byte |= 0x20;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    /* we have only 5b available, must be enough */
Packit 8fb591
    assert((value_type & 0x1f) == value_type);
Packit 8fb591
Packit 8fb591
    if ((value_flags & LY_VALUE_USER) || (type->base == LY_TYPE_UNION)) {
Packit 8fb591
        value_type = LY_TYPE_STRING;
Packit 8fb591
    } else if (value_type == LY_TYPE_LEAFREF) {
Packit 8fb591
        assert(!(value_flags & LY_VALUE_UNRES));
Packit 8fb591
        /* find the leafref target type */
Packit 8fb591
        while (type->base == LY_TYPE_LEAFREF) {
Packit 8fb591
            type = &type->info.lref.target->type;
Packit 8fb591
        }
Packit 8fb591
        value_type = type->base;
Packit 8fb591
Packit 8fb591
        /* and also use its value */
Packit 8fb591
        value = ((struct lyd_node_leaf_list *)value.leafref)->value;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    /* store the value type */
Packit 8fb591
    byte |= value_type & 0x1f;
Packit 8fb591
Packit 8fb591
    /* write value type byte */
Packit 8fb591
    ret += lyb_write(out, &byte, sizeof byte, lybs);
Packit 8fb591
Packit 8fb591
    /* print value itself */
Packit 8fb591
    if (value_flags & LY_VALUE_USER) {
Packit 8fb591
        dtype = LY_TYPE_STRING;
Packit 8fb591
    } else {
Packit 8fb591
        dtype = value_type;
Packit 8fb591
    }
Packit 8fb591
    switch (dtype) {
Packit 8fb591
    case LY_TYPE_BINARY:
Packit 8fb591
    case LY_TYPE_INST:
Packit 8fb591
    case LY_TYPE_STRING:
Packit 8fb591
    case LY_TYPE_UNION:
Packit 8fb591
    case LY_TYPE_IDENT:
Packit 8fb591
    case LY_TYPE_UNKNOWN:
Packit 8fb591
        /* store string */
Packit 8fb591
        ret += lyb_write_string(value_str, 0, 0, out, lybs);
Packit 8fb591
        break;
Packit 8fb591
    case LY_TYPE_BITS:
Packit 8fb591
        /* find the correct structure */
Packit 8fb591
        for (; !type->info.bits.count; type = &type->der->type);
Packit 8fb591
Packit 8fb591
        /* store a bitfield */
Packit 8fb591
        bits_i = 0;
Packit 8fb591
Packit 8fb591
        for (count = type->info.bits.count / 8; count; --count) {
Packit 8fb591
            /* will be a full byte */
Packit 8fb591
            for (byte = 0, i = 0; i < 8; ++i) {
Packit 8fb591
                if (value.bit[bits_i + i]) {
Packit 8fb591
                    byte |= (1 << i);
Packit 8fb591
                }
Packit 8fb591
            }
Packit 8fb591
            ret += lyb_write(out, &byte, sizeof byte, lybs);
Packit 8fb591
            bits_i += 8;
Packit 8fb591
        }
Packit 8fb591
Packit 8fb591
        /* store the remainder */
Packit 8fb591
        if (type->info.bits.count % 8) {
Packit 8fb591
            for (byte = 0, i = 0; i < type->info.bits.count % 8; ++i) {
Packit 8fb591
                if (value.bit[bits_i + i]) {
Packit 8fb591
                    byte |= (1 << i);
Packit 8fb591
                }
Packit 8fb591
            }
Packit 8fb591
            ret += lyb_write(out, &byte, sizeof byte, lybs);
Packit 8fb591
        }
Packit 8fb591
        break;
Packit 8fb591
    case LY_TYPE_BOOL:
Packit 8fb591
        /* store the whole byte */
Packit 8fb591
        byte = 0;
Packit 8fb591
        if (value.bln) {
Packit 8fb591
            byte = 1;
Packit 8fb591
        }
Packit 8fb591
        ret += lyb_write(out, &byte, sizeof byte, lybs);
Packit 8fb591
        break;
Packit 8fb591
    case LY_TYPE_EMPTY:
Packit 8fb591
        /* nothing to store */
Packit 8fb591
        break;
Packit 8fb591
    case LY_TYPE_ENUM:
Packit 8fb591
        /* find the correct structure */
Packit 8fb591
        for (; !type->info.enums.count; type = &type->der->type);
Packit 8fb591
Packit 8fb591
        /* store the enum index (save bytes if possible) */
Packit 8fb591
        i = value.enm - type->info.enums.enm;
Packit 8fb591
        ret += lyb_write_enum(i, type->info.enums.count, out, lybs);
Packit 8fb591
        break;
Packit 8fb591
    case LY_TYPE_INT8:
Packit 8fb591
    case LY_TYPE_UINT8:
Packit 8fb591
        ret += lyb_write_number(value.uint8, 1, out, lybs);
Packit 8fb591
        break;
Packit 8fb591
    case LY_TYPE_INT16:
Packit 8fb591
    case LY_TYPE_UINT16:
Packit 8fb591
        ret += lyb_write_number(value.uint16, 2, out, lybs);
Packit 8fb591
        break;
Packit 8fb591
    case LY_TYPE_INT32:
Packit 8fb591
    case LY_TYPE_UINT32:
Packit 8fb591
        ret += lyb_write_number(value.uint32, 4, out, lybs);
Packit 8fb591
        break;
Packit 8fb591
    case LY_TYPE_DEC64:
Packit 8fb591
    case LY_TYPE_INT64:
Packit 8fb591
    case LY_TYPE_UINT64:
Packit 8fb591
        ret += lyb_write_number(value.uint64, 8, out, lybs);
Packit 8fb591
        break;
Packit 8fb591
    default:
Packit 8fb591
        return 0;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    return ret;
Packit 8fb591
}
Packit 8fb591
Packit 8fb591
static int
Packit 8fb591
lyb_print_attributes(struct lyout *out, struct lyd_attr *attr, struct lyb_state *lybs)
Packit 8fb591
{
Packit 8fb591
    int r, ret = 0;
Packit 8fb591
    uint8_t count;
Packit 8fb591
    struct lyd_attr *iter;
Packit 8fb591
    struct lys_type **type;
Packit 8fb591
Packit 8fb591
    /* count attributes */
Packit 8fb591
    for (count = 0, iter = attr; iter; ++count, iter = iter->next) {
Packit 8fb591
        if (count == UINT8_MAX) {
Packit 8fb591
            LOGERR(NULL, LY_EINT, "Maximum supported number of data node attributes is %u.", UINT8_MAX);
Packit 8fb591
            return -1;
Packit 8fb591
        }
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    /* write number of attributes on 1 byte */
Packit 8fb591
    ret += (r = lyb_write(out, &count, 1, lybs));
Packit 8fb591
    if (r < 0) {
Packit 8fb591
        return -1;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    /* write all the attributes */
Packit 8fb591
    LY_TREE_FOR(attr, iter) {
Packit 8fb591
        /* each attribute is a subtree */
Packit 8fb591
        ret += (r = lyb_write_start_subtree(out, lybs));
Packit 8fb591
        if (r < 0) {
Packit 8fb591
            return -1;
Packit 8fb591
        }
Packit 8fb591
Packit 8fb591
        /* model */
Packit 8fb591
        ret += (r = lyb_print_model(out, iter->annotation->module, lybs));
Packit 8fb591
        if (r < 0) {
Packit 8fb591
            return -1;
Packit 8fb591
        }
Packit 8fb591
Packit 8fb591
        /* annotation name with length */
Packit 8fb591
        ret += (r = lyb_write_string(iter->annotation->arg_value, 0, 1, out, lybs));
Packit 8fb591
        if (r < 0) {
Packit 8fb591
            return -1;
Packit 8fb591
        }
Packit 8fb591
Packit 8fb591
        /* get the type */
Packit 8fb591
        type = (struct lys_type **)lys_ext_complex_get_substmt(LY_STMT_TYPE, iter->annotation, NULL);
Packit 8fb591
        if (!type || !(*type)) {
Packit 8fb591
            return -1;
Packit 8fb591
        }
Packit 8fb591
Packit 8fb591
        /* attribute value */
Packit 8fb591
        ret += (r = lyb_print_value(*type, iter->value_str, iter->value, iter->value_type, iter->value_flags, 0, out, lybs));
Packit 8fb591
        if (r < 0) {
Packit 8fb591
            return -1;
Packit 8fb591
        }
Packit 8fb591
Packit 8fb591
        /* finish attribute subtree */
Packit 8fb591
        ret += (r = lyb_write_stop_subtree(out, lybs));
Packit 8fb591
        if (r < 0) {
Packit 8fb591
            return -1;
Packit 8fb591
        }
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    return ret;
Packit 8fb591
}
Packit 8fb591
Packit 8fb591
static int
Packit 8fb591
lyb_print_schema_hash(struct lyout *out, struct lys_node *schema, struct hash_table **sibling_ht, struct lyb_state *lybs,
Packit 8fb591
                      int options)
Packit 8fb591
{
Packit 8fb591
    int r, ret = 0;
Packit 8fb591
    void *mem;
Packit 8fb591
    uint32_t i;
Packit 8fb591
    LYB_HASH hash;
Packit 8fb591
    struct lys_node *first_sibling, *parent, *iter;
Packit 8fb591
Packit 8fb591
    /* create whole sibling HT if not already created and saved */
Packit 8fb591
    if (!*sibling_ht) {
Packit 8fb591
        /* get first schema data sibling */
Packit 8fb591
        for (parent = lys_parent(schema);
Packit 8fb591
             parent && (parent->nodetype & (LYS_USES | LYS_CASE | LYS_CHOICE | LYS_INPUT | LYS_OUTPUT));
Packit 8fb591
             parent = lys_parent(parent)) {
Packit 8fb591
Packit 8fb591
            /* we have checked this before */
Packit 8fb591
            assert(!(options & LYD_OPT_RPC) || (parent->nodetype != LYS_OUTPUT));
Packit 8fb591
            assert(!(options & LYD_OPT_RPCREPLY) || (parent->nodetype != LYS_INPUT));
Packit 8fb591
        }
Packit 8fb591
Packit 8fb591
        first_sibling = (struct lys_node *)lys_getnext(NULL, parent, lys_node_module(schema), 0);
Packit 8fb591
        if (options & (LYD_OPT_RPC | LYD_OPT_RPCREPLY)) {
Packit 8fb591
check_inout:
Packit 8fb591
            for (iter = lys_parent(first_sibling);
Packit 8fb591
                 iter && (iter->nodetype & (LYS_USES | LYS_CASE | LYS_CHOICE));
Packit 8fb591
                 iter = lys_parent(iter));
Packit 8fb591
Packit 8fb591
            if (iter && (((options & LYD_OPT_RPC) && (iter->nodetype == LYS_OUTPUT))
Packit 8fb591
                    || ((options & LYD_OPT_RPCREPLY) && (iter->nodetype == LYS_INPUT)))) {
Packit 8fb591
                first_sibling = (struct lys_node *)lys_getnext(first_sibling, NULL, NULL, 0);
Packit 8fb591
                goto check_inout;
Packit 8fb591
            }
Packit 8fb591
        }
Packit 8fb591
Packit 8fb591
        for (r = 0; r < lybs->sib_ht_count; ++r) {
Packit 8fb591
            if (lybs->sib_ht[r].first_sibling == first_sibling) {
Packit 8fb591
                /* we have already created a hash table for these siblings */
Packit 8fb591
                *sibling_ht = lybs->sib_ht[r].ht;
Packit 8fb591
                break;
Packit 8fb591
            }
Packit 8fb591
        }
Packit 8fb591
Packit 8fb591
        if (!*sibling_ht) {
Packit 8fb591
            /* we must create sibling hash table */
Packit 8fb591
            *sibling_ht = lyb_hash_siblings(first_sibling, NULL, 0, options);
Packit 8fb591
            if (!*sibling_ht) {
Packit 8fb591
                return -1;
Packit 8fb591
            }
Packit 8fb591
Packit 8fb591
            /* and save it */
Packit 8fb591
            ++lybs->sib_ht_count;
Packit 8fb591
            mem = realloc(lybs->sib_ht, lybs->sib_ht_count * sizeof *lybs->sib_ht);
Packit 8fb591
            LY_CHECK_ERR_RETURN(!mem, LOGMEM(schema->module->ctx), -1);
Packit 8fb591
            lybs->sib_ht = mem;
Packit 8fb591
Packit 8fb591
            lybs->sib_ht[lybs->sib_ht_count - 1].first_sibling = first_sibling;
Packit 8fb591
            lybs->sib_ht[lybs->sib_ht_count - 1].ht = *sibling_ht;
Packit 8fb591
        }
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    /* get our hash */
Packit 8fb591
    hash = lyb_hash_find(*sibling_ht, schema);
Packit 8fb591
    if (!hash) {
Packit 8fb591
        return -1;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    /* write the hash */
Packit 8fb591
    ret += (r = lyb_write(out, &hash, sizeof hash, lybs));
Packit 8fb591
    if (r < 0) {
Packit 8fb591
        return -1;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    if (hash & LYB_HASH_COLLISION_ID) {
Packit 8fb591
        /* no collision for this hash, we are done */
Packit 8fb591
        return ret;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    /* written hash was a collision, write also all the preceding hashes */
Packit 8fb591
    for (i = 0; !(hash & (LYB_HASH_COLLISION_ID >> i)); ++i);
Packit 8fb591
Packit 8fb591
    for (; i; --i) {
Packit 8fb591
        hash = lyb_hash(schema, i - 1);
Packit 8fb591
        if (!hash) {
Packit 8fb591
            return -1;
Packit 8fb591
        }
Packit 8fb591
        assert(hash & (LYB_HASH_COLLISION_ID >> (i - 1)));
Packit 8fb591
Packit 8fb591
        ret += (r = lyb_write(out, &hash, sizeof hash, lybs));
Packit 8fb591
        if (r < 0) {
Packit 8fb591
            return -1;
Packit 8fb591
        }
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    return ret;
Packit 8fb591
}
Packit 8fb591
Packit 8fb591
static int
Packit 8fb591
lyb_print_subtree(struct lyout *out, const struct lyd_node *node, struct hash_table **sibling_ht, struct lyb_state *lybs,
Packit 8fb591
                  int options, int top_level)
Packit 8fb591
{
Packit 8fb591
    int r, ret = 0;
Packit 8fb591
    struct lyd_node_leaf_list *leaf;
Packit 8fb591
    struct lys_node *sparent;
Packit 8fb591
    struct hash_table *child_ht = NULL;
Packit 8fb591
Packit 8fb591
    /* skip nodes that should not be printed */
Packit 8fb591
    if (options & (LYD_OPT_RPC | LYD_OPT_RPCREPLY)) {
Packit 8fb591
        for (sparent = lys_parent(node->schema);
Packit 8fb591
            sparent && (sparent->nodetype & (LYS_USES | LYS_CASE | LYS_CHOICE));
Packit 8fb591
            sparent = lys_parent(sparent));
Packit 8fb591
Packit 8fb591
        if (sparent) {
Packit 8fb591
            if ((options & LYD_OPT_RPC) && (sparent->nodetype == LYS_OUTPUT)) {
Packit 8fb591
                return 0;
Packit 8fb591
            }
Packit 8fb591
            if ((options & LYD_OPT_RPCREPLY) && (sparent->nodetype == LYS_INPUT)) {
Packit 8fb591
                return 0;
Packit 8fb591
            }
Packit 8fb591
        }
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    /* register a new subtree */
Packit 8fb591
    ret += (r = lyb_write_start_subtree(out, lybs));
Packit 8fb591
    if (r < 0) {
Packit 8fb591
        return -1;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    /*
Packit 8fb591
     * write the node information
Packit 8fb591
     */
Packit 8fb591
    if (top_level) {
Packit 8fb591
        /* write model info first */
Packit 8fb591
        ret += (r = lyb_print_model(out, lyd_node_module(node), lybs));
Packit 8fb591
        if (r < 0) {
Packit 8fb591
            return -1;
Packit 8fb591
        }
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    ret += (r = lyb_print_schema_hash(out, node->schema, sibling_ht, lybs, options));
Packit 8fb591
    if (r < 0) {
Packit 8fb591
        return -1;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    ret += (r = lyb_print_attributes(out, node->attr, lybs));
Packit 8fb591
    if (r < 0) {
Packit 8fb591
        return -1;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    /* write node content */
Packit 8fb591
    switch (node->schema->nodetype) {
Packit 8fb591
    case LYS_CONTAINER:
Packit 8fb591
    case LYS_LIST:
Packit 8fb591
    case LYS_NOTIF:
Packit 8fb591
    case LYS_RPC:
Packit 8fb591
    case LYS_ACTION:
Packit 8fb591
        /* nothing to write */
Packit 8fb591
        break;
Packit 8fb591
    case LYS_LEAF:
Packit 8fb591
    case LYS_LEAFLIST:
Packit 8fb591
        leaf = (struct lyd_node_leaf_list *)node;
Packit 8fb591
        ret += (r = lyb_print_value(&((struct lys_node_leaf *)leaf->schema)->type, leaf->value_str, leaf->value,
Packit 8fb591
                                    leaf->value_type, leaf->value_flags, leaf->dflt, out, lybs));
Packit 8fb591
        if (r < 0) {
Packit 8fb591
            return -1;
Packit 8fb591
        }
Packit 8fb591
        break;
Packit 8fb591
    case LYS_ANYXML:
Packit 8fb591
    case LYS_ANYDATA:
Packit 8fb591
        ret += (r = lyb_print_anydata((struct lyd_node_anydata *)node, out, lybs));
Packit 8fb591
        if (r < 0) {
Packit 8fb591
            return -1;
Packit 8fb591
        }
Packit 8fb591
        break;
Packit 8fb591
    default:
Packit 8fb591
        return -1;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    /* recursively write all the descendants */
Packit 8fb591
    r = 0;
Packit 8fb591
    if (node->schema->nodetype & (LYS_CONTAINER | LYS_LIST | LYS_NOTIF | LYS_RPC | LYS_ACTION)) {
Packit 8fb591
        LY_TREE_FOR(node->child, node) {
Packit 8fb591
            ret += (r = lyb_print_subtree(out, node, &child_ht, lybs, options, 0));
Packit 8fb591
            if (r < 0) {
Packit 8fb591
                break;
Packit 8fb591
            }
Packit 8fb591
        }
Packit 8fb591
    }
Packit 8fb591
    if (r < 0) {
Packit 8fb591
        return -1;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    /* finish this subtree */
Packit 8fb591
    ret += (r = lyb_write_stop_subtree(out, lybs));
Packit 8fb591
    if (r < 0) {
Packit 8fb591
        return -1;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    return ret;
Packit 8fb591
}
Packit 8fb591
Packit 8fb591
int
Packit 8fb591
lyb_print_data(struct lyout *out, const struct lyd_node *root, int options)
Packit 8fb591
{
Packit 8fb591
    int r, ret = 0, rc = EXIT_SUCCESS;
Packit 8fb591
    uint8_t zero = 0;
Packit 8fb591
    struct hash_table *top_sibling_ht = NULL;
Packit 8fb591
    const struct lys_module *prev_mod = NULL;
Packit 8fb591
    struct lyb_state lybs;
Packit 8fb591
Packit 8fb591
    memset(&lybs, 0, sizeof lybs);
Packit 8fb591
Packit 8fb591
    /* LYB magic number */
Packit 8fb591
    ret += (r = lyb_print_magic_number(out));
Packit 8fb591
    if (r < 0) {
Packit 8fb591
        rc = EXIT_FAILURE;
Packit 8fb591
        goto finish;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    /* LYB header */
Packit 8fb591
    ret += (r = lyb_print_header(out));
Packit 8fb591
    if (r < 0) {
Packit 8fb591
        rc = EXIT_FAILURE;
Packit 8fb591
        goto finish;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    /* all used models */
Packit 8fb591
    ret += (r = lyb_print_data_models(out, root, &lybs));
Packit 8fb591
    if (r < 0) {
Packit 8fb591
        rc = EXIT_FAILURE;
Packit 8fb591
        goto finish;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    LY_TREE_FOR(root, root) {
Packit 8fb591
        /* do not reuse sibling hash tables from different modules */
Packit 8fb591
        if (lyd_node_module(root) != prev_mod) {
Packit 8fb591
            top_sibling_ht = NULL;
Packit 8fb591
            prev_mod = lyd_node_module(root);
Packit 8fb591
        }
Packit 8fb591
Packit 8fb591
        ret += (r = lyb_print_subtree(out, root, &top_sibling_ht, &lybs, options, 1));
Packit 8fb591
        if (r < 0) {
Packit 8fb591
            rc = EXIT_FAILURE;
Packit 8fb591
            goto finish;
Packit 8fb591
        }
Packit 8fb591
Packit 8fb591
        if (!(options & LYP_WITHSIBLINGS)) {
Packit 8fb591
            break;
Packit 8fb591
        }
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
    /* ending zero byte */
Packit 8fb591
    ret += (r = lyb_write(out, &zero, sizeof zero, &lybs));
Packit 8fb591
    if (r < 0) {
Packit 8fb591
        rc = EXIT_FAILURE;
Packit 8fb591
    }
Packit 8fb591
Packit 8fb591
finish:
Packit 8fb591
    free(lybs.written);
Packit 8fb591
    free(lybs.position);
Packit 8fb591
    free(lybs.inner_chunks);
Packit 8fb591
    for (r = 0; r < lybs.sib_ht_count; ++r) {
Packit 8fb591
        lyht_free(lybs.sib_ht[r].ht);
Packit 8fb591
    }
Packit 8fb591
    free(lybs.sib_ht);
Packit 8fb591
Packit 8fb591
    return rc;
Packit 8fb591
}