mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-23 10:11:53 +00:00
aa14b546b8
The first iteration of MIB tree with tests.
910 lines
23 KiB
C
910 lines
23 KiB
C
#include "mib_tree.h"
|
|
#include "snmp_utils.h"
|
|
|
|
/* TODO does the code handle leafs correctly ?! */
|
|
|
|
#ifdef allocz
|
|
#undef allocz
|
|
#endif
|
|
|
|
#define alloc(size) mb_alloc(p, size)
|
|
#define allocz(size) mb_allocz(p, size)
|
|
#define free(ptr) mb_free(ptr)
|
|
#define realloc(ptr, newsize) mib_mb_realloc(p, ptr, newsize)
|
|
|
|
/*
|
|
* mib_mb_realloc - fix mb_realloc for NULL
|
|
* @p: pool to use for NULL pointers
|
|
* @ptr: old pointer to be reallocated
|
|
* @size: new size of allocated memory block
|
|
*
|
|
* The mb_realloc() does not work with NULL as ptr.
|
|
*/
|
|
static inline void *
|
|
mib_mb_realloc(pool *p, void *ptr, unsigned size)
|
|
{
|
|
if (!ptr)
|
|
return mb_alloc(p, size);
|
|
|
|
return mb_realloc(ptr, size);
|
|
}
|
|
|
|
void
|
|
mib_tree_init(pool *p, struct mib_tree *t)
|
|
{
|
|
struct mib_node *node = &t->root;
|
|
node->c.id = 0;
|
|
node->c.flags = 0;
|
|
node->children = NULL;
|
|
node->child_len = 0;
|
|
|
|
struct oid *oid = tmp_alloc(
|
|
snmp_oid_size_from_len((uint) ARRAY_SIZE(snmp_internet)));
|
|
STORE_U8(oid->n_subid, ARRAY_SIZE(snmp_internet));
|
|
STORE_U8(oid->prefix, 0);
|
|
STORE_U8(oid->include, 0);
|
|
STORE_U8(oid->reserved, 0);
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(snmp_internet); i++)
|
|
STORE_U32(oid->ids[i], snmp_internet[i]);
|
|
|
|
(void) mib_tree_add(p, t, oid, 0);
|
|
|
|
/* WTF ??
|
|
struct mib_walk_state walk = { };
|
|
(void) mib_tree_find(t, &walk, oid);
|
|
*/
|
|
}
|
|
|
|
|
|
// This function does not work with leaf nodes inside the snmp_internet prefix
|
|
// area
|
|
// Return NULL of failure, valid mib_node_u pointer otherwise
|
|
mib_node_u *
|
|
mib_tree_add(pool *p, struct mib_tree *t, const struct oid *oid, int is_leaf)
|
|
{
|
|
//ASSERT(snmp_oid_is_prefixed(oid) || !snmp_oid_is_prefixable(oid));
|
|
struct mib_walk_state walk;
|
|
mib_node_u *node;
|
|
|
|
/* The empty prefix is associated with the root tree node */
|
|
if (snmp_is_oid_empty(oid) && !is_leaf)
|
|
return (mib_node_u *) &t->root;
|
|
else if (snmp_is_oid_empty(oid))
|
|
return NULL;
|
|
|
|
mib_tree_walk_init(&walk);
|
|
node = mib_tree_find(t, &walk, oid);
|
|
ASSERT(walk.id_pos <= LOAD_U8(oid->n_subid) + 1);
|
|
|
|
if (node)
|
|
{
|
|
if (mib_node_is_leaf(node) == is_leaf)
|
|
return node;
|
|
|
|
/* we are trying to insert a leaf node in place of inner node,
|
|
* or vice versa */
|
|
return NULL;
|
|
}
|
|
|
|
ASSERT(walk.id_pos < LOAD_U8(oid->n_subid) + 1);
|
|
|
|
node = walk.stack[walk.stack_pos - 1];
|
|
/* we encounter leaf node before end of OID's id path */
|
|
if (mib_node_is_leaf(node))
|
|
return NULL;
|
|
|
|
struct mib_node *node_inner = &node->inner;
|
|
if (snmp_oid_is_prefixed(oid) &&
|
|
walk.stack_pos <= ARRAY_SIZE(snmp_internet) + 1)
|
|
{
|
|
ASSUME(walk.stack_pos && walk.stack[0] == (mib_node_u *) &t->root);
|
|
|
|
for (u32 id = walk.stack_pos - 1; id < ARRAY_SIZE(snmp_internet); id++)
|
|
{
|
|
if (snmp_internet[id] >= node_inner->child_len)
|
|
{
|
|
u32 old_len = node_inner->child_len;
|
|
node_inner->child_len = snmp_internet[id] + 1;
|
|
node_inner->children = realloc(node_inner->children,
|
|
node_inner->child_len * sizeof(mib_node_u *));
|
|
|
|
for (u32 i = old_len; i < node_inner->child_len; i++)
|
|
node_inner->children[i] = NULL;
|
|
}
|
|
|
|
node = allocz(sizeof(struct mib_node));
|
|
/* assign child into a parent's children array */
|
|
node_inner->children[snmp_internet[id]] = node;
|
|
node_inner = &node->inner;
|
|
node_inner->c.id = snmp_internet[id];
|
|
/* node_inner's fields c.flags, child_len, children defaults to zero or
|
|
* NULL respectively */
|
|
walk.stack[walk.stack_pos++] = node;
|
|
}
|
|
|
|
if (walk.stack_pos == ARRAY_SIZE(snmp_internet) + 1)
|
|
{
|
|
u32 old_len = node_inner->child_len;
|
|
node_inner->child_len = MAX(old_len, (u32) LOAD_U8(oid->prefix) + 1);
|
|
node_inner->children = realloc(node_inner->children,
|
|
node_inner->child_len * sizeof(mib_node_u *));
|
|
|
|
for (u32 i = old_len; i < node_inner->child_len; i++)
|
|
node_inner->children[i] = NULL;
|
|
|
|
if (is_leaf && !LOAD_U8(oid->n_subid))
|
|
{
|
|
node = allocz(sizeof(struct mib_leaf));
|
|
node->empty.flags = MIB_TREE_LEAF;
|
|
}
|
|
else
|
|
{
|
|
node = allocz(sizeof(struct mib_node));
|
|
node->empty.flags = 0;
|
|
}
|
|
|
|
node->empty.id = LOAD_U8(oid->prefix);
|
|
/* add node into the parent's children array */
|
|
node_inner->children[LOAD_U8(oid->prefix)] = node;
|
|
node_inner = &node->inner;
|
|
walk.stack[walk.stack_pos++] = node;
|
|
}
|
|
}
|
|
|
|
/* snmp_internet + 2 = empty + snmp_internet + prefix */
|
|
if (snmp_oid_is_prefixed(oid) &&
|
|
walk.stack_pos == ARRAY_SIZE(snmp_internet) + 2 &&
|
|
LOAD_U8(oid->n_subid) == 0 &&
|
|
mib_node_is_leaf(node) == is_leaf)
|
|
return node;
|
|
|
|
if (mib_node_is_leaf(node))
|
|
return node;
|
|
|
|
u8 subids = LOAD_U8(oid->n_subid);
|
|
u32 old_len = node_inner->child_len;
|
|
u32 child_id = oid->ids[walk.id_pos];
|
|
node_inner->child_len = MAX(old_len, LOAD_U32(child_id) + 1);
|
|
node_inner->children = realloc(node_inner->children,
|
|
node_inner->child_len * sizeof(mib_node_u *));
|
|
|
|
for (u32 i = old_len; i < node_inner->child_len; i++)
|
|
node_inner->children[i] = NULL;
|
|
|
|
struct mib_node *parent;
|
|
/* break to loop before last node in the oid */
|
|
for (; walk.id_pos < subids - 1;)
|
|
{
|
|
parent = node_inner;
|
|
node_inner = allocz(sizeof(struct mib_node));
|
|
|
|
parent->children[child_id] = (mib_node_u *) node_inner;
|
|
node_inner->c.id = child_id;
|
|
|
|
child_id = LOAD_U32(oid->ids[++walk.id_pos]);
|
|
|
|
node_inner->child_len = child_id + 1;
|
|
node_inner->children = allocz(node_inner->child_len * sizeof(mib_node_u *));
|
|
/*
|
|
node_inner->child_len = (child_id == 0) ? 0 : child_id;
|
|
node_inner->children = (child_id == 0) ? NULL
|
|
: allocz(node_inner->child_len * sizeof(mib_node_u *));
|
|
*/
|
|
}
|
|
|
|
parent = node_inner;
|
|
mib_node_u *last;
|
|
if (is_leaf)
|
|
{
|
|
last = allocz(sizeof(struct mib_leaf));
|
|
struct mib_leaf *leaf = &last->leaf;
|
|
|
|
parent->children[child_id] = (mib_node_u *) leaf;
|
|
leaf->c.id = child_id;
|
|
|
|
//leaf->c.id = LOAD_U32(oid->ids[subids - 1]);
|
|
leaf->c.flags = MIB_TREE_LEAF;
|
|
}
|
|
else
|
|
{
|
|
last = allocz(sizeof(struct mib_node));
|
|
node_inner = &last->inner;
|
|
|
|
parent->children[child_id] = (mib_node_u *) node_inner;
|
|
node_inner->c.id = child_id;
|
|
//node_inner->c.id = LOAD_U32(oid->ids[subids - 1]);
|
|
/* fields c.flags, child_len and children are set by zeroed allocz() */
|
|
}
|
|
|
|
return last;
|
|
}
|
|
|
|
#if 0
|
|
// TODO merge functions mib_tree_add and mib_tree_insert into one with public iface
|
|
|
|
mib_node_u *
|
|
mib_tree_add(struct snmp_proto *p, struct mib_tree *t, const struct oid *oid, uint size, int is_leaf)
|
|
{
|
|
struct mib_walk_state walk = { };
|
|
mib_node_u *known = mib_tree_find(t, &walk, oid);
|
|
|
|
if (known)
|
|
return known;
|
|
|
|
known = walk.stack[walk.stack_pos];
|
|
|
|
// redundant ??, if not, would be returned from find
|
|
if (walk.id_pos_abs == oid->n_subid)
|
|
return known;
|
|
|
|
if (walk.id_pos_rel < 0)
|
|
|
|
if (walk.id_pos_abs < oid->n_subid && (u32) walk.id_pos_rel == known->id_len)
|
|
{
|
|
if (known->child_len >= oid->ids[walk.id_pos_abs]) // abs +1?
|
|
{
|
|
u32 old_len = known->child_len;
|
|
known->child_len = oid->ids[walk.id_pos_abs] + 1;
|
|
known->children = mb_realloc(known->children,
|
|
known->child_len * sizeof(struct mib_node *));
|
|
|
|
for (uint i = old_len; i < known->child_len; i++)
|
|
known->children[i] = NULL;
|
|
}
|
|
|
|
/* find would return it
|
|
if (known->children[oid->ids[]])
|
|
return known->children[oid->ids[]];
|
|
*/
|
|
|
|
struct mib_node *node = mb_alloc(p->p.pool, sizeof(struct mib_node));
|
|
node->id_len = oid->n_subid - walk.id_pos_abs;
|
|
node->ids = mb_alloc(p->p.pool, node->id_len * sizeof(u32));
|
|
node->flags = 0;
|
|
node->children = NULL;
|
|
node->child_len = 0;
|
|
node->child_count = 0;
|
|
|
|
known->child_count++;
|
|
known->children[oid->ids[0]] = node;
|
|
return node;
|
|
}
|
|
else if (walk.id_pos_abs < oid->n_subid)
|
|
{
|
|
/* We known that walk.id_pos_rel < known->id_len */
|
|
struct mib_node *parent = mb_alloc(p->p.pool, sizeof(struct mib_node));
|
|
parent->id_len = known->id_len - walk.id_pos_rel;
|
|
parent->ids = mb_alloc(p->p.pool,
|
|
parent->id_len * sizeof(struct mib_node *));
|
|
memcpy(&parent->ids, &known->ids, parent->id_len * sizeof(struct mib_node *));
|
|
u32 *ids = mb_alloc(p->p.pool,
|
|
(known->id_len - walk.id_pos_rel) * sizeof(u32));
|
|
memcpy(ids, &known->ids[parent->id_len],
|
|
(known->id_len - parent->id_len) * sizeof(struct mib_node *));
|
|
mb_free(known->ids);
|
|
known->id_len = known->id_len - walk.id_pos_rel;
|
|
known->ids = ids;
|
|
parent->child_len = MAX(known->ids[0], oid->ids[walk.id_pos_abs]) + 1;
|
|
parent->children = mb_allocz(p->p.pool,
|
|
parent->child_len * sizeof(struct mib_node *));
|
|
parent->children[known->ids[0]] = known;
|
|
|
|
struct mib_node *child = mb_alloc(p->p.pool, sizeof(struct mib_node));
|
|
child->id_len = oid->n_subid - walk.id_pos_abs - parent->id_len;
|
|
child->ids = mb_alloc(p->p.pool,
|
|
child->id_len * sizeof(struct mib_node *));
|
|
memcpy(&child->ids, &oid->ids[oid->n_subid - child->id_len],
|
|
child->id_len * sizeof(u32));
|
|
// TODO test that we do not override the known
|
|
parent->children[child->ids[0]] = child;
|
|
|
|
return child;
|
|
}
|
|
else if (walk.id_pos_abs > oid->n_subid)
|
|
die("unreachable");
|
|
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
int
|
|
mib_tree_insert(struct snmp_proto *p, struct mib_tree *t, struct oid *oid)
|
|
{
|
|
ASSUME(oid);
|
|
|
|
struct mib_walk_state walk = { };
|
|
struct mib_node *node = mib_tree_find(t, &walk, oid);
|
|
struct mib_leaf *leaf = NULL;
|
|
|
|
if (!node)
|
|
{
|
|
node = walk.stack[walk.stack_pos];
|
|
|
|
if (walk.id_pos_abs > oid->n_subid)
|
|
{
|
|
}
|
|
else / * walk.id_pos_abs <= oid->n_subid * /
|
|
{
|
|
leaf = mb_alloc(p->p.pool, sizeof(struct mib_leaf));
|
|
leaf->id_len = oid->n_subid - walk.id_pos_abs;
|
|
leaf->ids = mb_alloc(p->p.pool, leaf->id_len * sizeof(struct mib_node *));
|
|
memcpy(&leaf->ids, &oid->ids[oid->n_subid - leaf->id_len],
|
|
leaf->id_len * sizeof(u32));
|
|
leaf->flags = 0;
|
|
leaf->children = NULL;
|
|
leaf->child_len = 0;
|
|
leaf->child_count = 0;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
#if 0
|
|
int
|
|
mib_tree_insert(struct snmp_proto *p, struct mib_tree *t, struct oid *oid, struct mib_leaf *leaf)
|
|
{
|
|
ASSUME(oid);
|
|
|
|
struct mib_walk_state walk = { };
|
|
struct mib_node *node = mib_tree_find(t, &walk, oid);
|
|
struct mib_node *leaf_node = &leaf->n;
|
|
|
|
if (!node)
|
|
{
|
|
node = walk.stack[walk.stack_pos];
|
|
|
|
// can this really happen ??
|
|
if (walk.id_pos_abs > oid->n_subid)
|
|
{
|
|
struct mib_node *parent = mb_alloc(p->p.pool, sizeof(struct mib_node));
|
|
parent->id_len = walk.id_pos_abs - oid->n_subid; // -1?
|
|
parent->ids = mb_alloc(p->p.pool, parent->id_len * sizeof(u32));
|
|
memcpy(&parent->ids, &node->ids, parent->id_len * sizeof(u32));
|
|
u32 *ids = mb_alloc(p->p.pool,
|
|
(node->id_len - parent->id_len) * sizeof(u32));
|
|
node->id_len = node->id_len - parent->id_len;
|
|
memcpy(ids, &node->ids[parent->id_len], node->id_len * sizeof(u32));
|
|
mb_free(node->ids);
|
|
node->ids = ids;
|
|
|
|
parent->child_count = 2;
|
|
parent->child_len = MAX(node->ids[0], oid->ids[walk.id_pos_abs]) + 1;
|
|
parent->children = mb_allocz(p->p.pool,
|
|
parent->child_len * sizeof(struct mib_node *));
|
|
parent->children[node->ids[0]] = node;
|
|
parent->children[leaf_node->ids[0]] = leaf_node;
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
mb_free(leaf_node->ids);
|
|
leaf_node->id_len = oid->n_subid - walk.id_pos_abs;
|
|
leaf_node->ids = mb_alloc(p->p.pool, leaf_node->id_len * sizeof(u32));
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (mib_node_is_leaf(node))
|
|
{
|
|
struct mib_leaf *l = SKIP_BACK(struct mib_leaf, n, node);
|
|
insert_node(&leaf->leafs, &l->leafs);
|
|
return 1;
|
|
}
|
|
|
|
if (node->child_len > 0)
|
|
return 0;
|
|
|
|
// problem when node->id_len + (walk.id_pos_abs - walk.id_pos_rel) > oid->n_subid
|
|
if (walk.id_pos_abs < oid->n_subid) // +-1??
|
|
{
|
|
leaf_node->id_len = node->id_len - walk.id_pos_abs;
|
|
leaf_node->ids = mb_alloc(p->p.pool, leaf_node->id_len * sizeof(u32));
|
|
memcpy(&leaf_node->ids, &oid->ids[walk.id_pos_abs], leaf_node->id_len * sizeof(u32));
|
|
leaf_node->child_len = leaf_node->child_count = 0;
|
|
leaf_node->children = NULL;
|
|
return 1;
|
|
}
|
|
else
|
|
return 0;
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
int
|
|
mib_tree_remove(struct mib_tree *tree, struct oid *oid)
|
|
{
|
|
struct mib_walk_state walk = { };
|
|
struct mib_node *node = mib_tree_find(tree, &walk, oid);
|
|
|
|
if (!node)
|
|
return 0;
|
|
|
|
mib_tree_delete(&walk);
|
|
//mib_tree_delete(tree, &walk);
|
|
return 1;
|
|
}
|
|
*/
|
|
|
|
int
|
|
mib_tree_remove(struct mib_tree *t, const struct oid *oid)
|
|
{
|
|
struct mib_walk_state walk = { };
|
|
mib_node_u *node = mib_tree_find(t, &walk, oid);
|
|
|
|
if (!node)
|
|
return 0;
|
|
else
|
|
{
|
|
(void) mib_tree_delete(t, &walk);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
int
|
|
mib_tree_delete(struct mib_tree *t, struct mib_walk_state *walk)
|
|
{
|
|
int deleted = 0;
|
|
ASSUME(t);
|
|
|
|
/* (walk->stack_pos < 2) It is impossible to delete root node */
|
|
if (!walk || !walk->id_pos || walk->stack_pos < 2)
|
|
return 0;
|
|
|
|
struct mib_node *parent = &walk->stack[walk->stack_pos - 2]->inner;
|
|
mib_node_u *node = walk->stack[walk->stack_pos - 1];
|
|
|
|
struct mib_walk_state delete = {
|
|
.id_pos = walk->id_pos,
|
|
.stack_pos = 2,
|
|
.stack = {
|
|
(mib_node_u *) parent,
|
|
node,
|
|
NULL,
|
|
},
|
|
};
|
|
|
|
u32 last_id = 0;
|
|
while (delete.stack_pos > 1)
|
|
{
|
|
continue_while: /* like outer continue, but skip always true condition */
|
|
parent = (struct mib_node *) delete.stack[delete.stack_pos - 2];
|
|
|
|
if (mib_node_is_leaf(node))
|
|
{
|
|
/* Free leaf node */
|
|
last_id = node->leaf.c.id;
|
|
parent->children[last_id] = NULL;
|
|
delete.stack[--delete.stack_pos] = NULL;
|
|
free(node);
|
|
deleted++;
|
|
node = delete.stack[delete.stack_pos - 1];
|
|
continue; /* here, we couldn't skip the while condition */
|
|
}
|
|
|
|
struct mib_node *node_inner = &node->inner;
|
|
mib_node_u *child = NULL;
|
|
for (u32 id = last_id; id < node_inner->child_len; id++)
|
|
{
|
|
/* Recursively traverse child nodes */
|
|
child = node_inner->children[id];
|
|
|
|
if (!child)
|
|
continue;
|
|
|
|
last_id = 0;
|
|
delete.stack[delete.stack_pos++] = child;
|
|
parent = node_inner;
|
|
node = child;
|
|
goto continue_while; /* outer continue */
|
|
}
|
|
|
|
/* Free inner node without any children */
|
|
last_id = node_inner->c.id;
|
|
parent->children[last_id] = NULL;
|
|
delete.stack[--delete.stack_pos] = NULL;
|
|
free(node_inner->children);
|
|
free(node_inner);
|
|
deleted++;
|
|
node = (mib_node_u *) parent;
|
|
|
|
/* skip check for deleted node in loop over children */
|
|
last_id++;
|
|
}
|
|
|
|
/* delete the node from original stack */
|
|
walk->stack[--walk->stack_pos] = NULL;
|
|
|
|
node = walk->stack[walk->stack_pos - 1];
|
|
struct mib_node *node_inner = &node->inner;
|
|
u32 id;
|
|
for (id = 0; id < node_inner->child_len; id++)
|
|
{
|
|
if (node_inner->children[id] != NULL)
|
|
break;
|
|
}
|
|
|
|
if (id == node_inner->child_len)
|
|
{
|
|
/* all the children are NULL */
|
|
free(node_inner->children);
|
|
node_inner->children = NULL;
|
|
node_inner->child_len = 0;
|
|
}
|
|
|
|
return deleted;
|
|
}
|
|
|
|
/* currently support only search with blank new walk state */
|
|
/* requires non-NULL walk */
|
|
/* TODO doc string, user should check if the node is not root (or at least be
|
|
* aware of that */
|
|
mib_node_u *
|
|
mib_tree_find(const struct mib_tree *t, struct mib_walk_state *walk, const struct oid *oid)
|
|
{
|
|
ASSERT(t && walk);
|
|
|
|
if (!oid || snmp_is_oid_empty(oid))
|
|
{
|
|
walk->stack_pos = 1;
|
|
walk->stack[0] = (mib_node_u *) &t->root;
|
|
return (snmp_is_oid_empty(oid)) ? (mib_node_u *) &t->root : NULL;
|
|
}
|
|
|
|
mib_node_u *node;
|
|
struct mib_node *node_inner;
|
|
|
|
u8 oid_pos = walk->id_pos = 0;
|
|
node = walk->stack[walk->stack_pos++] = (mib_node_u *) &t->root;
|
|
|
|
#if 0
|
|
u8 oid_pos = walk->id_pos;
|
|
|
|
if (walk->stack_pos > 0)
|
|
node = walk->stack[walk->stack_pos];
|
|
else
|
|
node = walk->stack[walk->stack_pos++] = (mib_node_u *) &t->root;
|
|
|
|
if (mib_node_is_leaf(node))
|
|
{
|
|
if (snmp_oid_is_prefixed(oid) && LOAD_U8(oid->n_subid) + ARRAY_SIZE(snmp_internet) + 1 == walk->id_pos)
|
|
return node;
|
|
|
|
else if (!snmp_oid_is_prefixed(oid) && LOAD_U8(oid->n_subid) + 1 == walk->id_pos)
|
|
return node;
|
|
|
|
/* it could hold that LOAD_U8(oid->n_subid) >= walk->id_pos */
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
node_inner = &node->inner;
|
|
ASSERT(node && !mib_node_is_leaf(node));
|
|
|
|
/* Handling of prefixed OID */
|
|
if (snmp_oid_is_prefixed(oid))
|
|
{
|
|
uint i;
|
|
/* walking the snmp_internet prefix itself */
|
|
for (i = 0; i < ARRAY_SIZE(snmp_internet); i++)
|
|
{
|
|
if (node_inner->child_len <= snmp_internet[i])
|
|
return NULL;
|
|
|
|
node = node_inner->children[snmp_internet[i]];
|
|
node_inner = &node->inner;
|
|
|
|
if (!node)
|
|
return NULL;
|
|
|
|
ASSERT(node->empty.id == snmp_internet[i]);
|
|
walk->stack[walk->stack_pos++] = node;
|
|
|
|
if (mib_node_is_leaf(node))
|
|
return NULL;
|
|
}
|
|
|
|
/* walking the prefix continuation (OID field oid->prefix) */
|
|
u8 prefix = LOAD_U8(oid->prefix);
|
|
if (node_inner->child_len <= prefix)
|
|
return NULL;
|
|
|
|
node = node_inner->children[prefix];
|
|
node_inner = &node->inner;
|
|
|
|
if (!node)
|
|
return NULL;
|
|
|
|
ASSERT(node->empty.id == prefix);
|
|
walk->stack[walk->stack_pos++] = node;
|
|
|
|
if (mib_node_is_leaf(node) && LOAD_U8(oid->n_subid) > 0)
|
|
return NULL;
|
|
}
|
|
|
|
u8 subids = LOAD_U8(oid->n_subid);
|
|
if (subids == 0)
|
|
return (node == (mib_node_u *) &t->root) ? NULL : node;
|
|
|
|
/* loop for all OID's ids except the last one */
|
|
for (oid_pos = 0; oid_pos < subids - 1; oid_pos++) // remove oid_pos assignment
|
|
{
|
|
u32 id = LOAD_U32(oid->ids[oid_pos]);
|
|
if (node_inner->child_len <= id)
|
|
{
|
|
walk->id_pos = oid_pos;
|
|
return NULL;
|
|
}
|
|
|
|
node = node_inner->children[id];
|
|
node_inner = &node->inner;
|
|
|
|
if (!node)
|
|
{
|
|
walk->id_pos = oid_pos;
|
|
return NULL;
|
|
}
|
|
|
|
ASSERT(node->empty.id == id);
|
|
walk->stack[walk->stack_pos++] = node;
|
|
|
|
if (mib_node_is_leaf(node))
|
|
{
|
|
walk->id_pos = ++oid_pos;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
walk->id_pos = oid_pos;
|
|
u32 last_id = LOAD_U32(oid->ids[oid_pos]);
|
|
if (node_inner->child_len <= last_id)
|
|
return NULL;
|
|
|
|
node = node_inner->children[last_id];
|
|
node_inner = &node->inner;
|
|
|
|
if (!node)
|
|
return NULL;
|
|
|
|
/* here, the check of node being a leaf is intentionally omitted
|
|
* because we may need to search for a inner node */
|
|
ASSERT(node->empty.id == last_id);
|
|
walk->id_pos = ++oid_pos;
|
|
return walk->stack[walk->stack_pos++] = node;
|
|
}
|
|
|
|
void
|
|
mib_tree_walk_init(struct mib_walk_state *walk)
|
|
{
|
|
walk->id_pos = 0;
|
|
walk->stack_pos = 0;
|
|
memset(&walk->stack, 0, sizeof(walk->stack));
|
|
}
|
|
|
|
/*
|
|
void
|
|
mib_node_free(mib_node_u *node)
|
|
{
|
|
if (!mib_node_is_leaf(node))
|
|
{
|
|
struct mib_node *node_inner = &node->inner;
|
|
node_inner->child_len = 0;
|
|
free(node_inner->children);
|
|
node_inner->children = NULL;
|
|
}
|
|
|
|
free(node);
|
|
}
|
|
*/
|
|
|
|
mib_node_u *
|
|
mib_tree_walk_next(struct mib_tree *t, struct mib_walk_state *walk)
|
|
{
|
|
ASSERT(t && walk);
|
|
|
|
u32 next_id = 0;
|
|
|
|
if (walk->stack_pos == 0)
|
|
return NULL;
|
|
|
|
mib_node_u *node = walk->stack[walk->stack_pos - 1];
|
|
|
|
if (mib_node_is_leaf(node))
|
|
{
|
|
next_id = node->leaf.c.id + 1;
|
|
walk->stack[--walk->stack_pos] = NULL;
|
|
node = walk->stack[walk->stack_pos - 1];
|
|
}
|
|
|
|
while (walk->stack_pos > 0)
|
|
{
|
|
node = walk->stack[walk->stack_pos - 1];
|
|
|
|
if (mib_node_is_leaf(node))
|
|
{
|
|
walk->stack[walk->stack_pos++] = node;
|
|
return node;
|
|
}
|
|
|
|
struct mib_node *node_inner = &node->inner;
|
|
for (u32 id = next_id; id < node_inner->child_len; id++)
|
|
{
|
|
mib_node_u *child = node_inner->children[id];
|
|
|
|
if (!child)
|
|
continue;
|
|
|
|
walk->stack[walk->stack_pos++] = child;
|
|
return child;
|
|
}
|
|
|
|
next_id = node_inner->c.id + 1;
|
|
walk->stack[--walk->stack_pos] = NULL;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#if 0
|
|
struct mib_node *
|
|
mib_tree_walk_next(struct mib_walk_state *walk)
|
|
{
|
|
ASSUME(walk->stack[walk->stack_pos]);
|
|
|
|
if (walk->stack_pos == 0 && walk->stack[0] &&
|
|
walk->stack[0]->flags & (MIB_TREE_REG_ACK || MIB_TREE_REG_WAIT))
|
|
return walk->stack[0];
|
|
|
|
struct mib_node *node = walk->stack[walk->stack_pos];
|
|
u32 id;
|
|
|
|
find_leaf:
|
|
while (!mib_node_is_leaf(node))
|
|
{
|
|
for (id = 0; id < node->child_len; id++)
|
|
{
|
|
if (node->children[id])
|
|
{
|
|
node = node->children[id];
|
|
walk->stack[++walk->stack_pos] = node;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (node->flags & (MIB_TREE_REG_ACK || MIB_TREE_REG_WAIT))
|
|
return node;
|
|
}
|
|
|
|
id = node->ids[0];
|
|
|
|
while (walk->stack_pos)
|
|
{
|
|
walk->stack[walk->stack_pos] = NULL;
|
|
--walk->stack_pos;
|
|
node = walk->stack[walk->stack_pos];
|
|
|
|
if (id + 1 != node->child_len)
|
|
break;
|
|
}
|
|
|
|
if (id + 1 == node->child_len)
|
|
return walk->stack[0] = NULL;
|
|
|
|
node = node->children[id + 1];
|
|
walk->stack_pos++;
|
|
walk->stack[walk->stack_pos] = node;
|
|
goto find_leaf;
|
|
}
|
|
#endif
|
|
|
|
|
|
struct mib_leaf *
|
|
mib_tree_walk_next_leaf(struct mib_tree *t, struct mib_walk_state *walk)
|
|
{
|
|
(void) t;
|
|
|
|
if (walk->stack_pos == 0)
|
|
return NULL;
|
|
|
|
u32 next_id = 0;
|
|
mib_node_u *node = walk->stack[walk->stack_pos - 1];
|
|
|
|
if (mib_node_is_leaf(node) && walk->stack_pos > 1)
|
|
{
|
|
next_id = node->leaf.c.id + 1;
|
|
walk->stack[--walk->stack_pos] = NULL;
|
|
node = walk->stack[walk->stack_pos - 1]; // does it underflow ??
|
|
}
|
|
else if (mib_node_is_leaf(node))
|
|
{
|
|
/* walk->stack_pos == 1, so we NULL out the last stack field */
|
|
walk->stack[--walk->stack_pos] = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
mib_node_u *parent = (walk->stack_pos <= 1) ? NULL :
|
|
walk->stack[walk->stack_pos - 2];
|
|
|
|
while (walk->stack_pos > 0)
|
|
{
|
|
continue_while:
|
|
node = walk->stack[walk->stack_pos - 1];
|
|
|
|
if (mib_node_is_leaf(node))
|
|
{
|
|
walk->stack[walk->stack_pos++] = node;
|
|
return (struct mib_leaf *) node;
|
|
}
|
|
|
|
struct mib_node *node_inner = &node->inner;
|
|
for (u32 id = next_id; id < node_inner->child_len; id++)
|
|
{
|
|
mib_node_u *child = node_inner->children[id];
|
|
|
|
if (!child)
|
|
continue;
|
|
|
|
next_id = 0;
|
|
walk->stack[walk->stack_pos++] = child;
|
|
/* node is assign at the beginning of the while loop (from stack) */
|
|
goto continue_while;
|
|
}
|
|
|
|
while (walk->stack_pos > 1) // endless loop here possible ??
|
|
{
|
|
parent = walk->stack[walk->stack_pos - 2];
|
|
node = walk->stack[walk->stack_pos - 1];
|
|
|
|
ASSUME(mib_node_is_leaf(node));
|
|
if (node->leaf.c.id + 1 == parent->inner.child_len)
|
|
walk->stack[--walk->stack_pos] = NULL;
|
|
|
|
next_id = node->inner.c.id + 1;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#if 0
|
|
struct mib_leaf *
|
|
mib_tree_next_leaf(struct mib_walk_state *walk)
|
|
{
|
|
ASSUME(walk->stack[walk->stack_pos] &&
|
|
mib_node_is_leaf(walk->stack[walk->stack_pos]));
|
|
|
|
struct mib_node *node = walk->stack[walk->stack_pos];
|
|
u32 id;
|
|
|
|
while (walk->stack_pos)
|
|
{
|
|
id = node->ids[0];
|
|
walk->stack[walk->stack_pos] = NULL;
|
|
--walk->stack_pos;
|
|
node = walk->stack[walk->stack_pos];
|
|
|
|
if (id + 1 != node->child_len)
|
|
break;
|
|
}
|
|
|
|
if (id + 1 == node->child_len)
|
|
return (struct mib_leaf *) (walk->stack[0] = NULL);
|
|
|
|
id++;
|
|
while (!mib_node_is_leaf(node))
|
|
{
|
|
for (; id < node->child_len && !node->children[id]; id++)
|
|
;
|
|
|
|
node = node->children[id];
|
|
walk->stack[++walk->stack_pos] = node;
|
|
id = 0;
|
|
}
|
|
|
|
return (struct mib_leaf *) node;
|
|
}
|
|
#endif
|
|
|