1
0
Fork 0
man-db/gl/lib/gl_hash_set.c
Daniel Baumann 1fa764a8d3
Adding upstream version 2.13.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-21 08:13:55 +02:00

313 lines
7.9 KiB
C

/* Set data type implemented by a hash table.
Copyright (C) 2006, 2008-2024 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_hash_set.h"
#include <stdint.h> /* for uintptr_t, SIZE_MAX */
#include <stdlib.h>
#include "xsize.h"
/* --------------------------- gl_set_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 */
const void *value;
};
typedef struct gl_list_node_impl * gl_list_node_t;
/* Concrete gl_set_impl type, valid for this file only. */
struct gl_set_impl
{
struct gl_set_impl_base base;
gl_setelement_hashcode_fn hashcode_fn;
/* A hash table: managed as an array of collision lists. */
struct gl_hash_entry **table;
size_t table_size;
/* Number of hash table entries. */
size_t count;
};
#define CONTAINER_T gl_set_t
#define CONTAINER_COUNT(set) (set)->count
#include "gl_anyhash2.h"
/* --------------------------- gl_set_t Data Type --------------------------- */
static gl_set_t
gl_hash_nx_create_empty (gl_set_implementation_t implementation,
gl_setelement_equals_fn equals_fn,
gl_setelement_hashcode_fn hashcode_fn,
gl_setelement_dispose_fn dispose_fn)
{
struct gl_set_impl *set =
(struct gl_set_impl *) malloc (sizeof (struct gl_set_impl));
if (set == NULL)
return NULL;
set->base.vtable = implementation;
set->base.equals_fn = equals_fn;
set->base.dispose_fn = dispose_fn;
set->hashcode_fn = hashcode_fn;
set->table_size = 11;
set->table =
(gl_hash_entry_t *) calloc (set->table_size, sizeof (gl_hash_entry_t));
if (set->table == NULL)
goto fail;
set->count = 0;
return set;
fail:
free (set);
return NULL;
}
static size_t _GL_ATTRIBUTE_PURE
gl_hash_size (gl_set_t set)
{
return set->count;
}
static bool _GL_ATTRIBUTE_PURE
gl_hash_search (gl_set_t set, const void *elt)
{
size_t hashcode =
(set->hashcode_fn != NULL
? set->hashcode_fn (elt)
: (size_t)(uintptr_t) elt);
size_t bucket = hashcode % set->table_size;
gl_setelement_equals_fn equals = set->base.equals_fn;
/* Look for a match in the hash bucket. */
gl_list_node_t node;
for (node = (gl_list_node_t) set->table[bucket];
node != NULL;
node = (gl_list_node_t) node->h.hash_next)
if (node->h.hashcode == hashcode
&& (equals != NULL
? equals (elt, node->value)
: elt == node->value))
return true;
return false;
}
static int
gl_hash_nx_add (gl_set_t set, const void *elt)
{
size_t hashcode =
(set->hashcode_fn != NULL
? set->hashcode_fn (elt)
: (size_t)(uintptr_t) elt);
size_t bucket = hashcode % set->table_size;
gl_setelement_equals_fn equals = set->base.equals_fn;
/* Look for a match in the hash bucket. */
{
gl_list_node_t node;
for (node = (gl_list_node_t) set->table[bucket];
node != NULL;
node = (gl_list_node_t) node->h.hash_next)
if (node->h.hashcode == hashcode
&& (equals != NULL
? equals (elt, node->value)
: elt == node->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;
node->value = elt;
node->h.hashcode = hashcode;
/* Add node to the hash table. */
node->h.hash_next = set->table[bucket];
set->table[bucket] = &node->h;
/* Add node to the set. */
set->count++;
hash_resize_after_add (set);
return 1;
}
static bool
gl_hash_remove (gl_set_t set, const void *elt)
{
size_t hashcode =
(set->hashcode_fn != NULL
? set->hashcode_fn (elt)
: (size_t)(uintptr_t) elt);
size_t bucket = hashcode % set->table_size;
gl_setelement_equals_fn equals = set->base.equals_fn;
/* Look for the first match in the hash bucket. */
gl_list_node_t *nodep;
for (nodep = (gl_list_node_t *) &set->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 (elt, node->value)
: elt == node->value))
{
/* Remove node from the hash table. */
*nodep = (gl_list_node_t) node->h.hash_next;
/* Remove node from the set. */
set->count--;
if (set->base.dispose_fn != NULL)
set->base.dispose_fn (node->value);
free (node);
return true;
}
}
return false;
}
static void
gl_hash_free (gl_set_t set)
{
if (set->count > 0)
{
gl_setelement_dispose_fn dispose = set->base.dispose_fn;
struct gl_hash_entry **table = set->table;
size_t i;
for (i = set->table_size; i > 0; )
{
gl_hash_entry_t node = table[--i];
while (node != NULL)
{
gl_hash_entry_t next = node->hash_next;
/* Free the entry. */
if (dispose != NULL)
dispose (((gl_list_node_t) node)->value);
free (node);
node = next;
}
}
}
free (set->table);
free (set);
}
/* ---------------------- gl_set_iterator_t Data Type ---------------------- */
/* Here we iterate through the hash buckets. Therefore the order in which the
elements are returned is unpredictable. */
static gl_set_iterator_t
gl_hash_iterator (gl_set_t set)
{
gl_set_iterator_t result;
result.vtable = set->base.vtable;
result.set = set;
result.p = NULL; /* runs through the nodes of a bucket */
result.i = 0; /* index of the bucket that p points into + 1 */
result.j = set->table_size;
#if defined GCC_LINT || defined lint
result.q = NULL;
result.count = 0;
#endif
return result;
}
static bool
gl_hash_iterator_next (gl_set_iterator_t *iterator, const void **eltp)
{
if (iterator->p != NULL)
{
/* We're traversing a bucket. */
gl_list_node_t node = (gl_list_node_t) iterator->p;
*eltp = node->value;
iterator->p = (gl_list_node_t) node->h.hash_next;
return true;
}
else
{
/* Find the next bucket that is not empty. */
size_t j = iterator->j;
size_t i = iterator->i;
if (i < j)
{
gl_hash_entry_t *table = iterator->set->table;
do
{
gl_list_node_t node = (gl_list_node_t) table[i++];
if (node != NULL)
{
*eltp = node->value;
iterator->p = (gl_list_node_t) node->h.hash_next;
iterator->i = i;
return true;
}
}
while (i < j);
}
/* Here iterator->p is still NULL, and i == j. */
iterator->i = j;
return false;
}
}
static void
gl_hash_iterator_free (gl_set_iterator_t *iterator)
{
}
const struct gl_set_implementation gl_hash_set_implementation =
{
gl_hash_nx_create_empty,
gl_hash_size,
gl_hash_search,
gl_hash_nx_add,
gl_hash_remove,
gl_hash_free,
gl_hash_iterator,
gl_hash_iterator_next,
gl_hash_iterator_free
};