/* -*- coding: utf-8 -*- * ---------------------------------------------------------------------- * Copyright © 2010-2012, RedJack, LLC. * All rights reserved. * * Please see the LICENSE.txt file in this distribution for license * details. * ---------------------------------------------------------------------- */ #include #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; }