Common code several concrete list implementations.
[gnulib.git] / lib / gl_anytreehash_list1.h
1 /* Sequential list data type implemented by a hash table with a binary tree.
2    Copyright (C) 2006 Free Software Foundation, Inc.
3    Written by Bruno Haible <bruno@clisp.org>, 2006.
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2, or (at your option)
8    any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software Foundation,
17    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
18
19 /* Common code of gl_avltreehash_list.c and gl_rbtreehash_list.c.  */
20
21 /* Hash table entry representing the same value at more than one position.  */
22 struct gl_multiple_nodes
23 {
24   struct gl_hash_entry h;           /* hash table entry fields; must be first */
25   void *magic;                      /* used to distinguish from single node */
26   gl_oset_t nodes;                  /* set of nodes, sorted by position */
27 };
28 /* A value that cannot occur at the corresponding field (->left) in
29    gl_list_node_impl.  */
30 #define MULTIPLE_NODES_MAGIC  (void *) -1
31
32 /* Resize the hash table if needed, after list->count was incremented.  */
33 static inline void
34 hash_resize_after_add (gl_list_t list)
35 {
36   size_t count = (list->root != 0 ? list->root->branch_size : 0);
37   size_t estimate = xsum (count, count / 2); /* 1.5 * count */
38   if (estimate > list->table_size)
39     hash_resize (list, estimate);
40 }
41
42 /* Return the position of the given node in the tree.  */
43 static size_t
44 node_position (gl_list_node_t node)
45 {
46   size_t position = 0;
47
48   if (node->left != NULL)
49     position += node->left->branch_size;
50   for (;;)
51     {
52       gl_list_node_t parent = node->parent;
53
54       if (parent == NULL)
55         return position;
56       /* position is now relative to the subtree of node.  */
57       if (parent->right == node)
58         {
59           position += 1;
60           if (parent->left != NULL)
61             position += parent->left->branch_size;
62         }
63       /* position is now relative to the subtree of parent.  */
64       node = parent;
65     }
66 }
67
68 /* Compares two nodes by their position in the tree.  */
69 static int
70 compare_by_position (const void *x1, const void *x2)
71 {
72   gl_list_node_t node1 = (gl_list_node_t) x1;
73   gl_list_node_t node2 = (gl_list_node_t) x2;
74   size_t position1 = node_position (node1);
75   size_t position2 = node_position (node2);
76
77   return (position1 > position2 ? 1 :
78           position1 < position2 ? -1 : 0);
79 }
80
81 /* Return the first element of a non-empty ordered set of nodes.  */
82 static inline gl_list_node_t
83 gl_oset_first (gl_oset_t set)
84 {
85   gl_oset_iterator_t iter = gl_oset_iterator (set);
86   const void *first;
87
88   if (!gl_oset_iterator_next (&iter, &first))
89     abort ();
90   gl_oset_iterator_free (&iter);
91   return (gl_list_node_t) first;
92 }
93
94 /* Add a node to the hash table structure.
95    If duplicates are allowed, this function performs in average time
96    O((log n)^2): gl_oset_add may need to add an element to an ordered set of
97    size O(n), needing O(log n) comparison function calls.  The comparison
98    function is compare_by_position, which is O(log n) worst-case.
99    If duplicates are forbidden, this function is O(1).  */
100 static void
101 add_to_bucket (gl_list_t list, gl_list_node_t new_node)
102 {
103   size_t index = new_node->h.hashcode % list->table_size;
104
105   /* If no duplicates are allowed, multiple nodes are not needed.  */
106   if (list->base.allow_duplicates)
107     {
108       size_t hashcode = new_node->h.hashcode;
109       const void *value = new_node->value;
110       gl_listelement_equals_fn equals = list->base.equals_fn;
111       gl_hash_entry_t *entryp;
112
113       for (entryp = &list->table[index]; *entryp != NULL; entryp = &(*entryp)->hash_next)
114         {
115           gl_hash_entry_t entry = *entryp;
116
117           if (entry->hashcode == hashcode)
118             {
119               if (((struct gl_multiple_nodes *) entry)->magic == MULTIPLE_NODES_MAGIC)
120                 {
121                   /* An entry representing multiple nodes.  */
122                   gl_oset_t nodes = ((struct gl_multiple_nodes *) entry)->nodes;
123                   /* Only the first node is interesting.  */
124                   gl_list_node_t node = gl_oset_first (nodes);
125                   if (equals != NULL ? equals (value, node->value) : value == node->value)
126                     {
127                       /* Found already multiple nodes with the same value.
128                          Add the new_node to it.  */
129                       gl_oset_add (nodes, new_node);
130                       return;
131                     }
132                 }
133               else
134                 {
135                   /* An entry representing a single node.  */
136                   gl_list_node_t node = (struct gl_list_node_impl *) entry;
137                   if (equals != NULL ? equals (value, node->value) : value == node->value)
138                     {
139                       /* Found already a node with the same value.  Turn it
140                          into an ordered set, and add new_node to it.  */
141                       gl_oset_t nodes;
142                       struct gl_multiple_nodes *multi_entry;
143
144                       nodes =
145                         gl_oset_create_empty (OSET_TREE_FLAVOR,
146                                               compare_by_position);
147
148                       gl_oset_add (nodes, node);
149                       gl_oset_add (nodes, new_node);
150
151                       multi_entry =
152                         (struct gl_multiple_nodes *) xmalloc (sizeof (struct gl_multiple_nodes));
153                       multi_entry->h.hash_next = entry->hash_next;
154                       multi_entry->h.hashcode = entry->hashcode;
155                       multi_entry->magic = MULTIPLE_NODES_MAGIC;
156                       multi_entry->nodes = nodes;
157                       *entryp = &multi_entry->h;
158                       return;
159                     }
160                 }
161             }
162         }
163     }
164   /* If no duplicates are allowed, multiple nodes are not needed.  */
165   new_node->h.hash_next = list->table[index];
166   list->table[index] = &new_node->h;
167 }
168
169 /* Remove a node from the hash table structure.
170    If duplicates are allowed, this function performs in average time
171    O((log n)^2): gl_oset_remove may need to remove an element from an ordered
172    set of size O(n), needing O(log n) comparison function calls.  The
173    comparison function is compare_by_position, which is O(log n) worst-case.
174    If duplicates are forbidden, this function is O(1) on average.  */
175 static void
176 remove_from_bucket (gl_list_t list, gl_list_node_t old_node)
177 {
178   size_t index = old_node->h.hashcode % list->table_size;
179
180   if (list->base.allow_duplicates)
181     {
182       size_t hashcode = old_node->h.hashcode;
183       const void *value = old_node->value;
184       gl_listelement_equals_fn equals = list->base.equals_fn;
185       gl_hash_entry_t *entryp;
186
187       for (entryp = &list->table[index]; ; entryp = &(*entryp)->hash_next)
188         {
189           gl_hash_entry_t entry = *entryp;
190
191           if (entry == &old_node->h)
192             {
193               /* Found old_node as a single node in the bucket.  Remove it.  */
194               *entryp = old_node->h.hash_next;
195               break;
196             }
197           if (entry == NULL)
198             /* node is not in the right bucket.  Did the hash codes
199                change inadvertently?  */
200             abort ();
201           if (((struct gl_multiple_nodes *) entry)->magic == MULTIPLE_NODES_MAGIC
202               && entry->hashcode == hashcode)
203             {
204               /* An entry representing multiple nodes.  */
205               gl_oset_t nodes = ((struct gl_multiple_nodes *) entry)->nodes;
206               /* Only the first node is interesting.  */
207               gl_list_node_t node = gl_oset_first (nodes);
208               if (equals != NULL ? equals (value, node->value) : value == node->value)
209                 {
210                   /* Found multiple nodes with the same value.
211                      old_node must be one of them.  Remove it.  */
212                   if (!gl_oset_remove (nodes, old_node))
213                     abort ();
214                   if (gl_oset_size (nodes) == 1)
215                     {
216                       /* Replace a one-element set with a single node.  */
217                       node = gl_oset_first (nodes);
218                       node->h.hash_next = entry->hash_next;
219                       *entryp = &node->h;
220                       gl_oset_free (nodes);
221                       free (entry);
222                     }
223                   break;
224                 }
225             }
226         }
227     }
228   else
229     {
230       /* If no duplicates are allowed, multiple nodes are not needed.  */
231       gl_hash_entry_t *entryp;
232
233       for (entryp = &list->table[index]; ; entryp = &(*entryp)->hash_next)
234         {
235           if (*entryp == &old_node->h)
236             {
237               *entryp = old_node->h.hash_next;
238               break;
239             }
240           if (*entryp == NULL)
241             /* node is not in the right bucket.  Did the hash codes
242                change inadvertently?  */
243             abort ();
244         }
245     }
246 }
247
248 /* Build up the hash table during initialization: Store all the nodes of
249    list->root in the hash table.  */
250 static inline void
251 add_nodes_to_buckets (gl_list_t list)
252 {
253   /* Iterate across all nodes.  */
254   gl_list_node_t node = list->root;
255   iterstack_t stack;
256   iterstack_item_t *stack_ptr = &stack[0];
257
258   for (;;)
259     {
260       /* Descend on left branch.  */
261       for (;;)
262         {
263           if (node == NULL)
264             break;
265           stack_ptr->node = node;
266           stack_ptr->rightp = false;
267           node = node->left;
268           stack_ptr++;
269         }
270       /* Climb up again.  */
271       for (;;)
272         {
273           if (stack_ptr == &stack[0])
274             return;
275           stack_ptr--;
276           if (!stack_ptr->rightp)
277             break;
278         }
279       node = stack_ptr->node;
280       /* Add the current node to the hash table.  */
281       node->h.hashcode =
282         (list->base.hashcode_fn != NULL
283          ? list->base.hashcode_fn (node->value)
284          : (size_t)(uintptr_t) node->value);
285       add_to_bucket (list, node);
286       /* Descend on right branch.  */
287       stack_ptr->rightp = true;
288       node = node->right;
289       stack_ptr++;
290     }
291 }