blob: 0a85f569bf8b8eac6ca2fd5ad3cc42e9dd84b7ff [file] [log] [blame]
/**
* @file parser_lyb.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief LYB data parser for libyang
*
* Copyright (c) 2018 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "libyang.h"
#include "common.h"
#include "context.h"
#include "parser.h"
#include "tree_internal.h"
#define LYB_HAVE_READ_GOTO(r, d, go) if (r < 0) goto go; d += r;
#define LYB_HAVE_READ_RETURN(r, d, ret) if (r < 0) return ret; d += r;
static int
lyb_read(const char *data, uint8_t *buf, size_t count, struct lyb_state *lybs)
{
int ret = 0, i, empty_chunk_i;
size_t to_read;
assert(data && lybs);
while (1) {
/* check for fully-read (empty) data chunks */
to_read = count;
empty_chunk_i = -1;
for (i = 0; i < lybs->used; ++i) {
/* we want the innermost chunks resolved first, so replace previous empty chunks,
* also ignore chunks that are completely finished, there is nothing for us to do */
if ((lybs->written[i] <= count) && lybs->position[i]) {
/* empty chunk, do not read more */
to_read = lybs->written[i];
empty_chunk_i = i;
}
}
if ((empty_chunk_i == -1) && !count) {
break;
}
/* we are actually reading some data, not just finishing another chunk */
if (to_read) {
memcpy(buf, data + ret, to_read);
for (i = 0; i < lybs->used; ++i) {
/* decrease all written counters */
lybs->written[i] -= to_read;
}
/* decrease count/buf */
count -= to_read;
buf += to_read;
ret += to_read;
}
if (empty_chunk_i > -1) {
/* read the next chunk size */
memcpy(&lybs->written[empty_chunk_i], data + ret, LYB_SIZE_BYTES);
/* remember whether there is a following chunk or not */
lybs->position[empty_chunk_i] = (lybs->written[empty_chunk_i] == LYB_SIZE_MAX ? 1 : 0);
ret += LYB_SIZE_BYTES;
}
}
return ret;
}
static int
lyb_read_number(uint64_t *num, uint64_t max_num, const char *data, struct lyb_state *lybs)
{
int max_bits, max_bytes, i, r, ret = 0;
for (max_bits = 0; max_num; max_num >>= 1, ++max_bits);
max_bytes = max_bits / 8 + (max_bits % 8 ? 1 : 0);
*num = 0;
for (i = 0; i < max_bytes; ++i) {
*num <<= 8;
ret += (r = lyb_read(data, (uint8_t *)num, 1, lybs));
LYB_HAVE_READ_RETURN(r, data, -1);
}
return ret;
}
static int
lyb_read_string(struct ly_ctx *ctx, const char **str, const char *data, struct lyb_state *lybs)
{
int ret;
size_t len;
/* read until the end of this subtree */
len = lybs->written[lybs->used - 1];
*str = malloc((len + 1) * sizeof **str);
LY_CHECK_ERR_RETURN(!*str, LOGMEM(ctx), -1);
ret = lyb_read(data, (uint8_t *)*str, len, lybs);
LYB_HAVE_READ_GOTO(ret, data, error);
((char *)*str)[len] = '\0';
/* store in the dictionary */
*str = lydict_insert_zc(ctx, (char *)*str);
return ret;
error:
free((char *)*str);
*str = NULL;
return -1;
}
static void
lyb_read_stop_subtree(struct lyb_state *lybs)
{
if (lybs->written[lybs->used - 1]) {
LOGINT(NULL);
}
--lybs->used;
}
static int
lyb_read_start_subtree(const char *data, struct lyb_state *lybs)
{
uint64_t num = 0;
if (lybs->used == lybs->size) {
lybs->size += LYB_STATE_STEP;
lybs->written = ly_realloc(lybs->written, lybs->size * sizeof *lybs->written);
lybs->position = ly_realloc(lybs->position, lybs->size * sizeof *lybs->position);
LY_CHECK_ERR_RETURN(!lybs->written || !lybs->position, LOGMEM(NULL), -1);
}
memcpy(&num, data, LYB_SIZE_BYTES);
++lybs->used;
lybs->written[lybs->used - 1] = num;
lybs->position[lybs->used - 1] = (num == LYB_SIZE_MAX ? 1 : 0);
return LYB_SIZE_BYTES;
}
static int
lyb_parse_model(struct ly_ctx *ctx, const char *data, const struct lys_module **mod, struct lyb_state *lybs)
{
int r, ret = 0;
uint16_t num = 0;
char *mod_name = NULL, mod_rev[11];
/* model name length */
ret += (r = lyb_read(data, (uint8_t *)&num, sizeof(uint16_t), lybs));
LYB_HAVE_READ_GOTO(r, data, error);
mod_name = malloc(num + 1);
LY_CHECK_ERR_GOTO(!mod_name, LOGMEM(ctx), error);
/* model name */
ret += (r = lyb_read(data, (uint8_t *)mod_name, num, lybs));
LYB_HAVE_READ_GOTO(r, data, error);
mod_name[num] = '\0';
/* revision */
ret += (r = lyb_read(data, (uint8_t *)&num, sizeof(uint16_t), lybs));
LYB_HAVE_READ_GOTO(r, data, error);
if (num) {
sprintf(mod_rev, "%04u-%02u-%02u", ((num & 0xFE00) >> 9) + 2000, (num & 0x01E0) >> 5, (num & 0x001F));
*mod = ly_ctx_get_module(ctx, mod_name, mod_rev, 0);
} else {
*mod = ly_ctx_get_module(ctx, mod_name, NULL, 0);
}
if (!*mod) {
LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Module \"%s@%s\" not found in the context.", mod_name, (num ? mod_rev : "<none>"));
goto error;
}
free(mod_name);
return ret;
error:
free(mod_name);
return -1;
}
static struct lyd_node *
lyb_new_node(const struct lys_node *schema)
{
struct lyd_node *node;
switch (schema->nodetype) {
case LYS_CONTAINER:
case LYS_LIST:
case LYS_NOTIF:
case LYS_RPC:
case LYS_ACTION:
node = calloc(sizeof(struct lyd_node), 1);
break;
case LYS_LEAF:
case LYS_LEAFLIST:
node = calloc(sizeof(struct lyd_node_leaf_list), 1);
break;
case LYS_ANYDATA:
case LYS_ANYXML:
node = calloc(sizeof(struct lyd_node_anydata), 1);
break;
default:
return NULL;
}
LY_CHECK_ERR_RETURN(!node, LOGMEM(schema->module->ctx), NULL);
/* fill basic info */
node->schema = (struct lys_node *)schema;
node->validity = ly_new_node_validity(schema);
if (resolve_applies_when(schema, 0, NULL)) {
node->when_status = LYD_WHEN;
}
node->prev = node;
return node;
}
static int
lyb_parse_anydata(struct lyd_node *node, const char *data, struct lyb_state *lybs)
{
int r, ret = 0;
struct lyd_node_anydata *any = (struct lyd_node_anydata *)node;
/* read value type */
ret += (r = lyb_read(data, (uint8_t *)&any->value_type, sizeof any->value_type, lybs));
LYB_HAVE_READ_RETURN(r, data, -1);
/* read anydata content */
if (any->value_type == LYD_ANYDATA_DATATREE) {
any->value.tree = lyd_parse_lyb(node->schema->module->ctx, data, 0, NULL, NULL, NULL, &r);
ret += r;
LYB_HAVE_READ_RETURN(r, data, -1);
} else {
ret += (r = lyb_read_string(node->schema->module->ctx, &any->value.str, data, lybs));
LYB_HAVE_READ_RETURN(r, data, -1);
}
return ret;
}
static int
lyb_parse_val(struct lyd_node_leaf_list *node, const char *data, struct lyb_state *lybs)
{
int ret;
uint8_t byte;
struct ly_ctx *ctx = node->schema->module->ctx;
struct lys_type *type = &((struct lys_node_leaf *)node->schema)->type;
if (node->value_flags & (LY_VALUE_USER || LY_VALUE_UNRES)) {
/* just read value_str */
return lyb_read_string(ctx, &node->value_str, data, lybs);
}
switch (node->value_type) {
case LY_TYPE_INST:
case LY_TYPE_IDENT:
case LY_TYPE_UNION:
/* we do not actually fill value now, but value_str */
ret = lyb_read_string(ctx, &node->value_str, data, lybs);
break;
case LY_TYPE_BINARY:
case LY_TYPE_STRING:
case LY_TYPE_UNKNOWN:
/* read string */
ret = lyb_read_string(ctx, &node->value.string, data, lybs);
break;
case LY_TYPE_BITS:
/* find the correct structure */
for (; !type->info.bits.count; type = &type->der->type);
node->value.bit = calloc(type->info.bits.count, sizeof *node->value.bit);
LY_CHECK_ERR_RETURN(!node->value.bit, LOGMEM(ctx), -1);
/* read values */
/* TODO */
break;
case LY_TYPE_BOOL:
/* read byte */
ret = lyb_read(data, &byte, sizeof byte, lybs);
if ((ret > 0) && byte) {
node->value.bln = 1;
}
break;
case LY_TYPE_EMPTY:
/* nothing to read */
ret = 0;
break;
case LY_TYPE_ENUM:
/* find the correct structure */
for (; !type->info.bits.count; type = &type->der->type);
/* TODO */
break;
case LY_TYPE_INT8:
case LY_TYPE_UINT8:
ret = lyb_read_number((uint64_t *)&node->value.uint8, UINT8_MAX, data, lybs);
break;
case LY_TYPE_INT16:
case LY_TYPE_UINT16:
ret = lyb_read_number((uint64_t *)&node->value.uint16, UINT16_MAX, data, lybs);
break;
case LY_TYPE_INT32:
case LY_TYPE_UINT32:
ret = lyb_read_number((uint64_t *)&node->value.uint32, UINT32_MAX, data, lybs);
break;
case LY_TYPE_DEC64:
case LY_TYPE_INT64:
case LY_TYPE_UINT64:
ret = lyb_read_number((uint64_t *)&node->value.uint64, UINT64_MAX, data, lybs);
break;
default:
return -1;
}
return ret;
}
static int
lyb_parse_val_str(struct lyd_node_leaf_list *node)
{
struct ly_ctx *ctx = node->schema->module->ctx;
struct lys_type *type = &((struct lys_node_leaf *)node->schema)->type;
char num_str[21];
if (node->value_flags & LY_VALUE_UNRES) {
/* nothing to do */
return 0;
}
if (node->value_flags & LY_VALUE_USER) {
/* unfortunately, we need to also fill the value properly, so just parse it again */
node->value_flags &= ~LY_VALUE_USER;
if (!lyp_parse_value(type, &node->value_str, NULL, node, NULL,
lyd_node_module((struct lyd_node *)node), 1, node->dflt, 1)) {
return -1;
}
if (!(node->value_flags & LY_VALUE_USER)) {
LOGWRN(ctx, "Node \"%s\" value was stored as a user type, but it is not in the current context.", node->schema->name);
}
return 0;
}
switch (node->value_type) {
case LY_TYPE_INST:
/* fill the instance-identifier target now */
/* TODO */
break;
case LY_TYPE_IDENT:
/* fill the identity pointer now */
node->value.ident = resolve_identref(type, node->value_str, (struct lyd_node *)node, node->schema->module, node->dflt);
if (!node->value.ident) {
return -1;
}
break;
case LY_TYPE_BINARY:
case LY_TYPE_STRING:
case LY_TYPE_UNKNOWN:
/* just re-assign it */
node->value_str = node->value.string;
break;
case LY_TYPE_BITS:
/* TODO */
break;
case LY_TYPE_BOOL:
node->value_str = lydict_insert(ctx, (node->value.bln ? "true" : "false"), 0);
break;
case LY_TYPE_EMPTY:
case LY_TYPE_UNION:
/* leave value empty */
break;
case LY_TYPE_ENUM:
/* TODO */
break;
case LY_TYPE_INT8:
sprintf(num_str, "%d", node->value.int8);
node->value_str = lydict_insert(ctx, num_str, 0);
break;
case LY_TYPE_UINT8:
sprintf(num_str, "%u", node->value.uint8);
node->value_str = lydict_insert(ctx, num_str, 0);
break;
case LY_TYPE_INT16:
sprintf(num_str, "%d", node->value.int16);
node->value_str = lydict_insert(ctx, num_str, 0);
break;
case LY_TYPE_UINT16:
sprintf(num_str, "%u", node->value.uint16);
node->value_str = lydict_insert(ctx, num_str, 0);
break;
case LY_TYPE_INT32:
sprintf(num_str, "%d", node->value.int32);
node->value_str = lydict_insert(ctx, num_str, 0);
break;
case LY_TYPE_UINT32:
sprintf(num_str, "%u", node->value.uint32);
node->value_str = lydict_insert(ctx, num_str, 0);
break;
case LY_TYPE_INT64:
sprintf(num_str, "%ld", node->value.int64);
node->value_str = lydict_insert(ctx, num_str, 0);
break;
case LY_TYPE_UINT64:
sprintf(num_str, "%lu", node->value.uint64);
node->value_str = lydict_insert(ctx, num_str, 0);
break;
case LY_TYPE_DEC64:
/* TODO */
break;
default:
return -1;
}
return 0;
}
static int
lyb_parse_leaf(struct lyd_node *node, const char *data, struct lyb_state *lybs)
{
int r, ret = 0;
uint8_t start_byte;
struct lyd_node_leaf_list *leaf = (struct lyd_node_leaf_list *)node;
/* read value type and flags on the first byte */
ret += (r = lyb_read(data, &start_byte, sizeof start_byte, lybs));
LYB_HAVE_READ_RETURN(r, data, -1);
/* fill value type, flags */
leaf->value_type = start_byte & 0x1F;
if (start_byte & 0x80) {
leaf->dflt = 1;
}
if (start_byte & 0x40) {
leaf->value_flags |= LY_VALUE_USER;
}
if (start_byte & 0x20) {
leaf->value_flags |= LY_VALUE_UNRES;
}
ret += (r = lyb_parse_val(leaf, data, lybs));
LYB_HAVE_READ_RETURN(r, data, -1);
ret += (r = lyb_parse_val_str(leaf));
LYB_HAVE_READ_RETURN(r, data, -1);
return ret;
}
static int
lyb_parse_schema_hash(const struct lys_node *sparent, const struct lys_module *mod, const char *data,
struct hash_table **sibling_ht, struct lys_node **snode, struct lyb_state *lybs)
{
int r, ret = 0;
const struct lys_node *sibling = NULL;
LYB_HASH hash, cur_hash;
struct ly_ctx *ctx;
assert((sparent || mod) && (!sparent || !mod));
ctx = (sparent ? sparent->module->ctx : mod->ctx);
/* read the hash */
ret += (r = lyb_read(data, &hash, sizeof hash, lybs));
LYB_HAVE_READ_RETURN(r, data, -1);
while ((sibling = lys_getnext(sibling, sparent, mod, 0))) {
/* make sure hashes are ready and get the current one */
cur_hash = 0;
#ifdef LY_ENABLED_CACHE
if (sibling->hash) {
cur_hash = sibling->hash;
} else
#endif
if (!*sibling_ht) {
*sibling_ht = lyb_hash_siblings((struct lys_node *)sibling);
if (!*sibling_ht) {
return -1;
}
}
if (!cur_hash) {
cur_hash = lyb_hash_find(*sibling_ht, sibling);
if (!cur_hash) {
return -1;
}
}
if (hash == cur_hash) {
/* match found */
*snode = (struct lys_node *)sibling;
break;
}
}
if (!sibling) {
if (mod) {
LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Failed to find matching hash for a top-level node from \"%s\".", mod->name);
} else {
LOGVAL(ctx, LYE_SPEC, LY_VLOG_LYS, sparent, "Failed to find matching hash for a child of \"%s\".", sparent->name);
}
return -1;
}
return ret;
}
static int
lyb_parse_subtree(struct ly_ctx *ctx, const char *data, struct lyd_node *parent, struct lyd_node **first_sibling,
struct hash_table **sibling_ht, struct lyb_state *lybs)
{
int r, ret = 0;
struct hash_table *children_ht = NULL;
struct lyd_node *node = NULL, *iter;
const struct lys_module *mod;
struct lys_node *sparent, *snode;
assert((parent && !first_sibling) || (!parent && first_sibling));
/* register a new subtree */
ret += (r = lyb_read_start_subtree(data, lybs));
LYB_HAVE_READ_GOTO(r, data, error);
if (!parent) {
/* top-level, read module name */
ret += (r = lyb_parse_model(ctx, data, &mod, lybs));
LYB_HAVE_READ_GOTO(r, data, error);
sparent = NULL;
} else {
mod = NULL;
sparent = parent->schema;
}
/* read hash, find the schema node */
ret += (r = lyb_parse_schema_hash(sparent, mod, data, sibling_ht, &snode, lybs));
LYB_HAVE_READ_GOTO(r, data, error);
/*
* read the node
*/
node = lyb_new_node(snode);
if (!node) {
goto error;
}
/* TODO read attributes */
/* TODO read hash if flag */
/* read node content */
switch (snode->nodetype) {
case LYS_CONTAINER:
case LYS_LIST:
/* nothing to read */
break;
case LYS_LEAF:
case LYS_LEAFLIST:
ret += (r = lyb_parse_leaf(node, data, lybs));
LYB_HAVE_READ_GOTO(r, data, error);
break;
case LYS_ANYXML:
case LYS_ANYDATA:
ret += (r = lyb_parse_anydata(node, data, lybs));
LYB_HAVE_READ_GOTO(r, data, error);
break;
default:
goto error;
}
/* insert into data tree, manually */
if (parent) {
if (!parent->child) {
/* only child */
parent->child = node;
} else {
/* last child */
parent->child->prev->next = node;
node->prev = parent->child->prev;
parent->child->prev = node;
}
node->parent = parent;
} else if (*first_sibling) {
/* last sibling */
(*first_sibling)->prev->next = node;
node->prev = (*first_sibling)->prev;
(*first_sibling)->prev = node;
} else {
/* only sibling */
*first_sibling = node;
}
/* read all descendants */
while (lybs->written[lybs->used - 1]) {
ret += (r = lyb_parse_subtree(ctx, data, node, NULL, &children_ht, lybs));
LYB_HAVE_READ_GOTO(r, data, error);
}
if (children_ht) {
lyht_free(children_ht);
}
/* end the subtree */
lyb_read_stop_subtree(lybs);
/* make containers default if should be */
if (node->schema->nodetype == LYS_CONTAINER) {
LY_TREE_FOR(node->child, iter) {
if (!iter->dflt) {
break;
}
}
if (!iter) {
node->dflt = 1;
}
}
return ret;
error:
if (children_ht) {
lyht_free(children_ht);
}
lyd_free(node);
if (*first_sibling == node) {
*first_sibling = NULL;
}
return -1;
}
static int
lyb_parse_header(const char *data, struct lyb_state *lybs)
{
int ret = 0;
uint8_t byte = 0;
/* TODO version, option for hash storing */
ret += lyb_read(data, (uint8_t *)&byte, sizeof byte, lybs);
/* TODO all used models */
return ret;
}
struct lyd_node *
lyd_parse_lyb(struct ly_ctx *ctx, const char *data, int options, const struct lyd_node *rpc_act,
const struct lyd_node *data_tree, const char *yang_data_name, int *parsed)
{
int r, ret = 0;
struct hash_table *top_sibling_ht = NULL;
struct lyd_node *node = NULL;
struct lyb_state lybs;
if (!ctx || !data) {
LOGARG;
return NULL;
}
lybs.written = malloc(LYB_STATE_STEP * sizeof *lybs.written);
lybs.position = malloc(LYB_STATE_STEP * sizeof *lybs.position);
LY_CHECK_ERR_GOTO(!lybs.written || !lybs.position, LOGMEM(ctx), finish);
lybs.used = 0;
lybs.size = LYB_STATE_STEP;
/* read header */
ret += (r = lyb_parse_header(data, &lybs));
LYB_HAVE_READ_GOTO(r, data, finish);
/* TODO read used models */
/* read subtree(s) */
while (data[0]) {
ret += (r = lyb_parse_subtree(ctx, data, NULL, &node, &top_sibling_ht, &lybs));
LYB_HAVE_READ_GOTO(r, data, finish);
}
/* read the last zero, parsing finished */
++ret;
r = ret;
finish:
if (top_sibling_ht) {
lyht_free(top_sibling_ht);
}
free(lybs.written);
free(lybs.position);
if (parsed) {
*parsed = r;
}
return node;
}