(root)/
m4-1.4.19/
tests/
test-hash.c
       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)
      37  {
      38    ASSERT (x != y);
      39    return STREQ (x, y) ? true : false;
      40  }
      41  
      42  static void
      43  hash_freer (void *x)
      44  {
      45    free (x);
      46  }
      47  
      48  static void
      49  insert_new (Hash_table *ht, const void *ent)
      50  {
      51    void *e = hash_insert (ht, ent);
      52    ASSERT (e == ent);
      53  }
      54  
      55  static bool
      56  walk (void *ent, void *data)
      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)
      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)
      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  }