44 |
** These are used in freeing a table. Perhaps I should code up |
** These are used in freeing a table. Perhaps I should code up |
45 |
** something a little less grungy, but it works, so what the heck. |
** something a little less grungy, but it works, so what the heck. |
46 |
*/ |
*/ |
47 |
static void (*function)(void *) = NULL; |
static void (*function) (void *) = NULL; |
48 |
static hash_table *the_table = NULL; |
static hash_table *the_table = NULL; |
49 |
|
|
50 |
|
|
54 |
** of the table to 0. |
** of the table to 0. |
55 |
*/ |
*/ |
56 |
/*HW: changed, now returns NULL on malloc-failure */ |
/*HW: changed, now returns NULL on malloc-failure */ |
57 |
hash_table *hash_construct_table( hash_table *table, size_t size ) |
hash_table *hash_construct_table(hash_table * table, size_t size) |
58 |
{ |
{ |
59 |
size_t i; |
size_t i; |
60 |
bucket **temp; |
bucket **temp; |
61 |
|
|
62 |
table->size = size; |
table->size = size; |
63 |
table->count = 0; |
table->count = 0; |
64 |
table->table = (bucket **)malloc(sizeof(bucket *) * size); |
table->table = (bucket **) malloc(sizeof(bucket *) * size); |
65 |
temp = table->table; |
temp = table->table; |
|
|
|
|
if( NULL == temp ) |
|
|
{ |
|
|
table->size = 0; |
|
|
return NULL; /*HW: was 'table' */ |
|
|
} |
|
66 |
|
|
67 |
for( i=0; i<size; i++ ) |
if (NULL == temp) { |
68 |
temp[i] = NULL; |
table->size = 0; |
69 |
|
return NULL; /*HW: was 'table' */ |
70 |
|
} |
71 |
|
|
72 |
return table; |
for (i = 0; i < size; i++) |
73 |
|
temp[i] = NULL; |
74 |
|
|
75 |
|
return table; |
76 |
} |
} |
77 |
|
|
78 |
|
|
84 |
|
|
85 |
static unsigned short hash(char *string) |
static unsigned short hash(char *string) |
86 |
{ |
{ |
87 |
unsigned short ret_val = 0; |
unsigned short ret_val = 0; |
88 |
int i; |
int i; |
89 |
|
|
90 |
while (*string) |
while (*string) { |
91 |
{ |
/* |
92 |
/* |
** RBS: Added conditional to account for strings in which the |
93 |
** RBS: Added conditional to account for strings in which the |
** length is less than an integral multiple of sizeof(int). |
94 |
** length is less than an integral multiple of sizeof(int). |
** |
95 |
** |
** Note: This fixes the problem of hasing trailing garbage, but |
96 |
** Note: This fixes the problem of hasing trailing garbage, but |
** doesn't fix the problem with some CPU's which can't align on |
97 |
** doesn't fix the problem with some CPU's which can't align on |
** byte boundries. Any decent C compiler *should* fix this, but |
98 |
** byte boundries. Any decent C compiler *should* fix this, but |
** it still might extract a performance hit. Also unaddressed is |
99 |
** it still might extract a performance hit. Also unaddressed is |
** what happens when using a CPU which addresses data only on |
100 |
** what happens when using a CPU which addresses data only on |
** 4-byte boundries when it tries to work with a pointer to a |
101 |
** 4-byte boundries when it tries to work with a pointer to a |
** 2-byte unsigned short. |
102 |
** 2-byte unsigned short. |
*/ |
103 |
*/ |
|
104 |
|
if (strlen(string) >= sizeof(unsigned short)) |
105 |
if (strlen(string) >= sizeof(unsigned short)) |
i = *(unsigned short *) string; |
106 |
i = *(unsigned short *)string; |
else |
107 |
else i = (unsigned short)(*string); |
i = (unsigned short) (*string); |
108 |
ret_val ^= i; |
ret_val ^= i; |
109 |
ret_val <<= 1; |
ret_val <<= 1; |
110 |
string ++; |
string++; |
111 |
} |
} |
112 |
return ret_val; |
return ret_val; |
113 |
} |
} |
114 |
|
|
115 |
/* |
/* |
118 |
** NULL if the key wasn't in the table previously. |
** NULL if the key wasn't in the table previously. |
119 |
*/ |
*/ |
120 |
/* HW: returns NULL if malloc failed */ |
/* HW: returns NULL if malloc failed */ |
121 |
void *hash_insert( char *key, void *data, hash_table *table ) |
void *hash_insert(char *key, void *data, hash_table * table) |
122 |
{ |
{ |
123 |
unsigned short val = hash(key) % table->size; |
unsigned short val = hash(key) % table->size; |
124 |
bucket *ptr; |
bucket *ptr; |
125 |
|
|
126 |
assert( NULL != key ); |
assert(NULL != key); |
127 |
|
|
128 |
/* |
/* |
129 |
** NULL means this bucket hasn't been used yet. We'll simply |
** NULL means this bucket hasn't been used yet. We'll simply |
130 |
** allocate space for our new bucket and put our data there, with |
** allocate space for our new bucket and put our data there, with |
131 |
** the table pointing at it. |
** the table pointing at it. |
132 |
*/ |
*/ |
133 |
|
|
134 |
if( NULL == (table->table)[val] ) |
if (NULL == (table->table)[val]) { |
135 |
{ |
(table->table)[val] = (bucket *) malloc(sizeof(bucket)); |
136 |
(table->table)[val] = (bucket *)malloc(sizeof(bucket)); |
if (NULL == (table->table)[val]) |
137 |
if( NULL == (table->table)[val] ) |
return NULL; |
138 |
return NULL; |
|
139 |
|
if (NULL == |
140 |
if( NULL == ((table->table)[val]->key = (char*) malloc(strlen(key)+1)) ) |
((table->table)[val]->key = (char *) malloc(strlen(key) + 1))) { |
141 |
{ |
free((table->table)[val]); |
142 |
free( (table->table)[val] ); |
(table->table)[val] = NULL; |
143 |
(table->table)[val] = NULL; |
return NULL; |
144 |
return NULL; |
} |
145 |
} |
strcpy((table->table)[val]->key, key); |
146 |
strcpy( (table->table)[val]->key, key); |
(table->table)[val]->next = NULL; |
147 |
(table->table)[val] -> next = NULL; |
(table->table)[val]->data = data; |
148 |
(table->table)[val] -> data = data; |
table->count++; /* HW */ |
149 |
table->count++; /* HW */ |
return (table->table)[val]->data; |
150 |
return (table->table)[val] -> data; |
} |
|
} |
|
151 |
|
|
152 |
/* HW: added a #define so the hashtable can accept duplicate keys */ |
/* HW: added a #define so the hashtable can accept duplicate keys */ |
153 |
#ifndef DUPLICATE_KEYS |
#ifndef DUPLICATE_KEYS |
154 |
/* |
/* |
155 |
** This spot in the table is already in use. See if the current string |
** This spot in the table is already in use. See if the current string |
156 |
** has already been inserted, and if so, increment its count. |
** has already been inserted, and if so, increment its count. |
157 |
*/ /* HW: ^^^^^^^^ ?? */ |
*//* HW: ^^^^^^^^ ?? */ |
158 |
for( ptr = (table->table)[val]; NULL != ptr; ptr = ptr->next ) |
for (ptr = (table->table)[val]; NULL != ptr; ptr = ptr->next) |
159 |
if( 0 == strcmp(key, ptr->key) ) |
if (0 == strcmp(key, ptr->key)) { |
160 |
{ |
void *old_data; |
161 |
void *old_data; |
|
162 |
|
old_data = ptr->data; |
163 |
old_data = ptr->data; |
ptr->data = data; |
164 |
ptr->data = data; |
return old_data; |
165 |
return old_data; |
} |
|
} |
|
166 |
#endif |
#endif |
167 |
/* |
/* |
168 |
** This key must not be in the table yet. We'll add it to the head of |
** This key must not be in the table yet. We'll add it to the head of |
169 |
** the list at this spot in the hash table. Speed would be |
** the list at this spot in the hash table. Speed would be |
170 |
** slightly improved if the list was kept sorted instead. In this case, |
** slightly improved if the list was kept sorted instead. In this case, |
171 |
** this code would be moved into the loop above, and the insertion would |
** this code would be moved into the loop above, and the insertion would |
172 |
** take place as soon as it was determined that the present key in the |
** take place as soon as it was determined that the present key in the |
173 |
** list was larger than this one. |
** list was larger than this one. |
174 |
*/ |
*/ |
175 |
|
|
176 |
ptr = (bucket *)malloc(sizeof(bucket)); |
ptr = (bucket *) malloc(sizeof(bucket)); |
177 |
if( NULL == ptr ) |
if (NULL == ptr) |
178 |
return NULL; /*HW: was 0 */ |
return NULL; /*HW: was 0 */ |
179 |
|
|
180 |
if( NULL == (ptr -> key = (char*) malloc(strlen(key)+1)) ) |
if (NULL == (ptr->key = (char *) malloc(strlen(key) + 1))) { |
181 |
{ |
free(ptr); |
182 |
free(ptr); |
return NULL; |
183 |
return NULL; |
} |
184 |
} |
strcpy(ptr->key, key); |
185 |
strcpy( ptr->key, key ); |
ptr->data = data; |
186 |
ptr -> data = data; |
ptr->next = (table->table)[val]; |
187 |
ptr -> next = (table->table)[val]; |
(table->table)[val] = ptr; |
188 |
(table->table)[val] = ptr; |
table->count++; /* HW */ |
|
table->count++; /* HW */ |
|
189 |
|
|
190 |
return data; |
return data; |
191 |
} |
} |
192 |
|
|
193 |
|
|
195 |
** Look up a key and return the associated data. Returns NULL if |
** Look up a key and return the associated data. Returns NULL if |
196 |
** the key is not in the table. |
** the key is not in the table. |
197 |
*/ |
*/ |
198 |
void *hash_lookup( char *key, hash_table *table ) |
void *hash_lookup(char *key, hash_table * table) |
199 |
{ |
{ |
200 |
unsigned short val = hash(key) % table->size; |
unsigned short val = hash(key) % table->size; |
201 |
bucket *ptr; |
bucket *ptr; |
202 |
|
|
203 |
assert( NULL != key ); |
assert(NULL != key); |
204 |
|
|
205 |
if(NULL == (table->table)[val]) |
if (NULL == (table->table)[val]) |
206 |
return NULL; |
return NULL; |
207 |
|
|
208 |
for( ptr = (table->table)[val]; NULL != ptr; ptr = ptr->next ) |
for (ptr = (table->table)[val]; NULL != ptr; ptr = ptr->next) { |
209 |
{ |
if (0 == strcmp(key, ptr->key)) |
210 |
if(0 == strcmp( key, ptr -> key ) ) |
return ptr->data; |
211 |
return ptr->data; |
} |
|
} |
|
212 |
|
|
213 |
return NULL; |
return NULL; |
214 |
} |
} |
215 |
|
|
216 |
/* |
/* |
218 |
** data, or NULL if not present. |
** data, or NULL if not present. |
219 |
*/ |
*/ |
220 |
|
|
221 |
void *hash_del(char *key, hash_table *table) |
void *hash_del(char *key, hash_table * table) |
222 |
{ |
{ |
223 |
unsigned short val = hash(key) % table->size; |
unsigned short val = hash(key) % table->size; |
224 |
void *data; |
void *data; |
225 |
bucket *ptr, *last = NULL; |
bucket *ptr, *last = NULL; |
226 |
|
|
227 |
assert( NULL != key ); |
assert(NULL != key); |
228 |
|
|
229 |
if( NULL == (table->table)[val] ) |
if (NULL == (table->table)[val]) |
230 |
return NULL; /* HW: was 'return 0' */ |
return NULL; /* HW: was 'return 0' */ |
231 |
|
|
232 |
/* |
/* |
233 |
** Traverse the list, keeping track of the previous node in the list. |
** Traverse the list, keeping track of the previous node in the list. |
234 |
** When we find the node to delete, we set the previous node's next |
** When we find the node to delete, we set the previous node's next |
235 |
** pointer to point to the node after ourself instead. We then delete |
** pointer to point to the node after ourself instead. We then delete |
236 |
** the key from the present node, and return a pointer to the data it |
** the key from the present node, and return a pointer to the data it |
237 |
** contains. |
** contains. |
238 |
*/ |
*/ |
239 |
for( last = NULL, ptr = (table->table)[val]; |
for (last = NULL, ptr = (table->table)[val]; |
240 |
NULL != ptr; |
NULL != ptr; last = ptr, ptr = ptr->next) { |
241 |
last = ptr, ptr = ptr->next ) |
if (0 == strcmp(key, ptr->key)) { |
242 |
{ |
if (last != NULL) { |
243 |
if( 0 == strcmp( key, ptr -> key) ) |
data = ptr->data; |
244 |
{ |
last->next = ptr->next; |
245 |
if( last != NULL ) |
free(ptr->key); |
246 |
{ |
free(ptr); |
247 |
data = ptr -> data; |
table->count--; /* HW */ |
248 |
last -> next = ptr -> next; |
return data; |
|
free( ptr->key ); |
|
|
free( ptr ); |
|
|
table->count--; /* HW */ |
|
|
return data; |
|
|
} |
|
|
|
|
|
/* If 'last' still equals NULL, it means that we need to |
|
|
** delete the first node in the list. This simply consists |
|
|
** of putting our own 'next' pointer in the array holding |
|
|
** the head of the list. We then dispose of the current |
|
|
** node as above. |
|
|
*/ |
|
|
else |
|
|
{ |
|
|
/* HW: changed this bit to match the comments above */ |
|
|
data = ptr->data; |
|
|
(table->table)[val] = ptr->next; |
|
|
free( ptr->key ); |
|
|
free( ptr ); |
|
|
table->count--; /* HW */ |
|
|
return data; |
|
|
} |
|
249 |
} |
} |
|
} |
|
250 |
|
|
251 |
/* |
/* If 'last' still equals NULL, it means that we need to |
252 |
** If we get here, it means we didn't find the item in the table. |
** delete the first node in the list. This simply consists |
253 |
** Signal this by returning NULL. |
** of putting our own 'next' pointer in the array holding |
254 |
*/ |
** the head of the list. We then dispose of the current |
255 |
|
** node as above. |
256 |
|
*/ |
257 |
|
else { |
258 |
|
/* HW: changed this bit to match the comments above */ |
259 |
|
data = ptr->data; |
260 |
|
(table->table)[val] = ptr->next; |
261 |
|
free(ptr->key); |
262 |
|
free(ptr); |
263 |
|
table->count--; /* HW */ |
264 |
|
return data; |
265 |
|
} |
266 |
|
} |
267 |
|
} |
268 |
|
|
269 |
|
/* |
270 |
|
** If we get here, it means we didn't find the item in the table. |
271 |
|
** Signal this by returning NULL. |
272 |
|
*/ |
273 |
|
|
274 |
return NULL; |
return NULL; |
275 |
} |
} |
276 |
|
|
277 |
/* |
/* |
282 |
** process the data as needed. |
** process the data as needed. |
283 |
*/ |
*/ |
284 |
|
|
285 |
static void free_node( char *key, void *data ) |
static void free_node(char *key, void *data) |
286 |
{ |
{ |
287 |
(void) data; |
(void) data; |
288 |
|
|
289 |
assert( NULL != key ); |
assert(NULL != key); |
290 |
|
|
291 |
if( NULL != function ) |
if (NULL != function) { |
292 |
{ |
function(hash_del(key, the_table)); |
293 |
function( hash_del( key, the_table ) ); |
} |
294 |
} |
else |
295 |
else |
hash_del(key, the_table); |
|
hash_del( key, the_table ); |
|
296 |
} |
} |
297 |
|
|
298 |
/* |
/* |
303 |
** it. |
** it. |
304 |
*/ |
*/ |
305 |
|
|
306 |
void hash_free_table( hash_table *table, void (*func)(void *) ) |
void hash_free_table(hash_table * table, void (*func) (void *)) |
307 |
{ |
{ |
308 |
function = func; |
function = func; |
309 |
the_table = table; |
the_table = table; |
310 |
|
|
311 |
hash_enumerate( table, free_node ); |
hash_enumerate(table, free_node); |
312 |
free( table->table ); |
free(table->table); |
313 |
table->table = NULL; |
table->table = NULL; |
314 |
table->size = 0; |
table->size = 0; |
315 |
table->count = 0; /* HW */ |
table->count = 0; /* HW */ |
316 |
|
|
317 |
the_table = NULL; |
the_table = NULL; |
318 |
function = NULL; |
function = NULL; |
319 |
} |
} |
320 |
|
|
321 |
/* |
/* |
323 |
** node in the table, passing it the key and the associated data. |
** node in the table, passing it the key and the associated data. |
324 |
*/ |
*/ |
325 |
|
|
326 |
void hash_enumerate( hash_table *table, void (*func)(char *, void *) ) |
void hash_enumerate(hash_table * table, void (*func) (char *, void *)) |
327 |
{ |
{ |
328 |
unsigned i; |
unsigned i; |
329 |
bucket *temp; |
bucket *temp; |
330 |
bucket *swap; |
bucket *swap; |
331 |
|
|
332 |
for( i=0; i<table->size; i++ ) |
for (i = 0; i < table->size; i++) { |
333 |
{ |
if (NULL != (table->table)[i]) { |
334 |
if( NULL != (table->table)[i] ) |
/* HW: changed this loop */ |
335 |
{ |
temp = (table->table)[i]; |
336 |
/* HW: changed this loop */ |
while (NULL != temp) { |
337 |
temp = (table->table)[i]; |
/* HW: swap trick, in case temp is freed by 'func' */ |
338 |
while( NULL != temp ) |
swap = temp->next; |
339 |
{ |
func(temp->key, temp->data); |
340 |
/* HW: swap trick, in case temp is freed by 'func' */ |
temp = swap; |
|
swap = temp->next; |
|
|
func( temp -> key, temp->data ); |
|
|
temp = swap; |
|
|
} |
|
341 |
} |
} |
342 |
} |
} |
343 |
|
} |
344 |
} |
} |
345 |
|
|
346 |
/* HW: added hash_sorted_enum() |
/* HW: added hash_sorted_enum() |
351 |
*/ |
*/ |
352 |
|
|
353 |
typedef struct sort_struct |
typedef struct sort_struct |
354 |
{ |
{ |
355 |
char *key; |
char *key; |
356 |
void *data; |
void *data; |
357 |
} sort_struct; |
} sort_struct; |
358 |
static sort_struct *sortmap = NULL; |
static sort_struct *sortmap = NULL; |
359 |
|
|
360 |
static int counter = 0; |
static int counter = 0; |
361 |
|
|
362 |
/* HW: used as 'func' for hash_enumerate */ |
/* HW: used as 'func' for hash_enumerate */ |
363 |
static void key_get( char *key, void *data ) |
static void key_get(char *key, void *data) |
364 |
{ |
{ |
365 |
sortmap[ counter ].key = key; |
sortmap[counter].key = key; |
366 |
sortmap[ counter ].data = data; |
sortmap[counter].data = data; |
367 |
counter++; |
counter++; |
368 |
} |
} |
369 |
|
|
370 |
/* HW: used for comparing the keys in qsort */ |
/* HW: used for comparing the keys in qsort */ |
371 |
static int key_comp( const void* a, const void *b ) |
static int key_comp(const void *a, const void *b) |
372 |
{ |
{ |
373 |
return strcmp( (*(sort_struct*)a).key, (*(sort_struct*)b).key ); |
return strcmp((*(sort_struct *) a).key, (*(sort_struct *) b).key); |
374 |
} |
} |
375 |
|
|
376 |
/* HW: it's a compromise between speed and space. this one needs |
/* HW: it's a compromise between speed and space. this one needs |
379 |
to hash_lookup the data of every key after sorting the key. |
to hash_lookup the data of every key after sorting the key. |
380 |
returns 0 on malloc failure, 1 on success |
returns 0 on malloc failure, 1 on success |
381 |
*/ |
*/ |
382 |
int hash_sorted_enum( hash_table *table, void (*func)( char *, void *) ) |
int hash_sorted_enum(hash_table * table, void (*func) (char *, void *)) |
383 |
{ |
{ |
384 |
int i; |
int i; |
385 |
|
|
386 |
/* nothing to do ! */ |
/* nothing to do ! */ |
387 |
if( NULL == table || 0 == table->count || NULL == func ) |
if (NULL == table || 0 == table->count || NULL == func) |
388 |
return 0; |
return 0; |
389 |
|
|
390 |
/* malloc an pointerarray for all hashkey's and datapointers */ |
/* malloc an pointerarray for all hashkey's and datapointers */ |
391 |
if( NULL == ( sortmap = (sort_struct*) malloc( sizeof( sort_struct ) * table->count)) ) |
if (NULL == |
392 |
return 0; |
(sortmap = |
393 |
|
(sort_struct *) malloc(sizeof(sort_struct) * table->count))) |
394 |
|
return 0; |
395 |
|
|
396 |
/* copy the pointers to the hashkey's */ |
/* copy the pointers to the hashkey's */ |
397 |
counter = 0; |
counter = 0; |
398 |
hash_enumerate( table, key_get ); |
hash_enumerate(table, key_get); |
399 |
|
|
400 |
/* sort the pointers to the keys */ |
/* sort the pointers to the keys */ |
401 |
qsort( sortmap, table->count, sizeof(sort_struct), key_comp ); |
qsort(sortmap, table->count, sizeof(sort_struct), key_comp); |
402 |
|
|
403 |
/* call the function for each node */ |
/* call the function for each node */ |
404 |
for( i=0; i <abs( (table->count)); i++ ) |
for (i = 0; i < abs((table->count)); i++) { |
405 |
{ |
func(sortmap[i].key, sortmap[i].data); |
406 |
func( sortmap[i].key, sortmap[i].data ); |
} |
|
} |
|
407 |
|
|
408 |
/* free the pointerarray */ |
/* free the pointerarray */ |
409 |
free( sortmap ); |
free(sortmap); |
410 |
sortmap = NULL; |
sortmap = NULL; |
411 |
|
|
412 |
return 1; |
return 1; |
413 |
} |
} |
414 |
|
|
415 |
/* HW: changed testmain */ |
/* HW: changed testmain */ |
423 |
|
|
424 |
void fprinter(char *string, void *data) |
void fprinter(char *string, void *data) |
425 |
{ |
{ |
426 |
fprintf(o,"%s: %s\n", string, (char *)data); |
fprintf(o, "%s: %s\n", string, (char *) data); |
427 |
} |
} |
428 |
|
|
429 |
void printer(char *string, void *data) |
void printer(char *string, void *data) |
430 |
{ |
{ |
431 |
printf("%s: %s\n", string, (char *)data); |
printf("%s: %s\n", string, (char *) data); |
432 |
} |
} |
433 |
|
|
434 |
/* function to pass to hash_free_table */ |
/* function to pass to hash_free_table */ |
435 |
void strfree( void *d ) |
void strfree(void *d) |
436 |
{ |
{ |
437 |
/* any additional processing goes here (if you use structures as data) */ |
/* any additional processing goes here (if you use structures as data) */ |
438 |
/* free the datapointer */ |
/* free the datapointer */ |
439 |
free(d); |
free(d); |
440 |
} |
} |
441 |
|
|
442 |
|
|
443 |
int main(void) |
int main(void) |
444 |
{ |
{ |
445 |
hash_table table; |
hash_table table; |
446 |
|
|
447 |
char *strings[] = { |
char *strings[] = { |
448 |
"The first string", |
"The first string", |
449 |
"The second string", |
"The second string", |
450 |
"The third string", |
"The third string", |
451 |
"The fourth string", |
"The fourth string", |
452 |
"A much longer string than the rest in this example.", |
"A much longer string than the rest in this example.", |
453 |
"The last string", |
"The last string", |
454 |
NULL |
NULL |
455 |
}; |
}; |
456 |
|
|
457 |
char *junk[] = { |
char *junk[] = { |
458 |
"The first data", |
"The first data", |
459 |
"The second data", |
"The second data", |
460 |
"The third data", |
"The third data", |
461 |
"The fourth data", |
"The fourth data", |
462 |
"The fifth datum", |
"The fifth datum", |
463 |
"The sixth piece of data" |
"The sixth piece of data" |
464 |
}; |
}; |
465 |
|
|
466 |
int i; |
int i; |
467 |
void *j; |
void *j; |
468 |
|
|
469 |
hash_construct_table(&table,211); |
hash_construct_table(&table, 211); |
470 |
|
|
471 |
/* I know, no checking on strdup ;-)), but using strdup |
/* I know, no checking on strdup ;-)), but using strdup |
472 |
to demonstrate hash_table_free with a functionpointer */ |
to demonstrate hash_table_free with a functionpointer */ |
473 |
for (i = 0; NULL != strings[i]; i++ ) |
for (i = 0; NULL != strings[i]; i++) |
474 |
hash_insert( strings[i], strdup(junk[i]), &table ); |
hash_insert(strings[i], strdup(junk[i]), &table); |
475 |
|
|
476 |
/* enumerating to a file */ |
/* enumerating to a file */ |
477 |
if( NULL != (o = fopen("HASH.HSH","wb")) ) |
if (NULL != (o = fopen("HASH.HSH", "wb"))) { |
478 |
{ |
fprintf(o, "%d strings in the table:\n\n", table.count); |
479 |
fprintf( o, "%d strings in the table:\n\n", table.count ); |
hash_enumerate(&table, fprinter); |
480 |
hash_enumerate( &table, fprinter ); |
fprintf(o, "\nsorted by key:\n"); |
481 |
fprintf( o, "\nsorted by key:\n"); |
hash_sorted_enum(&table, fprinter); |
482 |
hash_sorted_enum( &table, fprinter ); |
fclose(o); |
483 |
fclose( o ); |
} |
484 |
} |
|
485 |
|
/* enumerating to screen */ |
486 |
/* enumerating to screen */ |
hash_sorted_enum(&table, printer); |
487 |
hash_sorted_enum( &table, printer ); |
printf("\n"); |
488 |
printf("\n"); |
|
489 |
|
/* delete 3 strings, should be 3 left */ |
490 |
/* delete 3 strings, should be 3 left */ |
for (i = 0; i < 3; i++) { |
491 |
for( i=0; i<3; i++ ) |
/* hash_del returns a pointer to the data */ |
492 |
{ |
strfree(hash_del(strings[i], &table)); |
493 |
/* hash_del returns a pointer to the data */ |
} |
494 |
strfree( hash_del( strings[i], &table) ); |
hash_enumerate(&table, printer); |
495 |
} |
|
496 |
hash_enumerate( &table, printer); |
for (i = 0; NULL != strings[i]; i++) { |
497 |
|
j = hash_lookup(strings[i], &table); |
498 |
for (i=0;NULL != strings[i];i++) |
if (NULL == j) |
499 |
{ |
printf("\n'%s' is not in the table", strings[i]); |
500 |
j = hash_lookup(strings[i], &table); |
else |
501 |
if (NULL == j) |
printf("\n%s is still in the table.", strings[i]); |
502 |
printf("\n'%s' is not in the table", strings[i]); |
} |
|
else |
|
|
printf("\n%s is still in the table.", strings[i]); |
|
|
} |
|
503 |
|
|
504 |
hash_free_table( &table, strfree ); |
hash_free_table(&table, strfree); |
505 |
|
|
506 |
return 0; |
return 0; |
507 |
} |
} |
508 |
#endif /* TEST */ |
#endif /* TEST */ |