Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.

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