diff options
Diffstat (limited to 'libcnary/node.c')
| -rw-r--r-- | libcnary/node.c | 120 |
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 | ||
| 77 | static 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 | |||
| 88 | static 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 | |||
| 118 | out: | ||
| 119 | free(st); | ||
| 120 | return maxd; | ||
| 121 | } | ||
| 122 | |||
| 123 | static 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 | |||
| 78 | int node_attach(node_t parent, node_t child) | 132 | int 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 | ||
| 92 | int node_detach(node_t parent, node_t child) | 162 | int 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 | ||
| 102 | int node_insert(node_t parent, unsigned int node_index, node_t child) | 172 | int 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 | ||
| 187 | int node_child_position(node_t parent, node_t child) | 273 | int 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 | } |
