summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2026-02-06 22:08:39 +0100
committerGravatar Nikias Bassen2026-02-06 22:08:39 +0100
commit1df039994ff2368bc64ea4bc38d9261e6153437c (patch)
treeed16b612677c253925685a9f380abbc19d74d05b
parentb7f09ccdddc75d82ccaed867eb60e6997a7cad40 (diff)
downloadlibplist-1df039994ff2368bc64ea4bc38d9261e6153437c.tar.gz
libplist-1df039994ff2368bc64ea4bc38d9261e6153437c.tar.bz2
libcnary: Define error codes and add cycle, depth, and parent guards
-rw-r--r--libcnary/include/node.h12
-rw-r--r--libcnary/node.c120
-rw-r--r--libcnary/node_list.c99
3 files changed, 164 insertions, 67 deletions
diff --git a/libcnary/include/node.h b/libcnary/include/node.h
index 123241a..5b3ae37 100644
--- a/libcnary/include/node.h
+++ b/libcnary/include/node.h
@@ -29,6 +29,18 @@
29 29
30#define NODE_TYPE 1; 30#define NODE_TYPE 1;
31 31
32#ifndef NODE_MAX_DEPTH
33#define NODE_MAX_DEPTH 512
34#endif
35
36#define NODE_ERR_SUCCESS 0
37#define NODE_ERR_INVALID_ARG -1
38#define NODE_ERR_NO_MEM -2
39#define NODE_ERR_PARENT -3
40#define NODE_ERR_CIRCULAR_REF -4
41#define NODE_ERR_MAX_DEPTH -5
42#define NODE_ERR_NOT_FOUND -6
43
32// This class implements the abstract iterator class 44// This class implements the abstract iterator class
33typedef struct node* node_t; 45typedef struct node* node_t;
34struct node { 46struct node {
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}
diff --git a/libcnary/node_list.c b/libcnary/node_list.c
index f6c2c70..218c81d 100644
--- a/libcnary/node_list.c
+++ b/libcnary/node_list.c
@@ -49,7 +49,7 @@ node_list_t node_list_create()
49 49
50int node_list_add(node_list_t list, node_t node) 50int node_list_add(node_list_t list, node_t node)
51{ 51{
52 if (!list || !node) return -1; 52 if (!list || !node) return NODE_ERR_INVALID_ARG;
53 53
54 // Find the last element in the list 54 // Find the last element in the list
55 node_t last = list->end; 55 node_t last = list->end;
@@ -72,89 +72,80 @@ int node_list_add(node_list_t list, node_t node)
72 72
73 // Increment our node count for this list 73 // Increment our node count for this list
74 list->count++; 74 list->count++;
75 return 0; 75 return NODE_ERR_SUCCESS;
76} 76}
77 77
78int node_list_insert(node_list_t list, unsigned int node_index, node_t node) 78int node_list_insert(node_list_t list, unsigned int node_index, node_t node)
79{ 79{
80 if (!list || !node) return -1; 80 if (!list || !node) return NODE_ERR_INVALID_ARG;
81 if (node_index >= list->count) { 81 if (node_index > list->count) return NODE_ERR_INVALID_ARG;
82 if (node_index == list->count) {
82 return node_list_add(list, node); 83 return node_list_add(list, node);
83 } 84 }
84 85
85 // Get the first element in the list 86 // Get the first element in the list
86 node_t cur = list->begin; 87 node_t cur = list->begin;
87
88 unsigned int pos = 0;
89 node_t prev = NULL; 88 node_t prev = NULL;
90 89
91 if (node_index > 0) { 90 for (unsigned int pos = 0; pos < node_index; pos++) {
92 while (pos < node_index) { 91 if (!cur) return NODE_ERR_INVALID_ARG;
93 prev = cur; 92 prev = cur;
94 cur = cur->next; 93 cur = cur->next;
95 pos++;
96 }
97 } 94 }
98 95
96 // insert node before cur
97 node->prev = prev;
98 node->next = cur;
99
99 if (prev) { 100 if (prev) {
100 // Set previous node
101 node->prev = prev;
102 // Set next node of our new node to next node of the previous node
103 node->next = prev->next;
104 // Set next node of previous node to our new node
105 prev->next = node; 101 prev->next = node;
106 } else { 102 } else {
107 node->prev = NULL;
108 // get old first element in list
109 node->next = list->begin;
110 // set new node as first element in list
111 list->begin = node; 103 list->begin = node;
112 } 104 }
113 105
114 if (node->next == NULL) { 106 if (cur) {
115 // Set the lists prev to the new last element 107 cur->prev = node;
116 list->end = node;
117 } else { 108 } else {
118 // set prev of the new next element to our node 109 // should not happen with bounds above, but keeps things consistent
119 node->next->prev = node; 110 list->end = node;
120 } 111 }
121 112
122 // Increment our node count for this list 113 // Increment our node count for this list
123 list->count++; 114 list->count++;
124 return 0; 115 return NODE_ERR_SUCCESS;
125} 116}
126 117
118// Returns removed index (>=0) on success, or NODE_ERR_* (<0) on failure.
127int node_list_remove(node_list_t list, node_t node) 119int node_list_remove(node_list_t list, node_t node)
128{ 120{
129 if (!list || !node) return -1; 121 if (!list || !node) return NODE_ERR_INVALID_ARG;
130 if (list->count == 0) return -1; 122 if (list->count == 0) return NODE_ERR_NOT_FOUND;
131 123
132 int node_index = 0; 124 int node_index = 0;
133 node_t n; 125 for (node_t n = list->begin; n; n = n->next, node_index++) {
134 for (n = list->begin; n; n = n->next) { 126 if (node != n) continue;
135 if (node == n) { 127
136 node_t newnode = node->next; 128 node_t newnode = node->next;
137 if (node->prev) { 129 if (node->prev) {
138 node->prev->next = newnode; 130 node->prev->next = newnode;
139 if (newnode) { 131 } else {
140 newnode->prev = node->prev; 132 // we just removed the first element
141 } else { 133 list->begin = newnode;
142 // last element in the list
143 list->end = node->prev;
144 }
145 } else {
146 // we just removed the first element
147 if (newnode) {
148 newnode->prev = NULL;
149 } else {
150 list->end = NULL;
151 }
152 list->begin = newnode;
153 }
154 list->count--;
155 return node_index;
156 } 134 }
157 node_index++; 135
136 if (newnode) {
137 newnode->prev = node->prev;
138 } else {
139 // we removed the last element, set new end
140 list->end = node->prev;
141 }
142
143 // fully detach node from list
144 node->prev = NULL;
145 node->next = NULL;
146
147 list->count--;
148 return node_index;
158 } 149 }
159 return -1; 150 return NODE_ERR_NOT_FOUND;
160} 151}