You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

129 lines
4.4 KiB

10 years ago
  1. /* -*- coding: utf-8 -*-
  2. * ----------------------------------------------------------------------
  3. * Copyright © 2010-2012, RedJack, LLC.
  4. * All rights reserved.
  5. *
  6. * Please see the LICENSE.txt file in this distribution for license
  7. * details.
  8. * ----------------------------------------------------------------------
  9. */
  10. #include <libcork/core.h>
  11. #include "ipset/bdd/nodes.h"
  12. #include "ipset/logging.h"
  13. /**
  14. * Add the given node ID to the node stack, and trace down from it
  15. * until we find a terminal node. Assign values to the variables for
  16. * each nonterminal that encounter along the way. We check low edges
  17. * first, so each new variable we encounter will be assigned FALSE.
  18. * (The high edges will be checked eventually by a call to the
  19. * ipset_bdd_iterator_advance() function.)
  20. */
  21. static void
  22. add_node(struct ipset_bdd_iterator *iterator, ipset_node_id node_id)
  23. {
  24. /* Keep tracing down low edges until we reach a terminal. */
  25. while (ipset_node_get_type(node_id) == IPSET_NONTERMINAL_NODE) {
  26. /* Add this nonterminal node to the stack, and trace down
  27. * further into the tree. We check low edges first, so set the
  28. * node's variable to FALSE in the assignment. */
  29. struct ipset_node *node =
  30. ipset_node_cache_get_nonterminal(iterator->cache, node_id);
  31. cork_array_append(&iterator->stack, node_id);
  32. ipset_assignment_set(iterator->assignment, node->variable, false);
  33. node_id = node->low;
  34. }
  35. /* Once we find a terminal node, save it away in the iterator result
  36. * and return. */
  37. iterator->value = ipset_terminal_value(node_id);
  38. }
  39. struct ipset_bdd_iterator *
  40. ipset_node_iterate(struct ipset_node_cache *cache, ipset_node_id root)
  41. {
  42. /* First allocate the iterator itself, and all of its contained
  43. * fields. */
  44. struct ipset_bdd_iterator *iterator =
  45. cork_new(struct ipset_bdd_iterator);
  46. iterator->finished = false;
  47. iterator->cache = cache;
  48. cork_array_init(&iterator->stack);
  49. iterator->assignment = ipset_assignment_new();
  50. /* Then add the root node to the iterator, tracing down until we
  51. * find the first terminal node. */
  52. add_node(iterator, root);
  53. return iterator;
  54. }
  55. void
  56. ipset_bdd_iterator_free(struct ipset_bdd_iterator *iterator)
  57. {
  58. cork_array_done(&iterator->stack);
  59. ipset_assignment_free(iterator->assignment);
  60. free(iterator);
  61. }
  62. void
  63. ipset_bdd_iterator_advance(struct ipset_bdd_iterator *iterator)
  64. {
  65. /* If we're already at the end of the iterator, don't do anything. */
  66. if (CORK_UNLIKELY(iterator->finished)) {
  67. return;
  68. }
  69. /* We look at the last node in the stack. If it's currently
  70. * assigned a false value, then we track down its true branch. If
  71. * it's got a true branch, then we pop it off and check the next to
  72. * last node. */
  73. DEBUG("Advancing BDD iterator");
  74. while (cork_array_size(&iterator->stack) > 0) {
  75. ipset_node_id last_node_id =
  76. cork_array_at
  77. (&iterator->stack, cork_array_size(&iterator->stack) - 1);
  78. struct ipset_node *last_node =
  79. ipset_node_cache_get_nonterminal(iterator->cache, last_node_id);
  80. enum ipset_tribool current_value =
  81. ipset_assignment_get(iterator->assignment, last_node->variable);
  82. /* The current value can't be EITHER, because we definitely
  83. * assign a TRUE or FALSE to the variables of the nodes that we
  84. * encounter. */
  85. if (current_value == IPSET_TRUE) {
  86. /* We've checked both outgoing edges for this node, so pop
  87. * it off and look at its parent. */
  88. iterator->stack.size--;
  89. /* Before continuing, reset this node's variable to
  90. * indeterminate in the assignment. */
  91. ipset_assignment_set
  92. (iterator->assignment, last_node->variable, IPSET_EITHER);
  93. } else {
  94. /* We've checked this node's low edge, but not its high
  95. * edge. Set the variable to TRUE in the assignment, and
  96. * add the high edge's node to the node stack. */
  97. ipset_assignment_set
  98. (iterator->assignment, last_node->variable, IPSET_TRUE);
  99. add_node(iterator, last_node->high);
  100. return;
  101. }
  102. }
  103. /* If we fall through then we ran out of nodes to check. That means
  104. * the iterator is done! */
  105. iterator->finished = true;
  106. }