/src/libplist/libcnary/node.c
Line | Count | Source |
1 | | /* |
2 | | * node.c |
3 | | * |
4 | | * Created on: Mar 7, 2011 |
5 | | * Author: posixninja |
6 | | * |
7 | | * Copyright (c) 2011 Joshua Hill. All Rights Reserved. |
8 | | * |
9 | | * This library is free software; you can redistribute it and/or |
10 | | * modify it under the terms of the GNU Lesser General Public |
11 | | * License as published by the Free Software Foundation; either |
12 | | * version 2.1 of the License, or (at your option) any later version. |
13 | | * |
14 | | * This library is distributed in the hope that it will be useful, |
15 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 | | * Lesser General Public License for more details. |
18 | | * |
19 | | * You should have received a copy of the GNU Lesser General Public |
20 | | * License along with this library; if not, write to the Free Software |
21 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
22 | | */ |
23 | | #include <stdio.h> |
24 | | #include <stdlib.h> |
25 | | #include <string.h> |
26 | | |
27 | | #include "node.h" |
28 | | #include "node_list.h" |
29 | | |
30 | | void node_destroy(node_t node) |
31 | 237k | { |
32 | 237k | if(!node) return; |
33 | | |
34 | 237k | if (node->children && node->children->count > 0) { |
35 | 0 | node_t ch; |
36 | 0 | while ((ch = node->children->begin)) { |
37 | 0 | node_list_remove(node->children, ch); |
38 | 0 | node_destroy(ch); |
39 | 0 | } |
40 | 0 | } |
41 | 237k | node_list_destroy(node->children); |
42 | 237k | node->children = NULL; |
43 | | |
44 | 237k | free(node); |
45 | 237k | } |
46 | | |
47 | | node_t node_create(node_t parent, void* data) |
48 | 237k | { |
49 | 237k | int error = 0; |
50 | | |
51 | 237k | node_t node = (node_t)calloc(1, sizeof(struct node)); |
52 | 237k | if (node == NULL) { |
53 | 0 | return NULL; |
54 | 0 | } |
55 | | |
56 | 237k | node->data = data; |
57 | 237k | node->next = NULL; |
58 | 237k | node->prev = NULL; |
59 | 237k | node->count = 0; |
60 | 237k | node->parent = NULL; |
61 | 237k | node->children = NULL; |
62 | | |
63 | | // Pass NULL to create a root node |
64 | 237k | if (parent != NULL) { |
65 | | // This is a child node so attach it to it's parent |
66 | 0 | error = node_attach(parent, node); |
67 | 0 | if (error < 0) { |
68 | | // Unable to attach nodes |
69 | 0 | node_destroy(node); |
70 | 0 | return NULL; |
71 | 0 | } |
72 | 0 | } |
73 | | |
74 | 237k | return node; |
75 | 237k | } |
76 | | |
77 | | static int node_depth_from_root(node_t n) |
78 | 235k | { |
79 | 235k | int d = 0; |
80 | 235k | while (n && n->parent) { |
81 | 0 | d++; |
82 | 0 | n = n->parent; |
83 | 0 | if (d > NODE_MAX_DEPTH) return d; // early out |
84 | 0 | } |
85 | 235k | return d; |
86 | 235k | } |
87 | | |
88 | | static int node_subtree_max_depth(node_t root) |
89 | 235k | { |
90 | 235k | if (!root) return 0; |
91 | | |
92 | 235k | typedef struct { node_t n; int depth; } frame_t; |
93 | 235k | size_t cap = 64, sp = 0; |
94 | 235k | frame_t *st = (frame_t*)malloc(cap * sizeof(*st)); |
95 | 235k | if (!st) return NODE_MAX_DEPTH + 1; |
96 | | |
97 | 235k | st[sp++] = (frame_t){ root, 0 }; |
98 | 235k | int maxd = 0; |
99 | | |
100 | 1.60M | while (sp) { |
101 | 1.36M | frame_t f = st[--sp]; |
102 | 1.36M | if (f.depth > maxd) maxd = f.depth; |
103 | 1.36M | if (maxd > NODE_MAX_DEPTH) break; |
104 | | |
105 | 1.36M | if (!f.n->children) continue; |
106 | | |
107 | 1.20M | for (node_t ch = node_first_child(f.n); ch; ch = node_next_sibling(ch)) { |
108 | 1.12M | if (sp == cap) { |
109 | 1.83k | cap *= 2; |
110 | 1.83k | frame_t *tmp = (frame_t*)realloc(st, cap * sizeof(*st)); |
111 | 1.83k | if (!tmp) { maxd = NODE_MAX_DEPTH + 1; goto out; } |
112 | 1.83k | st = tmp; |
113 | 1.83k | } |
114 | 1.12M | st[sp++] = (frame_t){ ch, f.depth + 1 }; |
115 | 1.12M | } |
116 | 70.3k | } |
117 | | |
118 | 235k | out: |
119 | 235k | free(st); |
120 | 235k | return maxd; |
121 | 235k | } |
122 | | |
123 | | static int would_create_cycle(node_t parent, node_t child) |
124 | 235k | { |
125 | | // if parent is anywhere in child's ancestor chain => cycle |
126 | 470k | for (node_t p = parent; p; p = p->parent) { |
127 | 235k | if (p == child) return 1; |
128 | 235k | } |
129 | 235k | return 0; |
130 | 235k | } |
131 | | |
132 | | int node_attach(node_t parent, node_t child) |
133 | 235k | { |
134 | 235k | if (!parent || !child) return NODE_ERR_INVALID_ARG; |
135 | | |
136 | | // already parented? |
137 | 235k | if (child->parent) return NODE_ERR_PARENT; |
138 | | |
139 | | // self/cycle guard |
140 | 235k | if (parent == child) return NODE_ERR_CIRCULAR_REF; |
141 | 235k | 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 | 235k | int pd = node_depth_from_root(parent); |
145 | 235k | int cd = node_subtree_max_depth(child); |
146 | 235k | if (pd + 1 + cd > NODE_MAX_DEPTH) { |
147 | 0 | return NODE_ERR_MAX_DEPTH; |
148 | 0 | } |
149 | | |
150 | 235k | if (!parent->children) { |
151 | 12.8k | parent->children = node_list_create(); |
152 | 12.8k | if (!parent->children) return NODE_ERR_NO_MEM; |
153 | 12.8k | } |
154 | 235k | int res = node_list_add(parent->children, child); |
155 | 235k | if (res == 0) { |
156 | 235k | child->parent = parent; |
157 | 235k | parent->count++; |
158 | 235k | } |
159 | 235k | return res; |
160 | 235k | } |
161 | | |
162 | | int node_detach(node_t parent, node_t child) |
163 | 235k | { |
164 | 235k | if (!parent || !child) return NODE_ERR_INVALID_ARG; |
165 | 235k | if (!parent->children) return NODE_ERR_NOT_FOUND; |
166 | 235k | if (child->parent && child->parent != parent) return NODE_ERR_PARENT; |
167 | | |
168 | 235k | int node_index = node_list_remove(parent->children, child); |
169 | 235k | if (node_index >= 0) { |
170 | 235k | if (parent->count > 0) parent->count--; |
171 | 235k | child->parent = NULL; |
172 | 235k | child->prev = NULL; |
173 | 235k | child->next = NULL; |
174 | 235k | } |
175 | 235k | return node_index; |
176 | 235k | } |
177 | | |
178 | | int node_insert(node_t parent, unsigned int node_index, node_t child) |
179 | 0 | { |
180 | 0 | if (!parent || !child) return NODE_ERR_INVALID_ARG; |
181 | | |
182 | | // already parented? |
183 | 0 | if (child->parent) return NODE_ERR_PARENT; |
184 | | |
185 | | // self/cycle guard |
186 | 0 | if (parent == child) return NODE_ERR_CIRCULAR_REF; |
187 | 0 | if (would_create_cycle(parent, child)) return NODE_ERR_CIRCULAR_REF; |
188 | | |
189 | | // depth guard: depth(parent)+1+max_depth(child_subtree) <= NODE_MAX_DEPTH |
190 | 0 | int pd = node_depth_from_root(parent); |
191 | 0 | int cd = node_subtree_max_depth(child); |
192 | 0 | if (pd + 1 + cd > NODE_MAX_DEPTH) { |
193 | 0 | return NODE_ERR_MAX_DEPTH; |
194 | 0 | } |
195 | | |
196 | 0 | if (!parent->children) { |
197 | 0 | parent->children = node_list_create(); |
198 | 0 | if (!parent->children) return NODE_ERR_NO_MEM; |
199 | 0 | } |
200 | 0 | int res = node_list_insert(parent->children, node_index, child); |
201 | 0 | if (res == 0) { |
202 | 0 | child->parent = parent; |
203 | 0 | parent->count++; |
204 | 0 | } |
205 | 0 | return res; |
206 | 0 | } |
207 | | |
208 | | static void _node_debug(node_t node, unsigned int depth) |
209 | 0 | { |
210 | 0 | unsigned int i = 0; |
211 | 0 | node_t current = NULL; |
212 | 0 | for(i = 0; i < depth; i++) { |
213 | 0 | printf("\t"); |
214 | 0 | } |
215 | 0 | if(!node->parent) { |
216 | 0 | printf("ROOT\n"); |
217 | 0 | } |
218 | |
|
219 | 0 | if(!node->children && node->parent) { |
220 | 0 | printf("LEAF\n"); |
221 | 0 | } else { |
222 | 0 | if(node->parent) { |
223 | 0 | printf("NODE\n"); |
224 | 0 | } |
225 | 0 | for (current = node_first_child(node); current; current = node_next_sibling(current)) { |
226 | 0 | _node_debug(current, depth+1); |
227 | 0 | } |
228 | 0 | } |
229 | |
|
230 | 0 | } |
231 | | |
232 | | void node_debug(node_t node) |
233 | 0 | { |
234 | 0 | _node_debug(node, 0); |
235 | 0 | } |
236 | | |
237 | | unsigned int node_n_children(node_t node) |
238 | 0 | { |
239 | 0 | if (!node) return 0; |
240 | 0 | return node->count; |
241 | 0 | } |
242 | | |
243 | | node_t node_nth_child(node_t node, unsigned int n) |
244 | 0 | { |
245 | 0 | if (!node || !node->children || !node->children->begin) return NULL; |
246 | 0 | unsigned int node_index = 0; |
247 | 0 | int found = 0; |
248 | 0 | node_t ch; |
249 | 0 | for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { |
250 | 0 | if (node_index++ == n) { |
251 | 0 | found = 1; |
252 | 0 | break; |
253 | 0 | } |
254 | 0 | } |
255 | 0 | if (!found) { |
256 | 0 | return NULL; |
257 | 0 | } |
258 | 0 | return ch; |
259 | 0 | } |
260 | | |
261 | | node_t node_first_child(node_t node) |
262 | 544k | { |
263 | 544k | if (!node || !node->children) return NULL; |
264 | 319k | return node->children->begin; |
265 | 544k | } |
266 | | |
267 | | node_t node_prev_sibling(node_t node) |
268 | 0 | { |
269 | 0 | if (!node) return NULL; |
270 | 0 | return node->prev; |
271 | 0 | } |
272 | | |
273 | | node_t node_next_sibling(node_t node) |
274 | 1.12M | { |
275 | 1.12M | if (!node) return NULL; |
276 | 1.12M | return node->next; |
277 | 1.12M | } |
278 | | |
279 | | int node_child_position(node_t parent, node_t child) |
280 | 0 | { |
281 | 0 | if (!parent || !parent->children || !parent->children->begin || !child) return NODE_ERR_INVALID_ARG; |
282 | 0 | int node_index = 0; |
283 | 0 | int found = 0; |
284 | 0 | node_t ch; |
285 | 0 | for (ch = node_first_child(parent); ch; ch = node_next_sibling(ch)) { |
286 | 0 | if (ch == child) { |
287 | 0 | found = 1; |
288 | 0 | break; |
289 | 0 | } |
290 | 0 | node_index++; |
291 | 0 | } |
292 | 0 | if (!found) { |
293 | 0 | return NODE_ERR_NOT_FOUND; |
294 | 0 | } |
295 | 0 | return node_index; |
296 | 0 | } |
297 | | |
298 | | node_t node_copy_deep(node_t node, copy_func_t copy_func) |
299 | 0 | { |
300 | 0 | if (!node) return NULL; |
301 | 0 | void *data = NULL; |
302 | 0 | if (copy_func) { |
303 | 0 | data = copy_func(node->data); |
304 | 0 | } |
305 | 0 | node_t copy = node_create(NULL, data); |
306 | 0 | if (!copy) return NULL; |
307 | 0 | node_t ch; |
308 | 0 | for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { |
309 | 0 | node_t cc = node_copy_deep(ch, copy_func); |
310 | 0 | if (!cc) { |
311 | 0 | node_destroy(copy); |
312 | 0 | return NULL; |
313 | 0 | } |
314 | 0 | if (node_attach(copy, cc) < 0) { |
315 | 0 | node_destroy(cc); |
316 | 0 | node_destroy(copy); |
317 | 0 | return NULL; |
318 | 0 | } |
319 | 0 | } |
320 | 0 | return copy; |
321 | 0 | } |