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); } } }
static int write_footer_v1(struct save_data *save_data, struct ipset_node_cache *cache, ipset_node_id root) { /* If the root is a terminal node, then we output the terminal value * in place of the (nonexistent) list of nonterminal nodes. */ if (ipset_node_get_type(root) == IPSET_TERMINAL_NODE) { ipset_value value = ipset_terminal_value(root); return write_uint32(save_data->stream, value); } return 0; }
/** * 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); }
static ipset_node_id_t cached_ite(ipset_node_cache_t *cache, ipset_node_id_t f, ipset_node_id_t g, ipset_node_id_t h) { g_d_debug("Applying ITE(%p,%p,%p)", f, g, h); /* * Some trivial cases first. */ /* * If F is a terminal, then we're in one of the following two * cases: * * ITE(1,G,H) = G * ITE(0,G,H) = H */ if (ipset_node_get_type(f) == IPSET_TERMINAL_NODE) { ipset_range_t f_value = ipset_terminal_value(f); ipset_node_id_t result = (f_value == 0)? h: g; g_d_debug("Trivial result = %p", result); return result; } /* * ITE(F,G,G) == G */ if (g == h) { g_d_debug("Trivial result = %p", g); return g; } /* * ITE(F,1,0) = F */ if ((ipset_node_get_type(g) == IPSET_TERMINAL_NODE) && (ipset_node_get_type(h) == IPSET_TERMINAL_NODE)) { ipset_range_t g_value = ipset_terminal_value(g); ipset_range_t h_value = ipset_terminal_value(h); if ((g_value == 1) && (h_value == 0)) { g_d_debug("Trivial result = %p", f); return f; } } /* * Check to see if we've already performed the operation on these * operands. */ ipset_trinary_key_t search_key; ipset_trinary_key_init(&search_key, f, g, h); gpointer found_key; gpointer found_result; gboolean node_exists = g_hash_table_lookup_extended(cache->ite_cache, &search_key, &found_key, &found_result); if (node_exists) { /* * There's a result in the cache, so return it. */ g_d_debug("Existing result = %p", found_result); return found_result; } else { /* * This result doesn't exist yet. Allocate a permanent copy * of the key. Apply the operator, add the result to the * cache, and then return it. */ ipset_trinary_key_t *real_key = g_slice_new(ipset_trinary_key_t); memcpy(real_key, &search_key, sizeof(ipset_trinary_key_t)); ipset_node_id_t result = apply_ite(cache, f, g, h); g_d_debug("NEW result = %p", result); g_hash_table_insert(cache->ite_cache, real_key, result); return result; } }