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