/* Copyright 2022 Jeffrey Kegler */ /* This is a modification of avl.c, from Ben Pfaff's libavl, which was under the LGPL 3. Here is the copyright notice from that file: libavl - library for manipulation of binary trees. Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004 Free Software Foundation, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include #include #include "marpa.h" #include "marpa_ami.h" #include "marpa_obs.h" #include "marpa_avl.h" /* An AVL tree node. */ typedef struct marpa_avl_node* NODE; /* Creates and returns a new table with comparison function |compare| using parameter |param|. */ MARPA_AVL_TREE _marpa_avl_create (marpa_avl_comparison_func *compare, void *param) { MARPA_AVL_TREE tree; struct marpa_obstack *avl_obstack = marpa_obs_init; assert (compare != NULL); tree = marpa_obs_new( avl_obstack, struct marpa_avl_table, 1); tree->avl_obstack = avl_obstack; tree->avl_root = NULL; tree->avl_compare = compare; tree->avl_param = param; tree->avl_count = 0; tree->avl_generation = 0; return tree; } /* Search |tree| for an item matching |item|, and return it if found. Otherwise return |NULL|. */ void * _marpa_avl_find (const MARPA_AVL_TREE tree, const void *item) { NODE p; assert (tree != NULL && item != NULL); for (p = tree->avl_root; p != NULL; ) { int cmp = tree->avl_compare (item, p->avl_data, tree->avl_param); if (cmp < 0) p = p->avl_link[0]; else if (cmp > 0) p = p->avl_link[1]; else /* |cmp == 0| */ return p->avl_data; } return NULL; } /* Search |tree| for an item matching or after |item|, and return it if found. Otherwise return |NULL|. */ void * _marpa_avl_at_or_after (const MARPA_AVL_TREE tree, const void *item) { NODE p; NODE at_or_after = NULL; assert (tree != NULL && item != NULL); for (p = tree->avl_root; p != NULL;) { int cmp = tree->avl_compare (item, p->avl_data, tree->avl_param); if (cmp < 0) { at_or_after = p; p = p->avl_link[0]; } else if (cmp > 0) { p = p->avl_link[1]; } else /* |cmp == 0| */ return p->avl_data; } return at_or_after ? at_or_after->avl_data : NULL; } /* Inserts |item| into |tree| and returns a pointer to |item|'s address. If a duplicate item is found in the tree, returns a pointer to the duplicate without inserting |item|. */ void ** _marpa_avl_probe (MARPA_AVL_TREE tree, void *item) { NODE y, z; /* Top node to update balance factor, and parent */ NODE p, q; /* Iterator, and parent. */ NODE n; /* Newly inserted node. */ NODE w; /* New root of rebalanced subtree. */ int dir; /* Direction to descend. */ unsigned char da[MARPA_AVL_MAX_HEIGHT]; /* Cached comparison results. */ int k = 0; /* Number of cached results. */ assert (tree != NULL && item != NULL); z = (NODE) &tree->avl_root; y = tree->avl_root; dir = 0; for (q = z, p = y; p != NULL; q = p, p = p->avl_link[dir]) { int cmp = tree->avl_compare (item, p->avl_data, tree->avl_param); if (cmp == 0) return &p->avl_data; if (p->avl_balance != 0) z = q, y = p, k = 0; dir = cmp > 0; da[k++] = (unsigned char)dir; } n = q->avl_link[dir] = marpa_obs_new (tree->avl_obstack, struct marpa_avl_node, 1); tree->avl_count++; n->avl_data = item; n->avl_link[0] = n->avl_link[1] = NULL; n->avl_balance = 0; if (y == NULL) return &n->avl_data; for (p = y, k = 0; p != n; p = p->avl_link[da[k]], k++) if (da[k] == 0) p->avl_balance--; else p->avl_balance++; if (y->avl_balance == -2) { NODE x = y->avl_link[0]; if (x->avl_balance == -1) { w = x; y->avl_link[0] = x->avl_link[1]; x->avl_link[1] = y; x->avl_balance = y->avl_balance = 0; } else { assert (x->avl_balance == +1); w = x->avl_link[1]; x->avl_link[1] = w->avl_link[0]; w->avl_link[0] = x; y->avl_link[0] = w->avl_link[1]; w->avl_link[1] = y; if (w->avl_balance == -1) x->avl_balance = 0, y->avl_balance = +1; else if (w->avl_balance == 0) x->avl_balance = y->avl_balance = 0; else /* |w->avl_balance == +1| */ x->avl_balance = -1, y->avl_balance = 0; w->avl_balance = 0; } } else if (y->avl_balance == +2) { NODE x = y->avl_link[1]; if (x->avl_balance == +1) { w = x; y->avl_link[1] = x->avl_link[0]; x->avl_link[0] = y; x->avl_balance = y->avl_balance = 0; } else { assert (x->avl_balance == -1); w = x->avl_link[0]; x->avl_link[0] = w->avl_link[1]; w->avl_link[1] = x; y->avl_link[1] = w->avl_link[0]; w->avl_link[0] = y; if (w->avl_balance == +1) x->avl_balance = 0, y->avl_balance = -1; else if (w->avl_balance == 0) x->avl_balance = y->avl_balance = 0; else /* |w->avl_balance == -1| */ x->avl_balance = +1, y->avl_balance = 0; w->avl_balance = 0; } } else return &n->avl_data; z->avl_link[y != z->avl_link[0]] = w; tree->avl_generation++; return &n->avl_data; } /* Inserts |item| into |table|. Returns |NULL| if |item| was successfully inserted. Otherwise, returns the duplicate item. */ void * _marpa_avl_insert (MARPA_AVL_TREE table, void *item) { void **p = _marpa_avl_probe (table, item); return p == NULL || *p == item ? NULL : *p; } /* Inserts |item| into |table|, replacing any duplicate item. Returns |NULL| if |item| was inserted without replacing a duplicate. Otherwise, returns the item that was replaced. */ void * _marpa_avl_replace (MARPA_AVL_TREE table, void *item) { void **p = _marpa_avl_probe (table, item); if (p == NULL || *p == item) return NULL; else { void *r = *p; *p = item; return r; } } /* Refreshes the stack of parent pointers in |trav| and updates its generation number. */ static void trav_refresh (struct marpa_avl_traverser *trav) { assert (trav != NULL); trav->avl_generation = trav->avl_table->avl_generation; if (trav->avl_node != NULL) { marpa_avl_comparison_func *cmp = trav->avl_table->avl_compare; void *param = trav->avl_table->avl_param; NODE node = trav->avl_node; NODE i; trav->avl_height = 0; for (i = trav->avl_table->avl_root; i != node; ) { assert (trav->avl_height < MARPA_AVL_MAX_HEIGHT); assert (i != NULL); trav->avl_stack[trav->avl_height++] = i; i = i->avl_link[cmp (node->avl_data, i->avl_data, param) > 0]; } } } /* Assuming that the tree is already set, set the traverser to its initial values */ static inline void trav_reset(MARPA_AVL_TRAV trav) { MARPA_AVL_TREE tree = MARPA_TREE_OF_AVL_TRAV(trav); trav->avl_node = NULL; trav->avl_height = 0; trav->avl_generation = tree->avl_generation; } /* Initializes |trav| for use with |tree| and selects the null node. */ MARPA_AVL_TRAV _marpa_avl_t_init (MARPA_AVL_TREE tree) { const MARPA_AVL_TRAV trav = marpa_obs_new (MARPA_AVL_OBSTACK (tree), struct marpa_avl_traverser, 1); trav->avl_table = tree; trav_reset(trav); return trav; } /* Initializes |trav| for use with |tree| and selects the null node. */ MARPA_AVL_TRAV _marpa_avl_t_reset (MARPA_AVL_TRAV trav) { trav_reset(trav); return trav; } /* Selects and returns a pointer to the least-valued item. Returns |NULL| if |tree| contains no nodes. */ void * _marpa_avl_t_first (MARPA_AVL_TRAV trav) { NODE x; MARPA_AVL_TREE tree = MARPA_TREE_OF_AVL_TRAV(trav); assert (trav != NULL); x = tree->avl_root; if (x != NULL) while (x->avl_link[0] != NULL) { assert (trav->avl_height < MARPA_AVL_MAX_HEIGHT); trav->avl_stack[trav->avl_height++] = x; x = x->avl_link[0]; } trav->avl_node = x; return x != NULL ? x->avl_data : NULL; } /* Selects and returns a pointer to the greatest-valued item. Returns |NULL| if |tree| contains no nodes. */ void * _marpa_avl_t_last (MARPA_AVL_TRAV trav) { NODE x; MARPA_AVL_TREE tree = MARPA_TREE_OF_AVL_TRAV(trav); assert (trav != NULL); x = tree->avl_root; if (x != NULL) while (x->avl_link[1] != NULL) { assert (trav->avl_height < MARPA_AVL_MAX_HEIGHT); trav->avl_stack[trav->avl_height++] = x; x = x->avl_link[1]; } trav->avl_node = x; return x != NULL ? x->avl_data : NULL; } /* Searches for |item| in |tree| of |trav|. If found, sets |trav| to the item found and returns the item as well. If there is no matching item, initializes |trav| to the null item and returns |NULL|. */ void * _marpa_avl_t_find (MARPA_AVL_TRAV trav, void *item) { NODE p, q; MARPA_AVL_TREE tree = MARPA_TREE_OF_AVL_TRAV(trav); assert (trav != NULL && item != NULL); for (p = tree->avl_root; p != NULL; p = q) { int cmp = tree->avl_compare (item, p->avl_data, tree->avl_param); if (cmp < 0) q = p->avl_link[0]; else if (cmp > 0) q = p->avl_link[1]; else /* |cmp == 0| */ { trav->avl_node = p; return p->avl_data; } assert (trav->avl_height < MARPA_AVL_MAX_HEIGHT); trav->avl_stack[trav->avl_height++] = p; } trav->avl_height = 0; trav->avl_node = NULL; return NULL; } /* Searches for |item| in |tree| of |trav|. Sets |trav| to the item, if found, otherwise to the first one after |item|, and returns the item as well. If there is no item after |item|, initializes |trav| to the null item and returns |NULL|. The implementation just calls |_marpa_at_or_after| and |_marpa_avl_t_find|. This means the tree is searched twice, but a custom routine would have to build a stack and then possibly back it out, so a double traversal seems inescapable. A custom routine would be faster, but not by much. */ void * _marpa_avl_t_at_or_after (MARPA_AVL_TRAV trav, void* sought_item) { const MARPA_AVL_TREE tree = MARPA_TREE_OF_AVL_TRAV(trav); void* found_item = _marpa_avl_at_or_after (tree, sought_item); if (!found_item) return NULL; return _marpa_avl_t_find(trav, found_item); } /* Attempts to insert |item| into tree of |trav|. If |item| is inserted successfully, it is returned and |trav| is initialized to its location. If a duplicate is found, it is returned and |trav| is initialized to its location. No replacement of the item occurs. */ void * _marpa_avl_t_insert ( MARPA_AVL_TRAV trav, void *item) { void **p; MARPA_AVL_TREE tree = MARPA_TREE_OF_AVL_TRAV(trav); assert (trav != NULL && tree != NULL && item != NULL); p = _marpa_avl_probe (tree, item); if (p != NULL) { trav->avl_table = tree; trav->avl_node = ((NODE ) ((char *) p - offsetof (struct marpa_avl_node, avl_data))); trav->avl_generation = tree->avl_generation - 1; return *p; } else { trav_reset (trav); return NULL; } } /* Initializes |trav| to have the same current node as |src|. */ void * _marpa_avl_t_copy (struct marpa_avl_traverser *trav, const struct marpa_avl_traverser *src) { assert (trav != NULL && src != NULL); if (trav != src) { trav->avl_table = src->avl_table; trav->avl_node = src->avl_node; trav->avl_generation = src->avl_generation; if (trav->avl_generation == trav->avl_table->avl_generation) { trav->avl_height = src->avl_height; memcpy (trav->avl_stack, (const void *) src->avl_stack, sizeof *trav->avl_stack * trav->avl_height); } } return trav->avl_node != NULL ? trav->avl_node->avl_data : NULL; } /* Returns the next data item in inorder within the tree being traversed with |trav|, or if there are no more data items returns |NULL|. */ void * _marpa_avl_t_next (MARPA_AVL_TRAV trav) { NODE x; assert (trav != NULL); if (trav->avl_generation != trav->avl_table->avl_generation) trav_refresh (trav); x = trav->avl_node; if (x == NULL) { return _marpa_avl_t_first (trav); } else if (x->avl_link[1] != NULL) { assert (trav->avl_height < MARPA_AVL_MAX_HEIGHT); trav->avl_stack[trav->avl_height++] = x; x = x->avl_link[1]; while (x->avl_link[0] != NULL) { assert (trav->avl_height < MARPA_AVL_MAX_HEIGHT); trav->avl_stack[trav->avl_height++] = x; x = x->avl_link[0]; } } else { NODE y; do { if (trav->avl_height == 0) { trav->avl_node = NULL; return NULL; } y = x; x = trav->avl_stack[--trav->avl_height]; } while (y == x->avl_link[1]); } trav->avl_node = x; return x->avl_data; } /* Returns the previous data item in inorder within the tree being traversed with |trav|, or if there are no more data items returns |NULL|. */ void * _marpa_avl_t_prev (MARPA_AVL_TRAV trav) { NODE x; assert (trav != NULL); if (trav->avl_generation != trav->avl_table->avl_generation) trav_refresh (trav); x = trav->avl_node; if (x == NULL) { return _marpa_avl_t_last (trav); } else if (x->avl_link[0] != NULL) { assert (trav->avl_height < MARPA_AVL_MAX_HEIGHT); trav->avl_stack[trav->avl_height++] = x; x = x->avl_link[0]; while (x->avl_link[1] != NULL) { assert (trav->avl_height < MARPA_AVL_MAX_HEIGHT); trav->avl_stack[trav->avl_height++] = x; x = x->avl_link[1]; } } else { NODE y; do { if (trav->avl_height == 0) { trav->avl_node = NULL; return NULL; } y = x; x = trav->avl_stack[--trav->avl_height]; } while (y == x->avl_link[0]); } trav->avl_node = x; return x->avl_data; } /* Returns |trav|'s current item. */ void * _marpa_avl_t_cur (MARPA_AVL_TRAV trav) { assert (trav != NULL); return trav->avl_node != NULL ? trav->avl_node->avl_data : NULL; } /* Replaces the current item in |trav| by |new| and returns the item replaced. |trav| must not have the null item selected. The new item must not upset the ordering of the tree. */ void * _marpa_avl_t_replace (struct marpa_avl_traverser *trav, void *new) { void *old; assert (trav != NULL && trav->avl_node != NULL && new != NULL); old = trav->avl_node->avl_data; trav->avl_node->avl_data = new; return old; } /* Frees storage allocated for |tree|. Everything is on the obstack. */ void _marpa_avl_destroy (MARPA_AVL_TREE tree) { if (tree == NULL) return; marpa_obs_free (tree->avl_obstack); } #undef NDEBUG #include