summaryrefslogtreecommitdiffstats
path: root/libcnary/node.c
diff options
context:
space:
mode:
Diffstat (limited to 'libcnary/node.c')
-rw-r--r--libcnary/node.c120
1 files changed, 107 insertions, 13 deletions
diff --git a/libcnary/node.c b/libcnary/node.c
index 8d3708b..9457548 100644
--- a/libcnary/node.c
+++ b/libcnary/node.c
@@ -61,12 +61,11 @@ node_t node_create(node_t parent, void* data)
61 node->children = NULL; 61 node->children = NULL;
62 62
63 // Pass NULL to create a root node 63 // Pass NULL to create a root node
64 if(parent != NULL) { 64 if (parent != NULL) {
65 // This is a child node so attach it to it's parent 65 // This is a child node so attach it to it's parent
66 error = node_attach(parent, node); 66 error = node_attach(parent, node);
67 if(error < 0) { 67 if (error < 0) {
68 // Unable to attach nodes 68 // Unable to attach nodes
69 printf("ERROR: %d \"Unable to attach nodes\"\n", error);
70 node_destroy(node); 69 node_destroy(node);
71 return NULL; 70 return NULL;
72 } 71 }
@@ -75,15 +74,86 @@ node_t node_create(node_t parent, void* data)
75 return node; 74 return node;
76} 75}
77 76
77static int node_depth_from_root(node_t n)
78{
79 int d = 0;
80 while (n && n->parent) {
81 d++;
82 n = n->parent;
83 if (d > NODE_MAX_DEPTH) return d; // early out
84 }
85 return d;
86}
87
88static int node_subtree_max_depth(node_t root)
89{
90 if (!root) return 0;
91
92 typedef struct { node_t n; int depth; } frame_t;
93 size_t cap = 64, sp = 0;
94 frame_t *st = (frame_t*)malloc(cap * sizeof(*st));
95 if (!st) return NODE_MAX_DEPTH + 1;
96
97 st[sp++] = (frame_t){ root, 0 };
98 int maxd = 0;
99
100 while (sp) {
101 frame_t f = st[--sp];
102 if (f.depth > maxd) maxd = f.depth;
103 if (maxd > NODE_MAX_DEPTH) break;
104
105 if (!f.n->children) continue;
106
107 for (node_t ch = node_first_child(f.n); ch; ch = node_next_sibling(ch)) {
108 if (sp == cap) {
109 cap *= 2;
110 frame_t *tmp = (frame_t*)realloc(st, cap * sizeof(*st));
111 if (!tmp) { maxd = NODE_MAX_DEPTH + 1; goto out; }
112 st = tmp;
113 }
114 st[sp++] = (frame_t){ ch, f.depth + 1 };
115 }
116 }
117
118out:
119 free(st);
120 return maxd;
121}
122
123static int would_create_cycle(node_t parent, node_t child)
124{
125 // if parent is anywhere in child's ancestor chain => cycle
126 for (node_t p = parent; p; p = p->parent) {
127 if (p == child) return 1;
128 }
129 return 0;
130}
131
78int node_attach(node_t parent, node_t child) 132int node_attach(node_t parent, node_t child)
79{ 133{
80 if (!parent || !child) return -1; 134 if (!parent || !child) return NODE_ERR_INVALID_ARG;
81 child->parent = parent; 135
82 if(!parent->children) { 136 // already parented?
137 if (child->parent) return NODE_ERR_PARENT;
138
139 // self/cycle guard
140 if (parent == child) return NODE_ERR_CIRCULAR_REF;
141 if (would_create_cycle(parent, child)) return NODE_ERR_CIRCULAR_REF;
142
143 // depth guard: depth(parent)+1+max_depth(child_subtree) <= NODE_MAX_DEPTH
144 int pd = node_depth_from_root(parent);
145 int cd = node_subtree_max_depth(child);
146 if (pd + 1 + cd > NODE_MAX_DEPTH) {
147 return NODE_ERR_MAX_DEPTH;
148 }
149
150 if (!parent->children) {
83 parent->children = node_list_create(); 151 parent->children = node_list_create();
152 if (!parent->children) return NODE_ERR_NO_MEM;
84 } 153 }
85 int res = node_list_add(parent->children, child); 154 int res = node_list_add(parent->children, child);
86 if (res == 0) { 155 if (res == 0) {
156 child->parent = parent;
87 parent->count++; 157 parent->count++;
88 } 158 }
89 return res; 159 return res;
@@ -91,7 +161,7 @@ int node_attach(node_t parent, node_t child)
91 161
92int node_detach(node_t parent, node_t child) 162int node_detach(node_t parent, node_t child)
93{ 163{
94 if (!parent || !child) return -1; 164 if (!parent || !child) return NODE_ERR_INVALID_ARG;
95 int node_index = node_list_remove(parent->children, child); 165 int node_index = node_list_remove(parent->children, child);
96 if (node_index >= 0) { 166 if (node_index >= 0) {
97 parent->count--; 167 parent->count--;
@@ -101,13 +171,29 @@ int node_detach(node_t parent, node_t child)
101 171
102int node_insert(node_t parent, unsigned int node_index, node_t child) 172int node_insert(node_t parent, unsigned int node_index, node_t child)
103{ 173{
104 if (!parent || !child) return -1; 174 if (!parent || !child) return NODE_ERR_INVALID_ARG;
105 child->parent = parent; 175
106 if(!parent->children) { 176 // already parented?
177 if (child->parent) return NODE_ERR_PARENT;
178
179 // self/cycle guard
180 if (parent == child) return NODE_ERR_CIRCULAR_REF;
181 if (would_create_cycle(parent, child)) return NODE_ERR_CIRCULAR_REF;
182
183 // depth guard: depth(parent)+1+max_depth(child_subtree) <= NODE_MAX_DEPTH
184 int pd = node_depth_from_root(parent);
185 int cd = node_subtree_max_depth(child);
186 if (pd + 1 + cd > NODE_MAX_DEPTH) {
187 return NODE_ERR_MAX_DEPTH;
188 }
189
190 if (!parent->children) {
107 parent->children = node_list_create(); 191 parent->children = node_list_create();
192 if (!parent->children) return NODE_ERR_NO_MEM;
108 } 193 }
109 int res = node_list_insert(parent->children, node_index, child); 194 int res = node_list_insert(parent->children, node_index, child);
110 if (res == 0) { 195 if (res == 0) {
196 child->parent = parent;
111 parent->count++; 197 parent->count++;
112 } 198 }
113 return res; 199 return res;
@@ -186,7 +272,7 @@ node_t node_next_sibling(node_t node)
186 272
187int node_child_position(node_t parent, node_t child) 273int node_child_position(node_t parent, node_t child)
188{ 274{
189 if (!parent || !parent->children || !parent->children->begin || !child) return -1; 275 if (!parent || !parent->children || !parent->children->begin || !child) return NODE_ERR_INVALID_ARG;
190 int node_index = 0; 276 int node_index = 0;
191 int found = 0; 277 int found = 0;
192 node_t ch; 278 node_t ch;
@@ -198,7 +284,7 @@ int node_child_position(node_t parent, node_t child)
198 node_index++; 284 node_index++;
199 } 285 }
200 if (!found) { 286 if (!found) {
201 return -1; 287 return NODE_ERR_NOT_FOUND;
202 } 288 }
203 return node_index; 289 return node_index;
204} 290}
@@ -211,10 +297,18 @@ node_t node_copy_deep(node_t node, copy_func_t copy_func)
211 data = copy_func(node->data); 297 data = copy_func(node->data);
212 } 298 }
213 node_t copy = node_create(NULL, data); 299 node_t copy = node_create(NULL, data);
300 if (!copy) return NULL;
214 node_t ch; 301 node_t ch;
215 for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { 302 for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
216 node_t cc = node_copy_deep(ch, copy_func); 303 node_t cc = node_copy_deep(ch, copy_func);
217 node_attach(copy, cc); 304 if (!cc) {
305 node_destroy(copy);
306 return NULL;
307 }
308 if (node_attach(copy, cc) < 0) {
309 node_destroy(copy);
310 return NULL;
311 }
218 } 312 }
219 return copy; 313 return copy;
220} 314}