mirror of
https://https.git.savannah.gnu.org/git/gnulib.git
synced 2026-06-15 15:25:49 +00:00
7e6f301b8e
* lib/bitset.c (bitset_print): * lib/bitset/list.c (LBITSET_ELT_BITS, debug_lbitset): * lib/bitset/table.c (TBITSET_ELT_BITS): * lib/bitsetv.c (bitsetv_dump, debug_bitsetv) (bitsetv_matrix_dump): * lib/gettext.h (gettext, ngettext, textdomain, bindtextdomain) (bind_textdomain_codeset): * lib/gl_anyavltree_list2.h (gl_tree_nx_add_first) (gl_tree_nx_add_last, gl_tree_nx_add_before) (gl_tree_nx_add_after): * lib/gl_anylinked_list2.h (gl_linked_nx_create) (gl_linked_node_nx_set_value, gl_linked_nx_set_at) (gl_linked_search_from_to, gl_linked_indexof_from_to) (gl_linked_nx_add_first, gl_linked_nx_add_last) (gl_linked_nx_add_before, gl_linked_nx_add_after) (gl_linked_nx_add_at): * lib/gl_anyrbtree_list2.h (gl_tree_nx_add_first) (gl_tree_nx_add_last, gl_tree_nx_add_before) (gl_tree_nx_add_after): * lib/gl_anytree_list2.h (gl_tree_node_nx_set_value) (gl_tree_nx_set_at): * lib/gl_anytreehash_list1.h (add_nodes_to_buckets): * lib/gl_anytreehash_list2.h (gl_tree_search_from_to): * lib/gl_array_list.c, lib/gl_carray_list.c, lib/gl_sublist.c: (INDEX_TO_NODE): * lib/gl_hash_map.c (gl_hash_search, gl_hash_nx_getput) (gl_hash_getremove): * lib/gl_hash_set.c (gl_hash_search, gl_hash_nx_add) (gl_hash_remove): * lib/gl_linkedhash_map.c (gl_linkedhash_search) (gl_linkedhash_nx_getput, gl_linkedhash_getremove): * lib/gl_linkedhash_set.c (gl_linkedhash_search) (gl_linkedhash_nx_add, gl_linkedhash_remove): * lib/isnand-nolibm.h (isnand): * lib/isnanf-nolibm.h (isnanf): * lib/isnanl-nolibm.h (isnanl): * lib/math.in.h (isnand, _gl_isnand, isnan): * lib/md4.c, lib/sha1.c: (rol): * lib/pagealign_alloc.c (pagealign_alloc, pagealign_free): * lib/ssfmalloc.h (allocate_block_from_pool): * lib/u64.h (u64hilo, u64lo, u64getlo) [INT_MAX < UINT64_MAX]: Use compound literal when it is safer than a cast and it pacifies -Wuseless-cast. * lib/fsusage.c (PROPAGATE_ALL_ONES): * lib/glthread/thread.h (glthread_atfork, glthread_sigmask): Parenthesize more. * lib/gettext.h (dgettext, dcgettext, dngettext, dcngettext) (bindtextdomain, bind_textdomain_codeset): Check types of unused args.
323 lines
9.0 KiB
C
323 lines
9.0 KiB
C
/* Map data type implemented by a hash table with a linked list.
|
|
Copyright (C) 2006, 2008-2026 Free Software Foundation, Inc.
|
|
Written by Bruno Haible <bruno@clisp.org>, 2018.
|
|
|
|
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/>. */
|
|
|
|
#include <config.h>
|
|
|
|
/* Specification. */
|
|
#include "gl_linkedhash_map.h"
|
|
|
|
#include <stdint.h> /* for uintptr_t, SIZE_MAX */
|
|
#include <stdlib.h>
|
|
|
|
#include "xsize.h"
|
|
|
|
/* --------------------------- gl_map_t Data Type --------------------------- */
|
|
|
|
#include "gl_anyhash1.h"
|
|
|
|
/* Concrete list node implementation, valid for this file only. */
|
|
struct gl_list_node_impl
|
|
{
|
|
struct gl_hash_entry h; /* hash table entry fields; must be first */
|
|
struct gl_list_node_impl *next;
|
|
struct gl_list_node_impl *prev;
|
|
const void *key;
|
|
const void *value;
|
|
};
|
|
typedef struct gl_list_node_impl * gl_list_node_t;
|
|
|
|
/* Concrete gl_map_impl type, valid for this file only. */
|
|
struct gl_map_impl
|
|
{
|
|
struct gl_map_impl_base base;
|
|
gl_mapkey_hashcode_fn hashcode_fn;
|
|
/* A hash table: managed as an array of collision lists. */
|
|
size_t table_size;
|
|
struct gl_hash_entry **table
|
|
_GL_ATTRIBUTE_COUNTED_BY (table_size);
|
|
/* A circular list anchored at root.
|
|
The first node is = root.next, the last node is = root.prev.
|
|
The root's value is unused. */
|
|
struct gl_list_node_impl root;
|
|
/* Number of list nodes, excluding the root. */
|
|
size_t count;
|
|
};
|
|
|
|
#define CONTAINER_T gl_map_t
|
|
#define CONTAINER_COUNT(map) (map)->count
|
|
#include "gl_anyhash2.h"
|
|
|
|
/* If the symbol SIGNAL_SAFE_MAP is defined, the code is compiled in such
|
|
a way that a gl_map_t data structure may be used from within a signal
|
|
handler. The operations allowed in the signal handler are:
|
|
gl_map_iterator, gl_map_iterator_next, gl_map_iterator_free.
|
|
The map and node fields that are therefore accessed from the signal handler
|
|
are:
|
|
map->root, node->next, node->value.
|
|
We are careful to make modifications to these fields only in an order
|
|
that maintains the consistency of the list data structure at any moment,
|
|
and we use 'volatile' assignments to prevent the compiler from reordering
|
|
such assignments. */
|
|
#ifdef SIGNAL_SAFE_MAP
|
|
# define ASYNCSAFE(type) *(type volatile *)&
|
|
#else
|
|
# define ASYNCSAFE(type)
|
|
#endif
|
|
|
|
/* --------------------------- gl_map_t Data Type --------------------------- */
|
|
|
|
static gl_map_t
|
|
gl_linkedhash_nx_create_empty (gl_map_implementation_t implementation,
|
|
gl_mapkey_equals_fn equals_fn,
|
|
gl_mapkey_hashcode_fn hashcode_fn,
|
|
gl_mapkey_dispose_fn kdispose_fn,
|
|
gl_mapvalue_dispose_fn vdispose_fn)
|
|
{
|
|
struct gl_map_impl *map =
|
|
(struct gl_map_impl *) malloc (sizeof (struct gl_map_impl));
|
|
|
|
if (map == NULL)
|
|
return NULL;
|
|
|
|
map->base.vtable = implementation;
|
|
map->base.equals_fn = equals_fn;
|
|
map->base.kdispose_fn = kdispose_fn;
|
|
map->base.vdispose_fn = vdispose_fn;
|
|
map->hashcode_fn = hashcode_fn;
|
|
map->table_size = 11;
|
|
map->table =
|
|
(gl_hash_entry_t *) calloc (map->table_size, sizeof (gl_hash_entry_t));
|
|
if (map->table == NULL)
|
|
goto fail;
|
|
map->root.next = &map->root;
|
|
map->root.prev = &map->root;
|
|
map->count = 0;
|
|
|
|
return map;
|
|
|
|
fail:
|
|
free (map);
|
|
return NULL;
|
|
}
|
|
|
|
static size_t _GL_ATTRIBUTE_PURE
|
|
gl_linkedhash_size (gl_map_t map)
|
|
{
|
|
return map->count;
|
|
}
|
|
|
|
static bool _GL_ATTRIBUTE_PURE
|
|
gl_linkedhash_search (gl_map_t map, const void *key, const void **valuep)
|
|
{
|
|
size_t hashcode =
|
|
(map->hashcode_fn != NULL
|
|
? map->hashcode_fn (key)
|
|
: (size_t) {(uintptr_t) key});
|
|
size_t bucket = hashcode % map->table_size;
|
|
gl_mapkey_equals_fn equals = map->base.equals_fn;
|
|
|
|
/* Look for a match in the hash bucket. */
|
|
for (gl_list_node_t node = (gl_list_node_t) map->table[bucket];
|
|
node != NULL;
|
|
node = (gl_list_node_t) node->h.hash_next)
|
|
if (node->h.hashcode == hashcode
|
|
&& (equals != NULL
|
|
? equals (key, node->key)
|
|
: key == node->key))
|
|
{
|
|
*valuep = node->value;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static int
|
|
gl_linkedhash_nx_getput (gl_map_t map, const void *key, const void *value,
|
|
const void **oldvaluep)
|
|
{
|
|
size_t hashcode =
|
|
(map->hashcode_fn != NULL
|
|
? map->hashcode_fn (key)
|
|
: (size_t) {(uintptr_t) key});
|
|
size_t bucket = hashcode % map->table_size;
|
|
gl_mapkey_equals_fn equals = map->base.equals_fn;
|
|
|
|
/* Look for a match in the hash bucket. */
|
|
for (gl_list_node_t node = (gl_list_node_t) map->table[bucket];
|
|
node != NULL;
|
|
node = (gl_list_node_t) node->h.hash_next)
|
|
if (node->h.hashcode == hashcode
|
|
&& (equals != NULL
|
|
? equals (key, node->key)
|
|
: key == node->key))
|
|
{
|
|
*oldvaluep = node->value;
|
|
node->value = value;
|
|
return 0;
|
|
}
|
|
|
|
/* Allocate a new node. */
|
|
gl_list_node_t node =
|
|
(struct gl_list_node_impl *) malloc (sizeof (struct gl_list_node_impl));
|
|
|
|
if (node == NULL)
|
|
return -1;
|
|
|
|
ASYNCSAFE(const void *) node->key = key;
|
|
ASYNCSAFE(const void *) node->value = value;
|
|
node->h.hashcode = hashcode;
|
|
|
|
/* Add node to the hash table. */
|
|
node->h.hash_next = map->table[bucket];
|
|
map->table[bucket] = &node->h;
|
|
|
|
/* Add node to the map. */
|
|
ASYNCSAFE(gl_list_node_t) node->next = &map->root;
|
|
node->prev = map->root.prev;
|
|
ASYNCSAFE(gl_list_node_t) node->prev->next = node;
|
|
map->root.prev = node;
|
|
map->count++;
|
|
|
|
hash_resize_after_add (map);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static bool
|
|
gl_linkedhash_getremove (gl_map_t map, const void *key, const void **oldvaluep)
|
|
{
|
|
size_t hashcode =
|
|
(map->hashcode_fn != NULL
|
|
? map->hashcode_fn (key)
|
|
: (size_t) {(uintptr_t) key});
|
|
size_t bucket = hashcode % map->table_size;
|
|
gl_mapkey_equals_fn equals = map->base.equals_fn;
|
|
|
|
/* Look for the first match in the hash bucket. */
|
|
for (gl_list_node_t *nodep = (gl_list_node_t *) &map->table[bucket];
|
|
*nodep != NULL;
|
|
nodep = (gl_list_node_t *) &(*nodep)->h.hash_next)
|
|
{
|
|
gl_list_node_t node = *nodep;
|
|
if (node->h.hashcode == hashcode
|
|
&& (equals != NULL
|
|
? equals (key, node->key)
|
|
: key == node->key))
|
|
{
|
|
*oldvaluep = node->value;
|
|
|
|
/* Remove node from the hash table. */
|
|
*nodep = (gl_list_node_t) node->h.hash_next;
|
|
|
|
/* Remove node from the list. */
|
|
{
|
|
gl_list_node_t prev = node->prev;
|
|
gl_list_node_t next = node->next;
|
|
|
|
ASYNCSAFE(gl_list_node_t) prev->next = next;
|
|
next->prev = prev;
|
|
}
|
|
map->count--;
|
|
|
|
if (map->base.kdispose_fn != NULL)
|
|
map->base.kdispose_fn (node->key);
|
|
free (node);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
gl_linkedhash_free (gl_map_t map)
|
|
{
|
|
gl_mapkey_dispose_fn kdispose = map->base.kdispose_fn;
|
|
gl_mapvalue_dispose_fn vdispose = map->base.vdispose_fn;
|
|
|
|
for (gl_list_node_t node = map->root.next; node != &map->root; )
|
|
{
|
|
gl_list_node_t next = node->next;
|
|
if (vdispose != NULL)
|
|
vdispose (node->value);
|
|
if (kdispose != NULL)
|
|
kdispose (node->key);
|
|
free (node);
|
|
node = next;
|
|
}
|
|
free (map->table);
|
|
free (map);
|
|
}
|
|
|
|
/* ---------------------- gl_map_iterator_t Data Type ---------------------- */
|
|
|
|
/* Iterate through the list, not through the hash buckets, so that the order
|
|
in which the pairs are returned is predictable. */
|
|
|
|
static gl_map_iterator_t
|
|
gl_linkedhash_iterator (gl_map_t map)
|
|
{
|
|
gl_map_iterator_t result;
|
|
|
|
result.vtable = map->base.vtable;
|
|
result.map = map;
|
|
result.p = map->root.next;
|
|
result.q = &map->root;
|
|
#if defined GCC_LINT || defined lint
|
|
result.i = 0;
|
|
result.j = 0;
|
|
result.count = 0;
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
static bool
|
|
gl_linkedhash_iterator_next (gl_map_iterator_t *iterator,
|
|
const void **keyp, const void **valuep)
|
|
{
|
|
if (iterator->p != iterator->q)
|
|
{
|
|
gl_list_node_t node = (gl_list_node_t) iterator->p;
|
|
*keyp = node->key;
|
|
*valuep = node->value;
|
|
iterator->p = node->next;
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
gl_linkedhash_iterator_free (gl_map_iterator_t *iterator)
|
|
{
|
|
}
|
|
|
|
|
|
const struct gl_map_implementation gl_linkedhash_map_implementation =
|
|
{
|
|
gl_linkedhash_nx_create_empty,
|
|
gl_linkedhash_size,
|
|
gl_linkedhash_search,
|
|
gl_linkedhash_nx_getput,
|
|
gl_linkedhash_getremove,
|
|
gl_linkedhash_free,
|
|
gl_linkedhash_iterator,
|
|
gl_linkedhash_iterator_next,
|
|
gl_linkedhash_iterator_free
|
|
};
|