@@ -6,12 +6,14 @@ See the file COPYING for details.
66*/
77
88#include "itable.h"
9+ #include "debug.h"
910
1011#include <stdlib.h>
1112#include <string.h>
1213
1314#define DEFAULT_SIZE 127
14- #define DEFAULT_LOAD 0.75
15+ #define DEFAULT_MAX_LOAD 0.75
16+ #define DEFAULT_MIN_LOAD 0.125
1517
1618struct entry {
1719 UINT64_T key ;
@@ -25,6 +27,14 @@ struct itable {
2527 struct entry * * buckets ;
2628 int ibucket ;
2729 struct entry * ientry ;
30+
31+ /* for memory safety, itable_nextkey cannot be called in the same
32+ * iteration if itable_insert or itable_remove has been called.
33+ * In such case, the executable will be terminated with a fatal message.
34+ * If the table should be modified during iterations, consider
35+ * using the array keys from itable_keys_array. (If so, remember
36+ * to free it afterwards with itable_free_keys_array.) */
37+ int cant_iterate_yet ;
2838};
2939
3040struct itable * itable_create (int bucket_count )
@@ -46,6 +56,7 @@ struct itable *itable_create(int bucket_count)
4656 }
4757
4858 h -> size = 0 ;
59+ h -> cant_iterate_yet = 0 ;
4960
5061 return h ;
5162}
@@ -69,6 +80,9 @@ void itable_clear(struct itable *h, void (*delete_func)(void *))
6980 for (i = 0 ; i < h -> bucket_count ; i ++ ) {
7081 h -> buckets [i ] = 0 ;
7182 }
83+
84+ /* buckets went away, thus a nextkey would be invalid */
85+ h -> cant_iterate_yet = 1 ;
7286}
7387
7488void itable_delete (struct itable * h )
@@ -78,11 +92,63 @@ void itable_delete(struct itable *h)
7892 free (h );
7993}
8094
95+ UINT64_T * itable_keys_array (struct itable * h )
96+ {
97+ UINT64_T * keys = (UINT64_T * )malloc (sizeof (int ) * h -> size );
98+ int ikey = 0 ;
99+
100+ struct entry * e , * f ;
101+ int i ;
102+
103+ for (i = 0 ; i < h -> bucket_count ; i ++ ) {
104+ e = h -> buckets [i ];
105+ while (e ) {
106+ keys [ikey ] = e -> key ;
107+ ikey ++ ;
108+ f = e -> next ;
109+ e = f ;
110+ }
111+ }
112+
113+ return keys ;
114+ }
115+
116+ void itable_free_keys_array (UINT64_T * keys )
117+ {
118+ free (keys );
119+ }
120+
81121int itable_size (struct itable * h )
82122{
83123 return h -> size ;
84124}
85125
126+ double itable_load (struct itable * h )
127+ {
128+ return (double )h -> size / h -> bucket_count ;
129+ }
130+
131+ static int insert_to_buckets_aux (struct entry * * buckets , int bucket_count , struct entry * new_entry )
132+ {
133+ unsigned index ;
134+ struct entry * e ;
135+
136+ index = new_entry -> key % bucket_count ;
137+ e = buckets [index ];
138+
139+ while (e ) {
140+ /* check that this key does not already exist in the table */
141+ if (new_entry -> key == e -> key ) {
142+ return 0 ;
143+ }
144+ e = e -> next ;
145+ }
146+
147+ new_entry -> next = buckets [index ];
148+ buckets [index ] = new_entry ;
149+ return 1 ;
150+ }
151+
86152void * itable_lookup (struct itable * h , UINT64_T key )
87153{
88154 struct entry * e ;
@@ -103,73 +169,96 @@ void *itable_lookup(struct itable *h, UINT64_T key)
103169
104170static int itable_double_buckets (struct itable * h )
105171{
106- struct itable * hn = itable_create (2 * h -> bucket_count ) ;
107-
108- if (!hn )
172+ int new_count = (2 * ( h -> bucket_count + 1 )) - 1 ;
173+ struct entry * * new_buckets = ( struct entry * * ) calloc ( new_count , sizeof ( struct entry * ));
174+ if (!new_buckets ) {
109175 return 0 ;
176+ }
110177
111- /* Move pairs to new hash */
112- uint64_t key ;
113- void * value ;
114- itable_firstkey (h );
115- while (itable_nextkey (h , & key , & value ))
116- if (!itable_insert (hn , key , value )) {
117- itable_delete (hn );
118- return 0 ;
119- }
120-
121- /* Delete all old pairs */
122178 struct entry * e , * f ;
123- int i ;
124- for (i = 0 ; i < h -> bucket_count ; i ++ ) {
179+ for (int i = 0 ; i < h -> bucket_count ; i ++ ) {
125180 e = h -> buckets [i ];
126181 while (e ) {
127182 f = e -> next ;
128- free (e );
183+ e -> next = NULL ;
184+ insert_to_buckets_aux (new_buckets , new_count , e );
129185 e = f ;
130186 }
131187 }
132188
133189 /* Make the old point to the new */
134190 free (h -> buckets );
135- h -> buckets = hn -> buckets ;
136- h -> bucket_count = hn -> bucket_count ;
137- h -> size = hn -> size ;
191+ h -> buckets = new_buckets ;
192+ h -> bucket_count = new_count ;
138193
139- /* Delete reference to new, so old is safe */
140- free ( hn ) ;
194+ /* structure of itable changed completely, thus a nextkey would be incorrect. */
195+ h -> cant_iterate_yet = 1 ;
141196
142197 return 1 ;
143198}
144199
145200int itable_insert (struct itable * h , UINT64_T key , const void * value )
146201{
147- struct entry * e ;
148- UINT64_T index ;
149-
150- if (((float )h -> size / h -> bucket_count ) > DEFAULT_LOAD )
202+ if (((float )h -> size / h -> bucket_count ) > DEFAULT_MAX_LOAD )
151203 itable_double_buckets (h );
152204
153- index = key % h -> bucket_count ;
154- e = h -> buckets [index ];
205+ struct entry * new_entry = (struct entry * )malloc (sizeof (struct entry ));
206+ if (!new_entry )
207+ return 0 ;
155208
156- while (e ) {
157- if (key == e -> key ) {
158- e -> value = (void * )value ;
159- return 1 ;
160- }
161- e = e -> next ;
209+ new_entry -> key = key ;
210+ new_entry -> value = (void * )value ;
211+
212+ int inserted = insert_to_buckets_aux (h -> buckets , h -> bucket_count , new_entry );
213+ if (inserted ) {
214+ h -> size ++ ;
215+ /* inserting cause different behaviours with nextkey (e.g., sometimes the new
216+ * key would be included or skipped in the iteration */
217+ h -> cant_iterate_yet = 1 ;
218+
219+ return 1 ;
220+ }
221+
222+ return 0 ;
223+ }
224+
225+ static int itable_reduce_buckets (struct itable * h )
226+ {
227+ int new_count = ((h -> bucket_count + 1 ) / 2 ) - 1 ;
228+
229+ /* DEFAULT_SIZE is the minimum size */
230+ if (new_count < DEFAULT_SIZE ) {
231+ return 1 ;
162232 }
163233
164- e = (struct entry * )malloc (sizeof (struct entry ));
165- if (!e )
234+ /* Table cannot be reduced above DEFAULT_MAX_LOAD */
235+ if (((float )h -> size / new_count ) > DEFAULT_MAX_LOAD ) {
236+ return 1 ;
237+ }
238+
239+ struct entry * * new_buckets = (struct entry * * )calloc (new_count , sizeof (struct entry * ));
240+ if (!new_buckets ) {
166241 return 0 ;
242+ }
167243
168- e -> key = key ;
169- e -> value = (void * )value ;
170- e -> next = h -> buckets [index ];
171- h -> buckets [index ] = e ;
172- h -> size ++ ;
244+ struct entry * e , * f ;
245+ for (int i = 0 ; i < h -> bucket_count ; i ++ ) {
246+ e = h -> buckets [i ];
247+ while (e ) {
248+ f = e -> next ;
249+ e -> next = NULL ;
250+ insert_to_buckets_aux (new_buckets , new_count , e );
251+ e = f ;
252+ }
253+ }
254+
255+ /* Make the old point to the new */
256+ free (h -> buckets );
257+ h -> buckets = new_buckets ;
258+ h -> bucket_count = new_count ;
259+
260+ /* structure of itable changed completely, thus a nextkey would be incorrect. */
261+ h -> cant_iterate_yet = 1 ;
173262
174263 return 1 ;
175264}
@@ -194,6 +283,12 @@ void *itable_remove(struct itable *h, UINT64_T key)
194283 value = e -> value ;
195284 free (e );
196285 h -> size -- ;
286+ h -> cant_iterate_yet = 1 ;
287+
288+ if (((float )h -> size / h -> bucket_count ) < DEFAULT_MIN_LOAD ) {
289+ itable_reduce_buckets (h );
290+ }
291+
197292 return value ;
198293 }
199294 f = e ;
@@ -218,6 +313,8 @@ void *itable_pop(struct itable *t)
218313
219314void itable_firstkey (struct itable * h )
220315{
316+ h -> cant_iterate_yet = 0 ;
317+
221318 h -> ientry = 0 ;
222319 for (h -> ibucket = 0 ; h -> ibucket < h -> bucket_count ; h -> ibucket ++ ) {
223320 h -> ientry = h -> buckets [h -> ibucket ];
@@ -228,6 +325,10 @@ void itable_firstkey(struct itable *h)
228325
229326int itable_nextkey (struct itable * h , UINT64_T * key , void * * value )
230327{
328+ if (h -> cant_iterate_yet ) {
329+ fatal ("cctools bug: the itable iteration has not been reset since last modification" );
330+ }
331+
231332 if (h -> ientry ) {
232333 * key = h -> ientry -> key ;
233334 if (value )
0 commit comments