/* Map data type implemented by a hash table. Copyright (C) 2006, 2008-2024 Free Software Foundation, Inc. Written by Bruno Haible , 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 . */ #include /* Specification. */ #include "gl_hash_map.h" #include /* for uintptr_t, SIZE_MAX */ #include #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 */ 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. */ struct gl_hash_entry **table; size_t table_size; /* Number of hash table entries. */ size_t count; }; #define CONTAINER_T gl_map_t #define CONTAINER_COUNT(map) (map)->count #include "gl_anyhash2.h" /* --------------------------- gl_map_t Data Type --------------------------- */ static gl_map_t gl_hash_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->count = 0; return map; fail: free (map); return NULL; } static size_t _GL_ATTRIBUTE_PURE gl_hash_size (gl_map_t map) { return map->count; } static bool _GL_ATTRIBUTE_PURE gl_hash_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. */ gl_list_node_t node; for (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_hash_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. */ { gl_list_node_t node; for (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; node->key = key; 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. */ map->count++; hash_resize_after_add (map); return 1; } static bool gl_hash_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. */ gl_list_node_t *nodep; for (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 map. */ map->count--; if (map->base.kdispose_fn != NULL) map->base.kdispose_fn (node->key); free (node); return true; } } return false; } static void gl_hash_free (gl_map_t map) { if (map->count > 0) { gl_mapkey_dispose_fn kdispose = map->base.kdispose_fn; gl_mapvalue_dispose_fn vdispose = map->base.vdispose_fn; struct gl_hash_entry **table = map->table; size_t i; for (i = map->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 (vdispose != NULL) vdispose (((gl_list_node_t) node)->value); if (kdispose != NULL) kdispose (((gl_list_node_t) node)->key); free (node); node = next; } } } free (map->table); free (map); } /* ---------------------- gl_map_iterator_t Data Type ---------------------- */ /* Here we iterate through the hash buckets. Therefore the order in which the pairs are returned is unpredictable. */ static gl_map_iterator_t gl_hash_iterator (gl_map_t map) { gl_map_iterator_t result; result.vtable = map->base.vtable; result.map = map; result.p = NULL; /* runs through the nodes of a bucket */ result.i = 0; /* index of the bucket that p points into + 1 */ result.j = map->table_size; #if defined GCC_LINT || defined lint result.q = NULL; result.count = 0; #endif return result; } static bool gl_hash_iterator_next (gl_map_iterator_t *iterator, const void **keyp, const void **valuep) { if (iterator->p != NULL) { /* We're traversing a bucket. */ gl_list_node_t node = (gl_list_node_t) iterator->p; *keyp = node->key; *valuep = 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->map->table; do { gl_list_node_t node = (gl_list_node_t) table[i++]; if (node != NULL) { *keyp = node->key; *valuep = 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_map_iterator_t *iterator) { } const struct gl_map_implementation gl_hash_map_implementation = { gl_hash_nx_create_empty, gl_hash_size, gl_hash_search, gl_hash_nx_getput, gl_hash_getremove, gl_hash_free, gl_hash_iterator, gl_hash_iterator_next, gl_hash_iterator_free };