1 /* simple example of a search tree with string keys and indexing */
17 struct xyla_avl_node avl; /* intrusion */
18 char *key, *value; /* string key and value */
19 size_t n; /* subtree node count */
22 static NORETURN PRINTF_LIKE(1, 2) void bail(const char *msg, ...)
24 /* Print a `printf'-style message MSG to stderr and exit nonzero. */
27 va_start(ap, msg); vfprintf(stderr, msg, ap); va_end(ap);
28 fputc('\n', stderr); exit(2);
31 static void idx_upd(struct xyla_bt_nodecls *cls, struct xyla_bt_node *node)
33 /* Update function to maintain the node count. */
35 struct node *n = (struct node *)node,
36 *l = (struct node *)n->avl.bt.left,
37 *r = (struct node *)n->avl.bt.right;
39 /* The node count is one more than the sum of the child node counts. */
40 n->n = 1 + (l ? l->n : 0) + (r ? r->n : 0);
43 static int str_nav(struct xyla_bt_nodecls *cls,
44 const struct xyla_bt_node *node, void *arg)
46 /* Navigation function to search by string key. */
48 const struct node *n = (const struct node *)node;
51 return (strcmp(k, n->key));
54 static int idx_nav(struct xyla_bt_nodecls *cls,
55 const struct xyla_bt_node *node, void *arg)
57 /* Navigation function to search by integer index. */
59 const struct node *n = (const struct node *)node,
60 *l = (const struct node *)n->avl.bt.left;
61 size_t *i_inout = arg, i = *i_inout, ln;
63 /* Invariant: *I_INOUT is the index of the node we want /within/ the
68 /* The left subtree is not empty. If i is less than the left-subtree's
69 * node count, then the node we want is in there. Otherwise, adjust the
70 * index so that it counts from this node.
74 if (i < ln) return (-1);
78 /* If the index is now zero, then it's this node that we want. Otherwise,
79 * subtract off one, for this node, and continue the search into the right
83 else { *i_inout = i - 1; return (+1); }
86 static const void *str_key(struct xyla_bt_nodecls *cls,
87 const struct xyla_bt_node *node)
89 /* String key projection. */
91 const struct node *n = (const struct node *)node;
96 static int node_chk(unsigned op, const struct xyla_bt_check *chk)
98 /* Node checking function. */
100 const struct node *node, *left, *right;
102 int rc = XYLA_RC_OK, subrc;
104 if (op == XYLA_CHKOP_AFTER) {
105 /* Children are checked, so check the parent. */
107 /* Check the node count. */
108 node = (const struct node *)chk->node;
109 left = (const struct node *)chk->left;
110 right = (const struct node *)chk->right;
111 n = 1 + (left ? left->n : 0) + (right ? right->n : 0);
114 xyla_bt_bughdr("EXAMPLE", chk->root, chk->fp);
115 fputs("node ", chk->fp);
116 xyla_bt_printnode(chk->cls, &node->avl.bt, chk->fp);
117 fprintf(chk->fp, " count %lu /= %lu expected\n",
118 (unsigned long)node->n, (unsigned long)n);
124 /* Check ordering. */
125 subrc = xyla_bt_chkorder(op, chk);
127 else if (subrc == XYLA_RC_BAD) rc = subrc;
132 static void node_id(struct xyla_bt_nodecls *cls,
133 const struct xyla_bt_node *node, FILE *fp)
135 const struct node *n = (const struct node *)n;
137 fprintf(fp, "#<node `%s' = `%s'>", n->key, n->value);
140 /* Node class operations. */
141 static const struct xyla_bt_chkops node_ops = {
142 { sizeof(node_ops), idx_upd },
143 { str_nav, str_key },
144 { node_chk, sizeof(struct xyla_bt_ordinfo), node_id }
147 static int idx_ascend(struct xyla_bt_node *node,
148 struct xyla_bt_node *parent,
149 struct xyla_bt_node *sibling,
150 unsigned pos, void *arg)
152 /* Ascension function to determine a node's index. */
154 const struct node *s = (const struct node *)sibling;
155 size_t *i_inout = arg;
157 /* Invariant: *I_INOUT is the index of the original node within the
158 * subtree rooted at NODE.
161 /* Update the index ready for its parent. If we're the left child, then
162 * there's nothing to do. Otherwise, there's the parent, and its left
165 if (pos == XYLA_BTPOS_RIGHT) *i_inout += 1 + (s ? s->n : 0);
169 static size_t read_index(const char *p)
175 if (!isdigit((unsigned char)*p)) bail("integer expected");
176 n = strtoul(p, &q, 0);
177 if (errno || *q) bail("integer expected");
178 if (n > (size_t)-1) bail("out of range");
184 struct xyla_bt_node *root = 0; /* tree root */
185 struct xyla_bt_nodecls cls; /* node class */
186 struct xyla_avl_iter it; /* iterator */
187 struct xyla_avl_path path; /* path for insert, remove, ascend */
188 struct node *node; /* a node pointer */
189 const struct node *t; /* a temporary node pointer */
190 char buf[256]; size_t len; /* input buffer and line length */
191 char *p, *q; /* cursors for parsing input line */
192 size_t kn, vn; /* key and value lengths */
193 size_t i, j; /* indices */
194 size_t n; /* total node count */
195 int rc; /* return code */
197 /* Establish the node class operations. */
198 cls.ops = &node_ops.node;
200 while (fgets(buf, sizeof(buf), stdin)) {
201 /* Read command lines from stdin and process them. */
203 /* Check for overrun and chomp the trailing newline. */
204 len = strlen(buf); assert(len);
205 if (buf[len - 1] != '\n') bail("line too long");
206 p = buf; buf[--len] = 0;
208 /* Dispatch based on the first character. */
209 if (*p == ';' || !*p) {
210 /* ;COMMENT | EMPTY -- copy to output. */
213 } else if (*p == '?') {
214 /* ?KEY | ?#INDEX -- print the node with KEY, or at INDEX. */
218 /* Lookup by index. */
220 /* Read and check the index. */
221 p++; i = read_index(p);
222 n = root ? ((const struct node *)root)->n : 0;
223 if (i >= n) { fputs("(out of range)\n", stdout); goto next; }
225 /* Find the right node. This clobbers the provided index, so work on
226 * a copy. We don't need a path, so `lookup' is good enough.
228 j = i; node = xyla_avl_lookup(&cls, &root, idx_nav, &j);
233 /* Find the node. We'll need the path to find the index, so `probe'
236 node = xyla_avl_probe(&cls, &root, 0, p, &path);
237 if (!node) { fputs("(not found)\n", stdout); goto next; }
239 /* Discover the node index. The ascension invariant is that `i'
240 * holds the index of the target node within the subtree rooted at
243 t = (struct node *)node->avl.bt.left; i = t ? t->n : 0;
244 rc = xyla_avl_ascend(idx_ascend, &path, &i); assert(!rc);
247 /* Print the node. */
248 printf("[%lu]: %s -> %s\n", (unsigned long)i, node->key, node->value);
249 } else if (*p == '-') {
250 /* -KEY | -#INDEX -- remove the node with KEY or at INDEX. */
254 /* Lookup by index. */
256 /* Read and check the index. */
257 p++; i = read_index(p);
258 n = root ? ((const struct node *)root)->n : 0;
259 if (i >= n) { fputs("(out of range)\n", stdout); goto next; }
261 /* Find the right node. We'll need the path to remove the node, so
264 node = xyla_avl_probe(&cls, &root, idx_nav, &i, &path); assert(node);
268 /* Find the node. Again, `probe', because we need the path to remove
271 node = xyla_avl_probe(&cls, &root, 0, p, &path);
272 if (!node) { fputs("(not found)\n", stdout); goto next; }
275 /* Remove the node from the tree and update the count. */
276 xyla_avl_remove(&cls, &path);
279 free(node->key); free(node->value); free(node);
280 } else if (*p == '*' && !p[1]) {
281 /* * -- print all of the nodes in order. */
283 /* Parallel iteration over the nodes and index. */
284 for (xyla_avl_inititer(root, &it), i = 0;
285 !!(node = xyla_avl_next(&it));
287 printf("[%lu]: %s -> %s\n",
288 (unsigned long)i, node->key, node->value);
290 /* [+]KEY=VALUE | #INDEX=VALUE -- set the VALUE of the node with KEY or
291 * at VALUE; if there is no node with KEY, insert a new one.
295 /* Lookup by index. */
297 /* Read the index and value, and check the index. */
298 p++; q = strchr(p, '='); if (!q) bail("missing `='");
299 *q++ = 0; i = read_index(p); vn = strlen(q);
300 n = root ? ((const struct node *)root)->n : 0;
301 if (i >= n) { fputs("out of range)\n", stdout); goto next; }
303 /* Find the right node. It must exist, so we won't need to insert,
304 * so we can use `lookup' here.
306 node = xyla_avl_lookup(&cls, &root, idx_nav, &i); assert(node);
308 /* We're going to replace the node's value, so free the old one. */
313 /* Read the key and value. */
315 q = strchr(p, '='); if (!q) bail("unknown command");
316 *q++ = 0; kn = strlen(p); vn = strlen(q);
318 /* Find the right node. If there isn't one, then we'll insert a new
319 * one, so we'll need a path, and must use `probe'.
321 node = xyla_avl_probe(&cls, &root, 0, p, &path);
324 /* The node exists. Free its old value so we can replace it. */
328 /* There's no existing node, so we must create and insert a new
332 /* Make the new node and fill in the key. We'll set the value in
333 * the common epilogue below.
335 node = malloc(sizeof(struct node));
336 if (!node) bail("out of memory");
337 node->key = malloc(kn + 1); if (!node->key) bail("out of memory");
338 memcpy(node->key, p, kn + 1);
340 /* Insert the new node and bump the node count. */
341 xyla_avl_insert(&cls, &path, &node->avl);
345 /* Set the node value. */
346 node->value = malloc(vn + 1); if (!node->key) bail("out of memory");
347 memcpy(node->value, q, vn + 1);
353 /* Check that the final tree is well-formed. */
354 if (xyla_avl_check(&cls, &root, stderr, 0, -1, 0))
355 bail("tree corrupted");
357 /* Free all of the nodes. */
358 while (!!(node = xyla_bt_severfirst(&root)))
359 { free(node->key); free(node->value); free(node); }
361 /* Check that all of the I/O worked properly. */
363 bail("input error: %s", strerror(errno));
364 if (fflush(stdout) || ferror(stdout))
365 bail("output error: %s", strerror(errno));