root/maint/gnulib/lib/di-set.c

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

DEFINITIONS

This source file includes following definitions.
  1. di_ent_hash
  2. di_ent_compare
  3. di_ent_free
  4. di_set_alloc
  5. di_set_free
  6. di_ino_hash
  7. map_device
  8. map_inode_number
  9. di_set_insert
  10. di_set_lookup

   1 /* Set operations for device-inode pairs stored in a space-efficient manner.
   2 
   3    Copyright 2009-2021 Free Software Foundation, Inc.
   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 /* written by Paul Eggert and Jim Meyering */
  19 
  20 #include <config.h>
  21 #include "di-set.h"
  22 
  23 #include "hash.h"
  24 #include "ino-map.h"
  25 
  26 #include <limits.h>
  27 #include <stdlib.h>
  28 
  29 /* The hash package hashes "void *", but this package wants to hash
  30    integers.  Use integers that are as large as possible, but no
  31    larger than void *, so that they can be cast to void * and back
  32    without losing information.  */
  33 typedef size_t hashint;
  34 #define HASHINT_MAX ((hashint) -1)
  35 
  36 /* Integers represent inode numbers.  Integers in the range
  37    1..(LARGE_INO_MIN-1) represent inode numbers directly.  (The hash
  38    package does not work with null pointers, so inode 0 cannot be used
  39    as a key.)  To find the representations of other inode numbers, map
  40    them through INO_MAP.  */
  41 #define LARGE_INO_MIN (HASHINT_MAX / 2)
  42 
  43 /* Set operations for device-inode pairs stored in a space-efficient
  44    manner.  Use a two-level hash table.  The top level hashes by
  45    device number, as there are typically a small number of devices.
  46    The lower level hashes by mapped inode numbers.  In the typical
  47    case where the inode number is positive and small, the inode number
  48    maps to itself, masquerading as a void * value; otherwise, its
  49    value is the result of hashing the inode value through INO_MAP.  */
  50 
  51 /* A pair that maps a device number to a set of inode numbers.  */
  52 struct di_ent
  53 {
  54   dev_t dev;
  55   struct hash_table *ino_set;
  56 };
  57 
  58 /* A two-level hash table that manages and indexes these pairs.  */
  59 struct di_set
  60 {
  61   /* Map device numbers to sets of inode number representatives.  */
  62   struct hash_table *dev_map;
  63 
  64   /* If nonnull, map large inode numbers to their small
  65      representatives.  If null, there are no large inode numbers in
  66      this set.  */
  67   struct ino_map *ino_map;
  68 
  69   /* Cache of the most recently allocated and otherwise-unused storage
  70      for probing this table.  */
  71   struct di_ent *probe;
  72 };
  73 
  74 /* Hash a device-inode-set entry.  */
  75 static size_t
  76 di_ent_hash (void const *x, size_t table_size)
     /* [previous][next][first][last][top][bottom][index][help] */
  77 {
  78   struct di_ent const *p = x;
  79   dev_t dev = p->dev;
  80 
  81   /* When DEV is wider than size_t, exclusive-OR the words of DEV into H.
  82      This avoids loss of info, without applying % to the wider type,
  83      which could be quite slow on some systems.  */
  84   size_t h = dev;
  85   unsigned int i;
  86   unsigned int n_words = sizeof dev / sizeof h + (sizeof dev % sizeof h != 0);
  87   for (i = 1; i < n_words; i++)
  88     h ^= dev >> CHAR_BIT * sizeof h * i;
  89 
  90   return h % table_size;
  91 }
  92 
  93 /* Return true if two device-inode-set entries are the same.  */
  94 static bool
  95 di_ent_compare (void const *x, void const *y)
     /* [previous][next][first][last][top][bottom][index][help] */
  96 {
  97   struct di_ent const *a = x;
  98   struct di_ent const *b = y;
  99   return a->dev == b->dev;
 100 }
 101 
 102 /* Free a device-inode-set entry.  */
 103 static void
 104 di_ent_free (void *v)
     /* [previous][next][first][last][top][bottom][index][help] */
 105 {
 106   struct di_ent *a = v;
 107   hash_free (a->ino_set);
 108   free (a);
 109 }
 110 
 111 /* Create a set of device-inode pairs.  Return NULL on allocation failure.  */
 112 struct di_set *
 113 di_set_alloc (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 114 {
 115   struct di_set *dis = malloc (sizeof *dis);
 116   if (dis)
 117     {
 118       enum { INITIAL_DEV_MAP_SIZE = 11 };
 119       dis->dev_map = hash_initialize (INITIAL_DEV_MAP_SIZE, NULL,
 120                                       di_ent_hash, di_ent_compare,
 121                                       di_ent_free);
 122       if (! dis->dev_map)
 123         {
 124           free (dis);
 125           return NULL;
 126         }
 127       dis->ino_map = NULL;
 128       dis->probe = NULL;
 129     }
 130 
 131   return dis;
 132 }
 133 
 134 /* Free a set of device-inode pairs.  */
 135 void
 136 di_set_free (struct di_set *dis)
     /* [previous][next][first][last][top][bottom][index][help] */
 137 {
 138   hash_free (dis->dev_map);
 139   if (dis->ino_map)
 140     ino_map_free (dis->ino_map);
 141   free (dis->probe);
 142   free (dis);
 143 }
 144 
 145 /* Hash an encoded inode number I.  */
 146 static size_t
 147 di_ino_hash (void const *i, size_t table_size)
     /* [previous][next][first][last][top][bottom][index][help] */
 148 {
 149   return (hashint) i % table_size;
 150 }
 151 
 152 /* Using the DIS table, map a device to a hash table that represents
 153    a set of inode numbers.  Return NULL on error.  */
 154 static struct hash_table *
 155 map_device (struct di_set *dis, dev_t dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 156 {
 157   /* Find space for the probe, reusing the cache if available.  */
 158   struct di_ent *ent;
 159   struct di_ent *probe = dis->probe;
 160   if (probe)
 161     {
 162       /* If repeating a recent query, return the cached result.   */
 163       if (probe->dev == dev)
 164         return probe->ino_set;
 165     }
 166   else
 167     {
 168       dis->probe = probe = malloc (sizeof *probe);
 169       if (! probe)
 170         return NULL;
 171     }
 172 
 173   /* Probe for the device.  */
 174   probe->dev = dev;
 175   ent = hash_insert (dis->dev_map, probe);
 176   if (! ent)
 177     return NULL;
 178 
 179   if (ent != probe)
 180     {
 181       /* Use the existing entry.  */
 182       probe->ino_set = ent->ino_set;
 183     }
 184   else
 185     {
 186       enum { INITIAL_INO_SET_SIZE = 1021 };
 187 
 188       /* Prepare to allocate a new probe next time; this one is in use.  */
 189       dis->probe = NULL;
 190 
 191       /* DEV is new; allocate an inode set for it.  */
 192       probe->ino_set = hash_initialize (INITIAL_INO_SET_SIZE, NULL,
 193                                         di_ino_hash, NULL, NULL);
 194     }
 195 
 196   return probe->ino_set;
 197 }
 198 
 199 /* Using the DIS table, map an inode number to a mapped value.
 200    Return INO_MAP_INSERT_FAILURE on error.  */
 201 static hashint
 202 map_inode_number (struct di_set *dis, ino_t ino)
     /* [previous][next][first][last][top][bottom][index][help] */
 203 {
 204   if (0 < ino && ino < LARGE_INO_MIN)
 205     return ino;
 206 
 207   if (! dis->ino_map)
 208     {
 209       dis->ino_map = ino_map_alloc (LARGE_INO_MIN);
 210       if (! dis->ino_map)
 211         return INO_MAP_INSERT_FAILURE;
 212     }
 213 
 214   return ino_map_insert (dis->ino_map, ino);
 215 }
 216 
 217 /* Attempt to insert the DEV,INO pair into the set DIS.
 218    If it matches a pair already in DIS, keep that pair and return 0.
 219    Otherwise, if insertion is successful, return 1.
 220    Upon any failure return -1.  */
 221 int
 222 di_set_insert (struct di_set *dis, dev_t dev, ino_t ino)
     /* [previous][next][first][last][top][bottom][index][help] */
 223 {
 224   hashint i;
 225 
 226   /* Map the device number to a set of inodes.  */
 227   struct hash_table *ino_set = map_device (dis, dev);
 228   if (! ino_set)
 229     return -1;
 230 
 231   /* Map the inode number to a small representative I.  */
 232   i = map_inode_number (dis, ino);
 233   if (i == INO_MAP_INSERT_FAILURE)
 234     return -1;
 235 
 236   /* Put I into the inode set.  */
 237   return hash_insert_if_absent (ino_set, (void const *) i, NULL);
 238 }
 239 
 240 /* Look up the DEV,INO pair in the set DIS.
 241    If found, return 1; if not found, return 0.
 242    Upon any failure return -1.  */
 243 int
 244 di_set_lookup (struct di_set *dis, dev_t dev, ino_t ino)
     /* [previous][next][first][last][top][bottom][index][help] */
 245 {
 246   hashint i;
 247 
 248   /* Map the device number to a set of inodes.  */
 249   struct hash_table *ino_set = map_device (dis, dev);
 250   if (! ino_set)
 251     return -1;
 252 
 253   /* Map the inode number to a small representative I.  */
 254   i = map_inode_number (dis, ino);
 255   if (i == INO_MAP_INSERT_FAILURE)
 256     return -1;
 257 
 258   /* Perform the look-up.  */
 259   return !!hash_lookup (ino_set, (void const *) i);
 260 }

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