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

/* -*- 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;
}