root/maint/gnulib/tests/test-hash.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. hash_compare_strings
  2. hash_freer
  3. insert_new
  4. walk
  5. get_seed
  6. main

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

/* [previous][next][first][last][top][bottom][index][help] */