| /* Map an ino_t inode number to a small integer. | |
| Copyright 2009-2026 Free Software Foundation, Inc. | |
| This program is free software: you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation, either version 3 of the License, or | |
| (at your option) any later version. | |
| This program is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with this program. If not, see <https://www.gnu.org/licenses/>. */ | |
| /* written by Paul Eggert and Jim Meyering */ | |
| #include <config.h> | |
| #include "ino-map.h" | |
| #include "hash.h" | |
| #include <limits.h> | |
| #include <stdlib.h> | |
| /* A pair that maps an inode number to a mapped inode number; the | |
| latter is a small unique ID for the former. */ | |
| struct ino_map_ent | |
| { | |
| ino_t ino; | |
| size_t mapped_ino; | |
| }; | |
| /* A table that manages and indexes these pairs. */ | |
| struct ino_map | |
| { | |
| /* A table of KEY,VAL pairs, where KEY is the raw ino_t value and | |
| VAL is the small number that it maps to. */ | |
| struct hash_table *map; | |
| /* The next mapped inode number to hand out. */ | |
| size_t next_mapped_ino; | |
| /* Cache of the most recently allocated and otherwise-unused storage | |
| for probing the table. */ | |
| struct ino_map_ent *probe; | |
| }; | |
| /* Hash an inode map entry. */ | |
| static size_t | |
| ino_hash (void const *x, size_t table_size) | |
| { | |
| struct ino_map_ent const *p = x; | |
| ino_t ino = p->ino; | |
| /* When INO is wider than size_t, exclusive-OR the words of INO into H. | |
| This avoids loss of info, without applying % to the wider type, | |
| which could be quite slow on some systems. */ | |
| size_t h = ino; | |
| unsigned int n_words = sizeof ino / sizeof h + (sizeof ino % sizeof h != 0); | |
| for (unsigned int i = 1; i < n_words; i++) | |
| h ^= ino >> CHAR_BIT * sizeof h * i; | |
| return h % table_size; | |
| } | |
| /* Return true if two inode map entries are the same. */ | |
| static bool | |
| ino_compare (void const *x, void const *y) | |
| { | |
| struct ino_map_ent const *a = x; | |
| struct ino_map_ent const *b = y; | |
| return a->ino == b->ino; | |
| } | |
| /* Allocate an inode map that will hand out integers starting with | |
| NEXT_MAPPED_INO. Return NULL if memory is exhausted. */ | |
| struct ino_map * | |
| ino_map_alloc (size_t next_mapped_ino) | |
| { | |
| struct ino_map *im = malloc (sizeof *im); | |
| if (im) | |
| { | |
| enum { INITIAL_INO_MAP_TABLE_SIZE = 1021 }; | |
| im->map = hash_initialize (INITIAL_INO_MAP_TABLE_SIZE, NULL, | |
| ino_hash, ino_compare, free); | |
| if (! im->map) | |
| { | |
| free (im); | |
| return NULL; | |
| } | |
| im->next_mapped_ino = next_mapped_ino; | |
| im->probe = NULL; | |
| } | |
| return im; | |
| } | |
| /* Free an inode map. */ | |
| void | |
| ino_map_free (struct ino_map *map) | |
| { | |
| hash_free (map->map); | |
| free (map->probe); | |
| free (map); | |
| } | |
| /* Insert into MAP the inode number INO if it's not there already, | |
| and return its nonnegative mapped inode number. | |
| If INO is already in MAP, return the existing mapped inode number. | |
| Return INO_MAP_INSERT_FAILURE on memory or counter exhaustion. */ | |
| size_t | |
| ino_map_insert (struct ino_map *im, ino_t ino) | |
| { | |
| /* Find space for the probe, reusing the cache if available. */ | |
| struct ino_map_ent *probe = im->probe; | |
| if (probe) | |
| { | |
| /* If repeating a recent query, return the cached result. */ | |
| if (probe->ino == ino) | |
| return probe->mapped_ino; | |
| } | |
| else | |
| { | |
| im->probe = probe = malloc (sizeof *probe); | |
| if (! probe) | |
| return INO_MAP_INSERT_FAILURE; | |
| } | |
| probe->ino = ino; | |
| struct ino_map_ent *ent = hash_insert (im->map, probe); | |
| if (! ent) | |
| return INO_MAP_INSERT_FAILURE; | |
| if (ent != probe) | |
| { | |
| /* Use the existing entry. */ | |
| probe->mapped_ino = ent->mapped_ino; | |
| } | |
| else | |
| { | |
| /* If adding 1 to map->next_mapped_ino would cause it to | |
| overflow to zero, then it must equal INO_MAP_INSERT_FAILURE, | |
| which is the value that should be returned in that case. | |
| Verify that this works. */ | |
| static_assert (INO_MAP_INSERT_FAILURE + 1 == 0); | |
| /* Prepare to allocate a new probe next time; this one is in use. */ | |
| im->probe = NULL; | |
| /* INO is new; allocate a mapped inode number for it. */ | |
| probe->mapped_ino = im->next_mapped_ino++; | |
| } | |
| return probe->mapped_ino; | |
| } |