Skip to content

Commit abbff8d

Browse files
committed
itable behave like hash_table for double/reduce/cant_iterate
1 parent 1b3bf37 commit abbff8d

File tree

2 files changed

+169
-42
lines changed

2 files changed

+169
-42
lines changed

dttools/src/itable.c

Lines changed: 143 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -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

1618
struct 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

3040
struct 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

7488
void 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+
81121
int 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+
86152
void *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

104170
static 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

145200
int 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

219314
void 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

229326
int 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)

dttools/src/itable.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,39 @@ Note that this function will not delete all of the objects contained within the
6969

7070
void itable_delete(struct itable *h);
7171

72+
73+
/** Return an array with all the current keys.
74+
It is the responsibility of the caller to free this array with
75+
itable_free_keys_array.
76+
@param h A pointer to a hash table.
77+
@return An array of all the current keys.
78+
*/
79+
80+
UINT64_T *itable_keys_array(struct itable *h);
81+
82+
83+
/** Free an array generated from itable_free_keys_array.
84+
@param keys An array of all the keys.
85+
*/
86+
87+
void itable_free_keys_array(UINT64_T *keys);
88+
89+
7290
/** Count the entries in an integer table.
7391
@return The number of entries in the table.
7492
@param h A pointer to an integer table.
7593
*/
7694

7795
int itable_size(struct itable *h);
7896

97+
/** Get the proportion of elements vs buckets in the table.
98+
@return The load of the table.
99+
@param h A pointer to an integer table.
100+
*/
101+
102+
double itable_load(struct itable *h);
103+
104+
79105
/** Insert a key and value.
80106
This call will fail if the table already contains the same key.
81107
You must call @ref itable_remove to remove it.

0 commit comments

Comments
 (0)