size_t ipset_node_reachable_count(const struct ipset_node_cache *cache, ipset_node_id node) { /* Create a set to track when we've visited a given node. */ struct cork_hash_table *visited = cork_pointer_hash_table_new(0, 0); /* And a queue of nodes to check. */ cork_array(ipset_node_id) queue; cork_array_init(&queue); if (ipset_node_get_type(node) == IPSET_NONTERMINAL_NODE) { DEBUG("Adding node %u to queue", node); cork_array_append(&queue, node); } /* And somewhere to store the result. */ size_t node_count = 0; /* Check each node in turn. */ while (!cork_array_is_empty(&queue)) { ipset_node_id curr = cork_array_at(&queue, --queue.size); /* We don't have to do anything if this node is already in the * visited set. */ if (cork_hash_table_get(visited, (void *) (uintptr_t) curr) == NULL) { DEBUG("Visiting node %u for the first time", curr); /* Add the node to the visited set. */ cork_hash_table_put (visited, (void *) (uintptr_t) curr, (void *) (uintptr_t) true, NULL, NULL, NULL); /* Increase the node count. */ node_count++; /* And add the node's nonterminal children to the visit * queue. */ struct ipset_node *node = ipset_node_cache_get_nonterminal(cache, curr); if (ipset_node_get_type(node->low) == IPSET_NONTERMINAL_NODE) { DEBUG("Adding node %u to queue", node->low); cork_array_append(&queue, node->low); } if (ipset_node_get_type(node->high) == IPSET_NONTERMINAL_NODE) { DEBUG("Adding node %u to queue", node->high); cork_array_append(&queue, node->high); } } } /* Return the result, freeing everything before we go. */ cork_hash_table_free(visited); cork_array_done(&queue); return node_count; }
static int save_visit_node(struct save_data *save_data, ipset_node_id node_id, serialized_id *dest) { /* Check whether we've already serialized this node. */ struct cork_hash_table_entry *entry; bool is_new; entry = cork_hash_table_get_or_create (save_data->serialized_ids, (void *) (uintptr_t) node_id, &is_new); if (!is_new) { *dest = (intptr_t) entry->value; return 0; } else { if (ipset_node_get_type(node_id) == IPSET_TERMINAL_NODE) { /* For terminals, there isn't really anything to do — we * just output the terminal node and use its value as the * serialized ID. */ ipset_value value = ipset_terminal_value(node_id); DEBUG("Writing terminal(%d)", value); rii_check(save_data->write_terminal(save_data, value)); entry->value = (void *) (intptr_t) value; *dest = value; return 0; } else { /* For nonterminals, we drill down into the node's children * first, then output the nonterminal node. */ struct ipset_node *node = ipset_node_cache_get_nonterminal(save_data->cache, node_id); DEBUG("Visiting node %u nonterminal(x%u? %u: %u)", node_id, node->variable, node->high, node->low); /* Output the node's nonterminal children before we output * the node itself. */ serialized_id serialized_low; serialized_id serialized_high; rii_check(save_visit_node(save_data, node->low, &serialized_low)); rii_check(save_visit_node(save_data, node->high, &serialized_high)); /* Output the nonterminal */ serialized_id result = save_data->next_serialized_id--; DEBUG("Writing node %u as serialized node %d = (x%u? %d: %d)", node_id, result, node->variable, serialized_low, serialized_high); entry->value = (void *) (intptr_t) result; *dest = result; return save_data->write_nonterminal (save_data, result, node->variable, serialized_low, serialized_high); } } }
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; }
/** * 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); }