You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
129 lines
4.4 KiB
129 lines
4.4 KiB
/* -*- coding: utf-8 -*-
|
|
* ----------------------------------------------------------------------
|
|
* Copyright © 2010-2012, RedJack, LLC.
|
|
* All rights reserved.
|
|
*
|
|
* Please see the LICENSE.txt file in this distribution for license
|
|
* details.
|
|
* ----------------------------------------------------------------------
|
|
*/
|
|
|
|
#include <libcork/core.h>
|
|
|
|
#include "ipset/bdd/nodes.h"
|
|
#include "ipset/logging.h"
|
|
|
|
|
|
/**
|
|
* Add the given node ID to the node stack, and trace down from it
|
|
* until we find a terminal node. Assign values to the variables for
|
|
* each nonterminal that encounter along the way. We check low edges
|
|
* first, so each new variable we encounter will be assigned FALSE.
|
|
* (The high edges will be checked eventually by a call to the
|
|
* ipset_bdd_iterator_advance() function.)
|
|
*/
|
|
static void
|
|
add_node(struct ipset_bdd_iterator *iterator, ipset_node_id node_id)
|
|
{
|
|
/* Keep tracing down low edges until we reach a terminal. */
|
|
while (ipset_node_get_type(node_id) == IPSET_NONTERMINAL_NODE) {
|
|
/* Add this nonterminal node to the stack, and trace down
|
|
* further into the tree. We check low edges first, so set the
|
|
* node's variable to FALSE in the assignment. */
|
|
struct ipset_node *node =
|
|
ipset_node_cache_get_nonterminal(iterator->cache, node_id);
|
|
|
|
cork_array_append(&iterator->stack, node_id);
|
|
ipset_assignment_set(iterator->assignment, node->variable, false);
|
|
|
|
node_id = node->low;
|
|
}
|
|
|
|
/* Once we find a terminal node, save it away in the iterator result
|
|
* and return. */
|
|
iterator->value = ipset_terminal_value(node_id);
|
|
}
|
|
|
|
|
|
struct ipset_bdd_iterator *
|
|
ipset_node_iterate(struct ipset_node_cache *cache, ipset_node_id root)
|
|
{
|
|
/* First allocate the iterator itself, and all of its contained
|
|
* fields. */
|
|
|
|
struct ipset_bdd_iterator *iterator =
|
|
cork_new(struct ipset_bdd_iterator);
|
|
iterator->finished = false;
|
|
iterator->cache = cache;
|
|
cork_array_init(&iterator->stack);
|
|
iterator->assignment = ipset_assignment_new();
|
|
|
|
/* Then add the root node to the iterator, tracing down until we
|
|
* find the first terminal node. */
|
|
add_node(iterator, root);
|
|
return iterator;
|
|
}
|
|
|
|
|
|
void
|
|
ipset_bdd_iterator_free(struct ipset_bdd_iterator *iterator)
|
|
{
|
|
cork_array_done(&iterator->stack);
|
|
ipset_assignment_free(iterator->assignment);
|
|
free(iterator);
|
|
}
|
|
|
|
|
|
void
|
|
ipset_bdd_iterator_advance(struct ipset_bdd_iterator *iterator)
|
|
{
|
|
/* If we're already at the end of the iterator, don't do anything. */
|
|
if (CORK_UNLIKELY(iterator->finished)) {
|
|
return;
|
|
}
|
|
|
|
/* We look at the last node in the stack. If it's currently
|
|
* assigned a false value, then we track down its true branch. If
|
|
* it's got a true branch, then we pop it off and check the next to
|
|
* last node. */
|
|
|
|
DEBUG("Advancing BDD iterator");
|
|
|
|
while (cork_array_size(&iterator->stack) > 0) {
|
|
ipset_node_id last_node_id =
|
|
cork_array_at
|
|
(&iterator->stack, cork_array_size(&iterator->stack) - 1);
|
|
|
|
struct ipset_node *last_node =
|
|
ipset_node_cache_get_nonterminal(iterator->cache, last_node_id);
|
|
|
|
enum ipset_tribool current_value =
|
|
ipset_assignment_get(iterator->assignment, last_node->variable);
|
|
|
|
/* The current value can't be EITHER, because we definitely
|
|
* assign a TRUE or FALSE to the variables of the nodes that we
|
|
* encounter. */
|
|
if (current_value == IPSET_TRUE) {
|
|
/* We've checked both outgoing edges for this node, so pop
|
|
* it off and look at its parent. */
|
|
iterator->stack.size--;
|
|
|
|
/* Before continuing, reset this node's variable to
|
|
* indeterminate in the assignment. */
|
|
ipset_assignment_set
|
|
(iterator->assignment, last_node->variable, IPSET_EITHER);
|
|
} else {
|
|
/* We've checked this node's low edge, but not its high
|
|
* edge. Set the variable to TRUE in the assignment, and
|
|
* add the high edge's node to the node stack. */
|
|
ipset_assignment_set
|
|
(iterator->assignment, last_node->variable, IPSET_TRUE);
|
|
add_node(iterator, last_node->high);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* If we fall through then we ran out of nodes to check. That means
|
|
* the iterator is done! */
|
|
iterator->finished = true;
|
|
}
|