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.

536 lines
14 KiB

10 years ago
  1. /* -*- coding: utf-8 -*-
  2. * ----------------------------------------------------------------------
  3. * Copyright © 2010-2013, RedJack, LLC.
  4. * All rights reserved.
  5. *
  6. * Please see the LICENSE.txt file in this distribution for license
  7. * details.
  8. * ----------------------------------------------------------------------
  9. */
  10. #ifndef IPSET_BDD_NODES_H
  11. #define IPSET_BDD_NODES_H
  12. #include <stdio.h>
  13. #include <libcork/core.h>
  14. #include <libcork/ds.h>
  15. /*-----------------------------------------------------------------------
  16. * Preliminaries
  17. */
  18. /**
  19. * Each variable in a BDD is referred to by number.
  20. */
  21. typedef unsigned int ipset_variable;
  22. /**
  23. * Each BDD terminal represents an integer value. The integer must be
  24. * non-negative, but must be within the range of the <i>signed</i>
  25. * integer type.
  26. */
  27. typedef unsigned int ipset_value;
  28. /**
  29. * An identifier for each distinct node in a BDD.
  30. *
  31. * Internal implementation note. Since pointers are aligned to at
  32. * least two bytes, the ID of a terminal node has its LSB set to 1,
  33. * and has the terminal value stored in the remaining bits. The ID of
  34. * a nonterminal node is simply a pointer to the node struct.
  35. */
  36. typedef unsigned int ipset_node_id;
  37. /**
  38. * Nodes can either be terminal or nonterminal.
  39. */
  40. enum ipset_node_type {
  41. IPSET_NONTERMINAL_NODE = 0,
  42. IPSET_TERMINAL_NODE = 1
  43. };
  44. /**
  45. * Return the type of node represented by a particular node ID.
  46. */
  47. #define ipset_node_get_type(node_id) ((node_id) & 0x01)
  48. #define IPSET_NODE_ID_FORMAT "%s%u"
  49. #define IPSET_NODE_ID_VALUES(node_id) \
  50. (ipset_node_get_type((node_id)) == IPSET_NONTERMINAL_NODE? "s": ""), \
  51. ((node_id) >> 1)
  52. /*-----------------------------------------------------------------------
  53. * Terminal nodes
  54. */
  55. /**
  56. * Return the value of a terminal node. The result is undefined if
  57. * the node ID represents a nonterminal.
  58. */
  59. #define ipset_terminal_value(node_id) ((node_id) >> 1)
  60. /**
  61. * Creates a terminal node ID from a terminal value.
  62. */
  63. #define ipset_terminal_node_id(value) \
  64. (((value) << 1) | IPSET_TERMINAL_NODE)
  65. /*-----------------------------------------------------------------------
  66. * Nonterminal nodes
  67. */
  68. /**
  69. * A nonterminal BDD node. This is an inner node of the BDD tree.
  70. * The node represents one variable in an overall variable assignment.
  71. * The node has two children: a low child and a high child. The
  72. * low child is the subtree that applies when the node's variable is
  73. * false or 0; the high child is the subtree that applies when it's
  74. * true or 1.
  75. *
  76. * This type does not take care of ensuring that all BDD nodes are
  77. * reduced; that is handled by the node_cache class.
  78. */
  79. struct ipset_node {
  80. /** The reference count for this node. */
  81. unsigned int refcount;
  82. /** The variable that this node represents. */
  83. ipset_variable variable;
  84. /** The subtree node for when the variable is false. */
  85. ipset_node_id low;
  86. /** The subtree node for when the variable is true. */
  87. ipset_node_id high;
  88. };
  89. /**
  90. * Return the "value" of a nonterminal node. The value of a nonterminal
  91. * is the index into the node array of the cache that the node belongs
  92. * to.
  93. */
  94. #define ipset_nonterminal_value(node_id) ((node_id) >> 1)
  95. /**
  96. * Creates a nonterminal node ID from a nonterminal value.
  97. */
  98. #define ipset_nonterminal_node_id(value) \
  99. (((value) << 1) | IPSET_NONTERMINAL_NODE)
  100. /**
  101. * Print out a node object.
  102. */
  103. void
  104. ipset_node_fprint(FILE *stream, struct ipset_node *node);
  105. /*-----------------------------------------------------------------------
  106. * Node caches
  107. */
  108. /**
  109. * The log2 of the size of each chunk of BDD nodes.
  110. */
  111. /* 16K elements per cache */
  112. #define IPSET_BDD_NODE_CACHE_BIT_SIZE 6
  113. #define IPSET_BDD_NODE_CACHE_SIZE (1 << IPSET_BDD_NODE_CACHE_BIT_SIZE)
  114. #define IPSET_BDD_NODE_CACHE_MASK (IPSET_BDD_NODE_CACHE_SIZE - 1)
  115. /**
  116. * A cache for BDD nodes. By creating and retrieving nodes through
  117. * the cache, we ensure that a BDD is reduced.
  118. */
  119. struct ipset_node_cache {
  120. /** The storage for the nodes managed by this cache. */
  121. cork_array(struct ipset_node *) chunks;
  122. /** The largest nonterminal index that has been handed out. */
  123. ipset_value largest_index;
  124. /** The index of the first node in the free list. */
  125. ipset_value free_list;
  126. /** A cache of the nonterminal nodes, keyed by their contents. */
  127. struct cork_hash_table *node_cache;
  128. };
  129. /**
  130. * Returns the index of the chunk that the given nonterminal lives in.
  131. */
  132. #define ipset_nonterminal_chunk_index(index) \
  133. ((index) >> IPSET_BDD_NODE_CACHE_BIT_SIZE)
  134. /**
  135. * Returns the offset of the given nonterminal within its chunk.
  136. */
  137. #define ipset_nonterminal_chunk_offset(index) \
  138. ((index) & IPSET_BDD_NODE_CACHE_MASK)
  139. /**
  140. * Returns a pointer to the ipset_node for a given nonterminal index.
  141. */
  142. #define ipset_node_cache_get_nonterminal_by_index(cache, index) \
  143. (&cork_array_at(&(cache)->chunks, ipset_nonterminal_chunk_index((index))) \
  144. [ipset_nonterminal_chunk_offset((index))])
  145. /**
  146. * Returns the ipset_node for a given nonterminal node ID.
  147. */
  148. #define ipset_node_cache_get_nonterminal(cache, node_id) \
  149. (ipset_node_cache_get_nonterminal_by_index \
  150. ((cache), ipset_nonterminal_value((node_id))))
  151. /**
  152. * Create a new node cache.
  153. */
  154. struct ipset_node_cache *
  155. ipset_node_cache_new(void);
  156. /**
  157. * Free a node cache.
  158. */
  159. void
  160. ipset_node_cache_free(struct ipset_node_cache *cache);
  161. /**
  162. * Create a new nonterminal node with the given contents, returning
  163. * its ID. This function ensures that there is only one node with the
  164. * given contents in this cache.
  165. *
  166. * Steals references to low and high.
  167. */
  168. ipset_node_id
  169. ipset_node_cache_nonterminal(struct ipset_node_cache *cache,
  170. ipset_variable variable,
  171. ipset_node_id low, ipset_node_id high);
  172. /**
  173. * Increment the reference count of a nonterminal node. (This is a
  174. * no-op for terminal nodes.)
  175. */
  176. ipset_node_id
  177. ipset_node_incref(struct ipset_node_cache *cache, ipset_node_id node);
  178. /**
  179. * Decrement the reference count of a nonterminal node. If the
  180. * reference count reaches 0, the storage for the node will be
  181. * reclaimed. (This is a no-op for terminal nodes.)
  182. */
  183. void
  184. ipset_node_decref(struct ipset_node_cache *cache, ipset_node_id node);
  185. /**
  186. * Return the number of nodes that are reachable from the given node.
  187. * This does not include duplicates if a node is reachable via more
  188. * than one path.
  189. */
  190. size_t
  191. ipset_node_reachable_count(const struct ipset_node_cache *cache,
  192. ipset_node_id node);
  193. /**
  194. * Return the amount of memory used by the nodes in the given BDD.
  195. */
  196. size_t
  197. ipset_node_memory_size(const struct ipset_node_cache *cache,
  198. ipset_node_id node);
  199. /**
  200. * Load a BDD from an input stream. The error field is filled in with
  201. * an error condition is the BDD can't be read for any reason.
  202. */
  203. ipset_node_id
  204. ipset_node_cache_load(FILE *stream, struct ipset_node_cache *cache);
  205. /**
  206. * Save a BDD to an output stream. This encodes the set using only
  207. * those nodes that are reachable from the BDD's root node.
  208. */
  209. int
  210. ipset_node_cache_save(struct cork_stream_consumer *stream,
  211. struct ipset_node_cache *cache, ipset_node_id node);
  212. /**
  213. * Compare two BDD nodes, possibly from different caches, for equality.
  214. */
  215. bool
  216. ipset_node_cache_nodes_equal(const struct ipset_node_cache *cache1,
  217. ipset_node_id node1,
  218. const struct ipset_node_cache *cache2,
  219. ipset_node_id node2);
  220. /**
  221. * Save a GraphViz dot graph for a BDD. The graph script is written
  222. * to the given output stream. This graph only includes those nodes
  223. * that are reachable from the BDD's root node.
  224. */
  225. int
  226. ipset_node_cache_save_dot(struct cork_stream_consumer *stream,
  227. struct ipset_node_cache *cache, ipset_node_id node);
  228. /*-----------------------------------------------------------------------
  229. * BDD operators
  230. */
  231. /**
  232. * A function that provides the value for each variable in a BDD.
  233. */
  234. typedef bool
  235. (*ipset_assignment_func)(const void *user_data,
  236. ipset_variable variable);
  237. /**
  238. * An assignment function that gets the variable values from an array
  239. * of gbooleans.
  240. */
  241. bool
  242. ipset_bool_array_assignment(const void *user_data,
  243. ipset_variable variable);
  244. /**
  245. * An assignment function that gets the variable values from an array
  246. * of bits.
  247. */
  248. bool
  249. ipset_bit_array_assignment(const void *user_data,
  250. ipset_variable variable);
  251. /**
  252. * Evaluate a BDD given a particular assignment of variables.
  253. */
  254. ipset_value
  255. ipset_node_evaluate(const struct ipset_node_cache *cache, ipset_node_id node,
  256. ipset_assignment_func assignment,
  257. const void *user_data);
  258. /**
  259. * Add an assignment to the BDD.
  260. */
  261. ipset_node_id
  262. ipset_node_insert(struct ipset_node_cache *cache, ipset_node_id node,
  263. ipset_assignment_func assignment,
  264. const void *user_data, ipset_variable variable_count,
  265. ipset_value value);
  266. /*-----------------------------------------------------------------------
  267. * Variable assignments
  268. */
  269. /**
  270. * Each variable in the input to a Boolean function can be true or
  271. * false; it can also be EITHER, which means that the variable can be
  272. * either true or false in a particular assignment without affecting
  273. * the result of the function.
  274. */
  275. enum ipset_tribool {
  276. IPSET_FALSE = 0,
  277. IPSET_TRUE = 1,
  278. IPSET_EITHER = 2
  279. };
  280. /**
  281. * An assignment is a mapping of variable numbers to Boolean values.
  282. * It represents an input to a Boolean function that maps to a
  283. * particular output value. Each variable in the input to a Boolean
  284. * function can be true or false; it can also be EITHER, which means
  285. * that the variable can be either true or false in a particular
  286. * assignment without affecting the result of the function.
  287. */
  288. struct ipset_assignment {
  289. /**
  290. * The underlying variable assignments are stored in a vector of
  291. * tribools. Every variable that has a true or false value must
  292. * appear in the vector. Variables that are EITHER only have to
  293. * appear to prevent gaps in the vector. Any variables outside
  294. * the range of the vector are assumed to be EITHER.
  295. */
  296. cork_array(enum ipset_tribool) values;
  297. };
  298. /**
  299. * Create a new assignment where all variables are indeterminite.
  300. */
  301. struct ipset_assignment *
  302. ipset_assignment_new();
  303. /**
  304. * Free an assignment.
  305. */
  306. void
  307. ipset_assignment_free(struct ipset_assignment *assignment);
  308. /**
  309. * Compare two assignments for equality.
  310. */
  311. bool
  312. ipset_assignment_equal(const struct ipset_assignment *assignment1,
  313. const struct ipset_assignment *assignment2);
  314. /**
  315. * Set the given variable, and all higher variables, to the EITHER
  316. * value.
  317. */
  318. void
  319. ipset_assignment_cut(struct ipset_assignment *assignment, ipset_variable var);
  320. /**
  321. * Clear the assignment, setting all variables to the EITHER value.
  322. */
  323. void
  324. ipset_assignment_clear(struct ipset_assignment *assignment);
  325. /**
  326. * Return the value assigned to a particular variable.
  327. */
  328. enum ipset_tribool
  329. ipset_assignment_get(struct ipset_assignment *assignment, ipset_variable var);
  330. /**
  331. * Set the value assigned to a particular variable.
  332. */
  333. void
  334. ipset_assignment_set(struct ipset_assignment *assignment,
  335. ipset_variable var, enum ipset_tribool value);
  336. /*-----------------------------------------------------------------------
  337. * Expanded assignments
  338. */
  339. /**
  340. * An iterator for expanding a variable assignment. For each EITHER
  341. * variable in the assignment, the iterator yields a result with both
  342. * values.
  343. */
  344. struct ipset_expanded_assignment {
  345. /** Whether there are any more assignments in this iterator. */
  346. bool finished;
  347. /**
  348. * The variable values in the current expanded assignment. Since
  349. * there won't be any EITHERs in the expanded assignment, we can
  350. * use a byte array, and represent each variable by a single bit.
  351. */
  352. struct cork_buffer values;
  353. /**
  354. * An array containing all of the variables that are EITHER in the
  355. * original assignment.
  356. */
  357. cork_array(ipset_variable) eithers;
  358. };
  359. /**
  360. * Return an iterator that expands a variable assignment. For each
  361. * variable that's EITHER in the assignment, the iterator yields a
  362. * result with both values. The iterator will ensure that the
  363. * specified number of variables are given concrete values.
  364. */
  365. struct ipset_expanded_assignment *
  366. ipset_assignment_expand(const struct ipset_assignment *assignment,
  367. ipset_variable var_count);
  368. /**
  369. * Free an expanded assignment iterator.
  370. */
  371. void
  372. ipset_expanded_assignment_free(struct ipset_expanded_assignment *exp);
  373. /**
  374. * Advance the iterator to the next assignment.
  375. */
  376. void
  377. ipset_expanded_assignment_advance(struct ipset_expanded_assignment *exp);
  378. /*-----------------------------------------------------------------------
  379. * BDD iterators
  380. */
  381. /**
  382. * An iterator for walking through the assignments for a given BDD
  383. * node.
  384. *
  385. * The iterator walks through each path in the BDD tree, stopping at
  386. * each terminal node. Each time we reach a terminal node, we yield a
  387. * new ipset_assignment object representing the assignment of variables
  388. * along the current path.
  389. *
  390. * We maintain a stack of nodes leading to the current terminal, which
  391. * allows us to backtrack up the path to find the next terminal when
  392. * we increment the iterator.
  393. */
  394. struct ipset_bdd_iterator {
  395. /** Whether there are any more assignments in this iterator. */
  396. bool finished;
  397. /** The node cache that we're iterating through. */
  398. struct ipset_node_cache *cache;
  399. /**
  400. * The sequence of nonterminal nodes leading to the current
  401. * terminal.
  402. */
  403. cork_array(ipset_node_id) stack;
  404. /** The current assignment. */
  405. struct ipset_assignment *assignment;
  406. /**
  407. * The value of the BDD's function when applied to the current
  408. * assignment.
  409. */
  410. ipset_value value;
  411. };
  412. /**
  413. * Return an iterator that yields all of the assignments in the given
  414. * BDD. The iterator contains two items of interest. The first is an
  415. * ipset_assignment providing the value that each variable takes, while
  416. * the second is the terminal value that is the result of the BDD's
  417. * function when applied to that variable assignment.
  418. */
  419. struct ipset_bdd_iterator *
  420. ipset_node_iterate(struct ipset_node_cache *cache, ipset_node_id root);
  421. /**
  422. * Free a BDD iterator.
  423. */
  424. void
  425. ipset_bdd_iterator_free(struct ipset_bdd_iterator *iterator);
  426. /**
  427. * Advance the iterator to the next assignment.
  428. */
  429. void
  430. ipset_bdd_iterator_advance(struct ipset_bdd_iterator *iterator);
  431. #endif /* IPSET_BDD_NODES_H */