Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

423 rader
11 KiB

  1. typedef struct RDIC_Node RDIC_Node;
  2. struct RDIC_Node {
  3. unsigned int generation;
  4. RDIC_Node *parent; // Ease of access.
  5. RDIC_Node *sibling;
  6. RDIC_Node *first_child;
  7. //RDIC_Node *last_child; // Ease of access.
  8. //RDIC_Node *previous_sibling; // Ease of access.
  9. #if 0
  10. NOTE(Zelaven):
  11. Pre-order traversal is self-then-first_child.
  12. Post-order traversal is first_child-then-self.
  13. The sibling always comes last.
  14. #endif
  15. };
  16. typedef struct {
  17. RDIC_Node *node;
  18. unsigned int generation;
  19. } RDIC_Node_Reference;
  20. // IDEA(Zelaven): Can the node reference hold only the pointer, and the pointed-to node hold a back-pointer to the reference?
  21. // There is only one valid reference at a time, so the reference validity should be easy to determine without use of generations.
  22. // NOTE(Zelaven): This idea is bad because it assumes that a reference won't be reallocated. Generations are much more reliable.
  23. typedef struct {
  24. RDIC_Node *root;
  25. RDIC_Node *node_freelist;
  26. RDIC_Node *current_parent;
  27. RDIC_Node *frame_current_node;
  28. } RDIC_Context;
  29. static inline int gui_node_references_equal(
  30. RDIC_Node_Reference n,
  31. RDIC_Node_Reference m)
  32. {
  33. return (n.node == m.node) && (n.generation == m.generation);
  34. }
  35. enum {
  36. RDIC_GET_NODE__NODES_COLLECTED = 1 << 0,
  37. RDIC_GET_NODE__NODE_ALLOCATED = 1 << 1,
  38. RDIC_GET_NODE__OUT_OF_FREELIST = 1 << 2,
  39. };
  40. void rdic_cull_subtrees(
  41. RDIC_Context context[static 1],
  42. RDIC_Node *subtree);
  43. RDIC_Node_Reference rdic_context_start_frame(
  44. RDIC_Context context[static 1],
  45. RDIC_Node_Reference last_root);
  46. int rdic_context_finish_frame(
  47. RDIC_Context context[static 1]);
  48. RDIC_Node_Reference rdic_get_node(
  49. RDIC_Context context[static 1],
  50. RDIC_Node_Reference node_reference,
  51. int *out_flags); // NOTE(Zelaven): NULL-safe.
  52. void rdic_push_parent(
  53. RDIC_Context context[static 1],
  54. RDIC_Node_Reference parent);
  55. int rdic_pop_parent(
  56. RDIC_Context context[static 1]);
  57. #ifndef NULL
  58. #warning "NULL not defined. Please define NULL or include stddef."
  59. #define NULL ((void*)0)
  60. #endif
  61. #ifdef RDIC_IMPLEMENTATION
  62. // TODO(Zelaven): Offer a macro that enables runtime NULL-guards.
  63. // ---
  64. /* *** Information for those reading this source ***
  65. * Procedure parameters such as `RDIC_Context context[static 1]` may look
  66. * strange, but they aren't as evil as they look. Arrays degrade to pointers,
  67. * and the static keyword simply requires that the array has a minimum size. In
  68. * this case we use this to pass a pointer that may not be NULL. Note that this
  69. * is just a compile time check and guard clauses for runtime NULL can still
  70. * make plenty of sense.
  71. */
  72. void rdic_cull_subtrees(
  73. RDIC_Context context[static 1],
  74. RDIC_Node *subtree)
  75. {
  76. assert(context != NULL);
  77. // NOTE(Zelaven):
  78. // This code collects an entire subtree.
  79. // It requires that the initial node has a NULL sibling to limit its
  80. // operation to only the subtree.
  81. // The way it operates is that it uses the parent pointers to maintain a
  82. // stack of "collect later" whenever a node in the tree has a sibling.
  83. // When a subtree is finished it "pops" an element from the stack and
  84. // continues collection from there.
  85. RDIC_Node *current = subtree;
  86. // NOTE(Zelaven): Top of our "stack" of "collect later".
  87. RDIC_Node *stashed = NULL;
  88. while(current != NULL)
  89. {
  90. RDIC_Node *next = NULL;
  91. if(current->first_child != NULL)
  92. {
  93. if(current->sibling != NULL)
  94. {
  95. // Has both a child and a sibling - we need to save one for later.
  96. // We "push" the sibling onto our "stack".
  97. current->sibling->parent = stashed;
  98. stashed = current->sibling;
  99. }
  100. // We always take the child as the next node.
  101. next = current->first_child;
  102. }
  103. else if(current->sibling != NULL)
  104. {
  105. // No child but we have a sibling, then we just continue with the sibling.
  106. next = current->sibling;
  107. }
  108. else
  109. {
  110. // A node with no child and no sibling. Time to "pop" from out "stack".
  111. next = stashed;
  112. if(stashed != NULL)
  113. {
  114. stashed = stashed->parent;
  115. }
  116. }
  117. current->sibling = context->node_freelist;
  118. current->generation += 1; // Invalidate references.
  119. context->node_freelist = current;
  120. current = next;
  121. }
  122. }
  123. // ---
  124. RDIC_Node_Reference rdic_context_start_frame(
  125. RDIC_Context context[static 1],
  126. RDIC_Node_Reference last_root)
  127. {
  128. RDIC_Node_Reference node_reference = last_root;
  129. int need_new_node = (last_root.node == NULL)
  130. || (last_root.generation != last_root.node->generation);
  131. if(need_new_node && context->node_freelist == NULL)
  132. {
  133. return (RDIC_Node_Reference){0};
  134. }
  135. if(need_new_node)
  136. {
  137. RDIC_Node_Reference result;
  138. result.node = context->node_freelist;
  139. result.generation = result.node->generation;
  140. context->node_freelist = context->node_freelist->sibling;
  141. RDIC_Node *root = result.node;
  142. root->sibling = NULL;
  143. root->parent = root;
  144. node_reference = result;
  145. }
  146. context->frame_current_node = node_reference.node;
  147. context->root = node_reference.node;
  148. context->current_parent = context->root;
  149. return node_reference;
  150. }
  151. // TODO(Zelaven): This is almost 1-1 with pop_parent, so consider compression.
  152. // TODO(Zelaven): pop parents until it gets to a node that is a child of the
  153. // root. This is so that all the stragglers of partial parents can be collected.
  154. int rdic_context_finish_frame(
  155. RDIC_Context context[static 1])
  156. {
  157. assert(context->current_parent == context->root);
  158. RDIC_Node *last_root_child = NULL;
  159. RDIC_Node *first_straggler = NULL;
  160. if(context->frame_current_node == context->root)
  161. {
  162. last_root_child = context->root->first_child;
  163. first_straggler = last_root_child;
  164. }
  165. else
  166. {
  167. last_root_child = context->frame_current_node;
  168. first_straggler = last_root_child->sibling;
  169. last_root_child->sibling = NULL;
  170. }
  171. int nodes_collected = 0;
  172. if(last_root_child == NULL)
  173. {
  174. return 0;
  175. }
  176. assert(last_root_child->parent == context->root);
  177. if(first_straggler != NULL)
  178. {
  179. rdic_cull_subtrees(context, first_straggler);
  180. RDIC_Node *root = context->root;
  181. if(first_straggler == root->first_child)
  182. {
  183. root->first_child = NULL;
  184. }
  185. nodes_collected = RDIC_GET_NODE__NODES_COLLECTED;
  186. }
  187. return nodes_collected;
  188. }
  189. // ---
  190. RDIC_Node_Reference rdic_get_node(
  191. RDIC_Context context[static 1],
  192. RDIC_Node_Reference node_reference,
  193. int *out_flags) // NOTE(Zelaven): NULL-safe.
  194. {
  195. //assert(context->current_parent != NULL);
  196. if(context->current_parent == NULL)
  197. {
  198. return (RDIC_Node_Reference){0};
  199. }
  200. int reference_is_valid =
  201. (node_reference.node != NULL)
  202. && (node_reference.generation == node_reference.node->generation);
  203. int first_node_of_parent =
  204. (context->current_parent == context->frame_current_node);
  205. // NOTE(Zelaven): If the node is being moved to another parent, then we want
  206. // to invalidate it to ensure consistent behavior.
  207. reference_is_valid =
  208. reference_is_valid
  209. && (context->current_parent == node_reference.node->parent);
  210. if(!reference_is_valid && context->node_freelist == NULL)
  211. {
  212. if(out_flags != NULL)
  213. {
  214. *out_flags = RDIC_GET_NODE__OUT_OF_FREELIST;
  215. }
  216. return (RDIC_Node_Reference){0};
  217. }
  218. if(reference_is_valid)
  219. {
  220. RDIC_Node *subroot_to_cull = NULL;
  221. if(first_node_of_parent)
  222. {
  223. // NOTE(Zelaven): Here I need to collect any preceding stale nodes.
  224. subroot_to_cull = context->current_parent->first_child;
  225. context->frame_current_node = node_reference.node;
  226. context->current_parent->first_child = node_reference.node;
  227. }
  228. else
  229. {
  230. // NOTE(Zelaven): Skip (and collect) nodes between last and new nodes.
  231. subroot_to_cull = context->frame_current_node->sibling;
  232. context->frame_current_node->sibling = node_reference.node;
  233. context->frame_current_node = node_reference.node;
  234. }
  235. if(subroot_to_cull != node_reference.node)
  236. {
  237. if(out_flags != NULL)
  238. {
  239. *out_flags = RDIC_GET_NODE__NODES_COLLECTED;
  240. }
  241. }
  242. while(subroot_to_cull != node_reference.node)
  243. {
  244. // NOTE(Zelaven): Here we want to cull a subtree at a time because we are
  245. // only skipping a certain quantity subtrees.
  246. // TODO(Zelaven): Would it be preferable to "detach" the last node to cull
  247. // and just to a single call to rdic_cull_subtrees?
  248. // It really depends on what is faster, so that is profiling territory.
  249. RDIC_Node *current = subroot_to_cull;
  250. subroot_to_cull = subroot_to_cull->sibling;
  251. current->sibling = NULL;
  252. rdic_cull_subtrees(context, current);
  253. }
  254. }
  255. else if(!reference_is_valid)
  256. {
  257. RDIC_Node_Reference new_reference = {0};
  258. { // Make new node
  259. // NOTE(Zelaven): We guard against empty freelist, so this is safe.
  260. RDIC_Node_Reference result = {0};
  261. result.node = context->node_freelist;
  262. result.generation = result.node->generation;
  263. context->node_freelist = context->node_freelist->sibling;
  264. // TODO(Zelaven): Should the various fields be filled out here, or outside?
  265. // What will be the strategy to avoid redundant work?
  266. // Doing it here certainly could avoid redundant work, assuming that all
  267. // the styling and other fields will stay stable across frames.
  268. // That will likely be the common case, but it may be necessary to expose
  269. // reference_is_valid to the caller, so that the caller can decide if it
  270. // should be used to avoid redundancy of work, whatever that means for
  271. // the given node.
  272. // I suppose that the caller can already determine that by comparing the
  273. // last_reference with the new_reference, and if they are different then
  274. // all work must be done.
  275. new_reference = result;
  276. }
  277. RDIC_Node *new_node = new_reference.node;
  278. if(first_node_of_parent)
  279. {
  280. RDIC_Node *parent = context->current_parent;
  281. RDIC_Node *former_first_child = parent->first_child;
  282. parent->first_child = new_node;
  283. new_node->sibling = former_first_child;
  284. new_node->parent = parent;
  285. }
  286. else
  287. {
  288. RDIC_Node *current = context->frame_current_node;
  289. RDIC_Node *old_sibling = current->sibling;
  290. current->sibling = new_node;
  291. new_node->sibling = old_sibling;
  292. new_node->parent = context->current_parent;
  293. }
  294. new_node->first_child = NULL;
  295. context->frame_current_node = new_node;
  296. node_reference = new_reference;
  297. if(out_flags != NULL)
  298. {
  299. *out_flags = RDIC_GET_NODE__NODE_ALLOCATED;
  300. }
  301. }
  302. return node_reference;
  303. }
  304. // ---
  305. void rdic_push_parent(
  306. RDIC_Context context[static 1],
  307. RDIC_Node_Reference parent)
  308. {
  309. //assert(parent.node != NULL);
  310. if(parent.node == NULL)
  311. {
  312. return;
  313. }
  314. context->current_parent = parent.node;
  315. }
  316. int rdic_pop_parent(
  317. RDIC_Context context[static 1])
  318. {
  319. RDIC_Node *current_parent = context->current_parent;
  320. RDIC_Node *last_child = NULL;
  321. RDIC_Node *first_straggler = NULL;
  322. if(context->frame_current_node == context->current_parent)
  323. {
  324. last_child = current_parent->first_child;
  325. first_straggler = last_child;
  326. }
  327. else
  328. {
  329. last_child = context->frame_current_node;
  330. first_straggler = last_child->sibling;
  331. last_child->sibling = NULL;
  332. }
  333. int nodes_collected = 0;
  334. if(last_child != NULL)
  335. {
  336. assert(last_child->parent == context->current_parent);
  337. if(first_straggler != NULL)
  338. {
  339. rdic_cull_subtrees(context, first_straggler);
  340. if(first_straggler == current_parent->first_child)
  341. {
  342. current_parent->first_child = NULL;
  343. }
  344. nodes_collected = RDIC_GET_NODE__NODES_COLLECTED;
  345. }
  346. }
  347. context->frame_current_node = context->current_parent;
  348. context->current_parent = context->current_parent->parent;
  349. assert(context->current_parent != NULL);
  350. return nodes_collected;
  351. }
  352. #endif /* RDIC_IMPLEMENTATION */