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; }
/* Retrieves the next value from the consumer's queue. When this * returns c->current_id will be the ID of the next value. You can * retrieve the value using vrt_queue_get. */ static int vrt_consumer_next_raw(struct vrt_queue *q, struct vrt_consumer *c) { /* We've just finished processing the current_id'th value. */ vrt_value_id last_consumed_id = c->current_id++; /* If we know there are values available that we haven't yet * consumed, go ahead and return one. */ if (vrt_mod_le(c->current_id, c->last_available_id)) { clog_trace("<%s> Next value is %d (already available)", c->name, c->current_id); return 0; } /* We've run out of values that we know can been processed. Notify * the world how much we've processed so far. */ clog_debug("<%s> Signal consumption of %d", c->name, last_consumed_id); vrt_consumer_set_cursor(c, last_consumed_id); /* Check to see if there are any more values that we can process. */ if (cork_array_is_empty(&c->dependencies)) { bool first = true; vrt_value_id last_available_id; clog_debug("<%s> Wait for value %d", c->name, c->current_id); /* If we don't have any dependencies check the queue itself to see how * many values have been published. */ last_available_id = vrt_queue_get_cursor(q); while (vrt_mod_le(last_available_id, last_consumed_id)) { clog_trace("<%s> Last available value is %d (wait)", c->name, last_available_id); c->yield_count++; rii_check(vrt_yield_strategy_yield (c->yield, first, q->name, c->name)); first = false; last_available_id = vrt_queue_get_cursor(q); } c->last_available_id = last_available_id; clog_debug("<%s> Last available value is %d", c->name, last_available_id); } else { bool first = true; vrt_value_id last_available_id; clog_debug("<%s> Wait for value %d from dependencies", c->name, c->current_id); /* If there are dependencies we can only process what they've *all* * finished processing. */ last_available_id = vrt_consumer_find_last_dependent_id(c); while (vrt_mod_le(last_available_id, last_consumed_id)) { clog_trace("<%s> Last available value is %d (wait)", c->name, last_available_id); c->yield_count++; rii_check(vrt_yield_strategy_yield (c->yield, first, q->name, c->name)); first = false; last_available_id = vrt_consumer_find_last_dependent_id(c); } c->last_available_id = last_available_id; clog_debug("<%s> Last available value is %d", c->name, last_available_id); } c->batch_count++; /* Once we fall through to here, we know that there are additional * values that we can process. */ clog_trace("<%s> Next value is %d", c->name, c->current_id); return 0; }