strtod: next round of AIX fixes
[gnulib.git] / tests / test-hash.c
1 /*
2  * Copyright (C) 2009, 2010 Free Software Foundation, Inc.
3  * Written by Jim Meyering
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 3 of the License, or
8  * (at your option) 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, see <http://www.gnu.org/licenses/>.  */
17
18 #include <config.h>
19
20 #include "hash.h"
21 #include "hash-pjw.h"
22 #include "inttostr.h"
23 #include "xalloc.h"
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <stdbool.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #include "macros.h"
32
33 #define STREQ(a, b) (strcmp (a, b) == 0)
34 #define ARRAY_CARDINALITY(Array) (sizeof (Array) / sizeof *(Array))
35
36 static bool
37 hash_compare_strings (void const *x, void const *y)
38 {
39   ASSERT (x != y);
40   return STREQ (x, y) ? true : false;
41 }
42
43 static void
44 hash_freer (void *x)
45 {
46   free (x);
47 }
48
49 static void
50 insert_new (Hash_table *ht, const void *ent)
51 {
52   void *e = hash_insert (ht, ent);
53   ASSERT (e == ent);
54 }
55
56 static bool
57 walk (void *ent, void *data)
58 {
59   char *str = ent;
60   unsigned int *map = data;
61   switch (*str)
62     {
63     case 'a': *map |= 1; return true;
64     case 'b': *map |= 2; return true;
65     case 'c': *map |= 4; return true;
66     }
67   *map |= 8;
68   return false;
69 }
70
71 static int
72 get_seed (char const *str, unsigned int *seed)
73 {
74   size_t len = strlen (str);
75   if (len == 0 || strspn (str, "0123456789") != len || 10 < len)
76     return 1;
77
78   *seed = atoi (str);
79   return 0;
80 }
81
82 int
83 main (int argc, char **argv)
84 {
85   unsigned int i;
86   unsigned int k;
87   unsigned int table_size[] = {1, 2, 3, 4, 5, 23, 53};
88   Hash_table *ht;
89   Hash_tuning tuning;
90
91   hash_reset_tuning (&tuning);
92   tuning.shrink_threshold = 0.3;
93   tuning.shrink_factor = 0.707;
94   tuning.growth_threshold = 1.5;
95   tuning.growth_factor = 2.0;
96   tuning.is_n_buckets = true;
97
98   if (1 < argc)
99     {
100       unsigned int seed;
101       if (get_seed (argv[1], &seed) != 0)
102         {
103           fprintf (stderr, "invalid seed: %s\n", argv[1]);
104           exit (EXIT_FAILURE);
105         }
106
107       srand (seed);
108     }
109
110   for (i = 0; i < ARRAY_CARDINALITY (table_size); i++)
111     {
112       size_t sz = table_size[i];
113       ht = hash_initialize (sz, NULL, hash_pjw, hash_compare_strings, NULL);
114       ASSERT (ht);
115       insert_new (ht, "a");
116       {
117         char *str1 = xstrdup ("a");
118         char *str2 = hash_insert (ht, str1);
119         ASSERT (str1 != str2);
120         ASSERT (STREQ (str1, str2));
121         free (str1);
122       }
123       insert_new (ht, "b");
124       insert_new (ht, "c");
125       i = 0;
126       ASSERT (hash_do_for_each (ht, walk, &i) == 3);
127       ASSERT (i == 7);
128       {
129         void *buf[5] = { NULL };
130         ASSERT (hash_get_entries (ht, NULL, 0) == 0);
131         ASSERT (hash_get_entries (ht, buf, 5) == 3);
132         ASSERT (STREQ (buf[0], "a") || STREQ (buf[0], "b") || STREQ (buf[0], "c"));
133       }
134       ASSERT (hash_delete (ht, "a"));
135       ASSERT (hash_delete (ht, "a") == NULL);
136       ASSERT (hash_delete (ht, "b"));
137       ASSERT (hash_delete (ht, "c"));
138
139       ASSERT (hash_rehash (ht, 47));
140       ASSERT (hash_rehash (ht, 467));
141
142       /* Free an empty table. */
143       hash_clear (ht);
144       hash_free (ht);
145
146       ht = hash_initialize (sz, NULL, hash_pjw, hash_compare_strings, NULL);
147       ASSERT (ht);
148
149       insert_new (ht, "z");
150       insert_new (ht, "y");
151       insert_new (ht, "x");
152       insert_new (ht, "w");
153       insert_new (ht, "v");
154       insert_new (ht, "u");
155
156       hash_clear (ht);
157       ASSERT (hash_get_n_entries (ht) == 0);
158       hash_free (ht);
159
160       /* Test pointer hashing.  */
161       ht = hash_initialize (sz, NULL, NULL, NULL, NULL);
162       ASSERT (ht);
163       {
164         char *str = xstrdup ("a");
165         insert_new (ht, "a");
166         insert_new (ht, str);
167         ASSERT (hash_lookup (ht, str) == str);
168         free (str);
169       }
170       hash_free (ht);
171     }
172
173   hash_reset_tuning (&tuning);
174   tuning.shrink_threshold = 0.3;
175   tuning.shrink_factor = 0.707;
176   tuning.growth_threshold = 1.5;
177   tuning.growth_factor = 2.0;
178   tuning.is_n_buckets = true;
179   /* Invalid tuning.  */
180   ht = hash_initialize (4651, &tuning, hash_pjw, hash_compare_strings,
181                         hash_freer);
182   ASSERT (!ht);
183
184   /* Alternate tuning.  */
185   tuning.growth_threshold = 0.89;
186
187   /* Run with default tuning, then with custom tuning settings.  */
188   for (k = 0; k < 2; k++)
189     {
190       Hash_tuning const *tune = (k == 0 ? NULL : &tuning);
191       /* Now, each entry is malloc'd.  */
192       ht = hash_initialize (4651, tune, hash_pjw,
193                             hash_compare_strings, hash_freer);
194       ASSERT (ht);
195       for (i = 0; i < 10000; i++)
196         {
197           unsigned int op = rand () % 10;
198           switch (op)
199             {
200             case 0:
201             case 1:
202             case 2:
203             case 3:
204             case 4:
205             case 5:
206               {
207                 char buf[50];
208                 char const *p = uinttostr (i, buf);
209                 insert_new (ht, xstrdup (p));
210               }
211               break;
212
213             case 6:
214               {
215                 size_t n = hash_get_n_entries (ht);
216                 ASSERT (hash_rehash (ht, n + rand () % 20));
217               }
218               break;
219
220             case 7:
221               {
222                 size_t n = hash_get_n_entries (ht);
223                 size_t delta = rand () % 20;
224                 if (delta < n)
225                   ASSERT (hash_rehash (ht, n - delta));
226               }
227               break;
228
229             case 8:
230             case 9:
231               {
232                 /* Delete a random entry.  */
233                 size_t n = hash_get_n_entries (ht);
234                 if (n)
235                   {
236                     size_t kk = rand () % n;
237                     void const *p;
238                     void *v;
239                     for (p = hash_get_first (ht); kk;
240                          --kk, p = hash_get_next (ht, p))
241                       {
242                         /* empty */
243                       }
244                     ASSERT (p);
245                     v = hash_delete (ht, p);
246                     ASSERT (v);
247                     free (v);
248                   }
249                 break;
250               }
251             }
252           ASSERT (hash_table_ok (ht));
253         }
254
255       hash_free (ht);
256     }
257
258   return 0;
259 }