Here comes the last of the five generic container types: - list - ordered set - set - ordered map - map
The operations are: Operation ARRAY LINKEDHASH HASH gl_map_size O(1) O(1) gl_map_get O(n) O(1) gl_map_put O(n) O(1) gl_map_remove O(n) O(1) gl_map_search O(n) O(1) gl_map_iterator O(1) O(1) gl_map_iterator_next O(1) O(1) Review and comments welcome! Bruno
>From 4d1dabf15bf9a3b5652872340ef85711b877c074 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Wed, 12 Dec 2018 01:23:21 +0100 Subject: [PATCH 1/8] map: New module. * lib/gl_map.h: New file. * lib/gl_map.c: New file. * lib/gl_omap.h (gl_mapkey_dispose_fn, gl_mapvalue_dispose_fn): Avoid conflict with gl_map.h. * modules/map: New file. --- ChangeLog | 9 ++ lib/gl_map.c | 3 + lib/gl_map.h | 382 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/gl_omap.h | 5 + modules/map | 24 ++++ 5 files changed, 423 insertions(+) create mode 100644 lib/gl_map.c create mode 100644 lib/gl_map.h create mode 100644 modules/map diff --git a/ChangeLog b/ChangeLog index 69a62b5..a49412b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,14 @@ 2018-12-11 Bruno Haible <br...@clisp.org> + map: New module. + * lib/gl_map.h: New file. + * lib/gl_map.c: New file. + * lib/gl_omap.h (gl_mapkey_dispose_fn, gl_mapvalue_dispose_fn): Avoid + conflict with gl_map.h. + * modules/map: New file. + +2018-12-11 Bruno Haible <br...@clisp.org> + omap: Don't dispose the old value when the function returns it. * lib/gl_array_omap.c (gl_array_remove_at): Don't invoke the vdispose_fn here. diff --git a/lib/gl_map.c b/lib/gl_map.c new file mode 100644 index 0000000..758a65f --- /dev/null +++ b/lib/gl_map.c @@ -0,0 +1,3 @@ +#include <config.h> +#define GL_MAP_INLINE _GL_EXTERN_INLINE +#include "gl_map.h" diff --git a/lib/gl_map.h b/lib/gl_map.h new file mode 100644 index 0000000..e14dd65 --- /dev/null +++ b/lib/gl_map.h @@ -0,0 +1,382 @@ +/* Abstract map data type. + Copyright (C) 2006-2007, 2009-2018 Free Software Foundation, Inc. + Written by Bruno Haible <br...@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/>. */ + +#ifndef _GL_MAP_H +#define _GL_MAP_H + +#include <stdbool.h> +#include <stddef.h> + +#ifndef _GL_INLINE_HEADER_BEGIN + #error "Please include config.h first." +#endif +_GL_INLINE_HEADER_BEGIN +#ifndef GL_MAP_INLINE +# define GL_MAP_INLINE _GL_INLINE +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +/* gl_map is an abstract map data type. It can contain any number of + (key, value) pairs, where + - keys and values are objects ('void *' or 'const void *' pointers), + - There are no (key, value1) and (key, value2) pairs with the same key + (in the sense of a given comparator function). + + There are several implementations of this map datatype, optimized for + different operations or for memory. You can start using the simplest map + implementation, GL_ARRAY_MAP, and switch to a different implementation + later, when you realize which operations are performed the most frequently. + The API of the different implementations is exactly the same; when switching + to a different implementation, you only have to change the gl_map_create + call. + + The implementations are: + GL_ARRAY_MAP a growable array + GL_LINKEDHASH_MAP a hash table with a linked list + GL_HASH_MAP a hash table + + The memory consumption is asymptotically the same: O(1) for every pair + in the map. When looking more closely at the average memory consumed + for an object, GL_ARRAY_MAP is the most compact representation, then comes + GL_HASH_MAP, and GL_LINKEDHASH_MAP needs the most memory. + + The guaranteed average performance of the operations is, for a map of + n pairs: + + Operation ARRAY LINKEDHASH + HASH + + gl_map_size O(1) O(1) + gl_map_get O(n) O(1) + gl_map_put O(n) O(1) + gl_map_remove O(n) O(1) + gl_map_search O(n) O(1) + gl_map_iterator O(1) O(1) + gl_map_iterator_next O(1) O(1) + */ + +/* --------------------------- gl_map_t Data Type --------------------------- */ + +/* Type of function used to compare two keys. + NULL denotes pointer comparison. */ +typedef bool (*gl_mapkey_equals_fn) (const void *key1, const void *key2); + +/* Type of function used to compute a hash code. + NULL denotes a function that depends only on the pointer itself. */ +typedef size_t (*gl_mapkey_hashcode_fn) (const void *key); + +#ifndef _GL_MAP_DISPOSE_FNS_DEFINED + +/* Type of function used to dispose a key once a (key, value) pair is removed + from a map. NULL denotes a no-op. */ +typedef void (*gl_mapkey_dispose_fn) (const void *key); + +/* Type of function used to dispose a value once a (key, value) pair is removed + from a map. NULL denotes a no-op. */ +typedef void (*gl_mapvalue_dispose_fn) (const void *value); + +# define _GL_MAP_DISPOSE_FNS_DEFINED 1 +#endif + +struct gl_map_impl; +/* Type representing an entire map. */ +typedef struct gl_map_impl * gl_map_t; + +struct gl_map_implementation; +/* Type representing a map datatype implementation. */ +typedef const struct gl_map_implementation * gl_map_implementation_t; + +#if 0 /* Unless otherwise specified, these are defined inline below. */ + +/* Create an empty map. + IMPLEMENTATION is one of GL_ARRAY_MAP, GL_LINKEDHASH_MAP, GL_HASH_MAP. + EQUALS_FN is a key comparison function or NULL. + HASHCODE_FN is a key hash code function or NULL. + KDISPOSE_FN is a key disposal function or NULL. + VDISPOSE_FN is a value disposal function or NULL. */ +/* declared in gl_xmap.h */ +extern gl_map_t gl_map_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); +/* Likewise. Return NULL upon out-of-memory. */ +extern gl_map_t gl_map_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); + +/* Return the current number of pairs in a map. */ +extern size_t gl_map_size (gl_map_t map); + +/* Search whether a pair with the given key is already in the map. + Return the value if found, or NULL if not present in the map. */ +extern const void * gl_map_get (gl_map_t map, const void *key); + +/* Search whether a pair with the given key is already in the map. + Return true and set *VALUEP to the value if found. + Return false if not present in the map. */ +extern bool gl_map_search (gl_map_t map, const void *key, const void **valuep); + +/* Add a pair to a map. + Return true if a pair with the given key was not already in the map and so + this pair was added. + Return false if a pair with the given key was already in the map and only + its value was replaced. */ +/* declared in gl_xmap.h */ +extern bool gl_map_put (gl_map_t map, const void *key, const void *value); +/* Likewise. Return -1 upon out-of-memory. */ +extern int gl_map_nx_put (gl_map_t map, const void *key, const void *value) +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) + __attribute__ ((__warn_unused_result__)) +#endif + ; + +/* Add a pair to a map and retrieve the previous value. + Return true if a pair with the given key was not already in the map and so + this pair was added. + Return false and set *OLDVALUEP to the previous value, if a pair with the + given key was already in the map and only its value was replaced. */ +/* declared in gl_xmap.h */ +extern bool gl_map_getput (gl_map_t map, const void *key, const void *value, + const void **oldvaluep); +/* Likewise. Return -1 upon out-of-memory. */ +extern int gl_map_nx_getput (gl_map_t map, const void *key, const void *value, + const void **oldvaluep) +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) + __attribute__ ((__warn_unused_result__)) +#endif + ; + +/* Remove a pair from a map. + Return true if the key was found and its pair removed. + Return false otherwise. */ +extern bool gl_map_remove (gl_map_t map, const void *key); + +/* Remove a pair from a map and retrieve the previous value. + Return true and set *OLDVALUEP to the previous value, if the key was found + and its pair removed. + Return false otherwise. */ +extern bool gl_map_getremove (gl_map_t map, const void *key, + const void **oldvaluep); + +/* Free an entire map. + (But this call does not free the keys and values of the pairs in the map. + It only invokes the KDISPOSE_FN on each key and the VDISPOSE_FN on each value + of the pairs in the map.) */ +extern void gl_map_free (gl_map_t map); + +#endif /* End of inline and gl_xmap.h-defined functions. */ + +/* ---------------------- gl_map_iterator_t Data Type ---------------------- */ + +/* Functions for iterating through a map. + Note: Iterating through a map of type GL_HASH_MAP returns the pairs in an + unpredictable order. If you need a predictable order, use GL_LINKEDHASH_MAP + instead of GL_HASH_MAP. */ + +/* Type of an iterator that traverses a map. + This is a fixed-size struct, so that creation of an iterator doesn't need + memory allocation on the heap. */ +typedef struct +{ + /* For fast dispatch of gl_map_iterator_next. */ + const struct gl_map_implementation *vtable; + /* For detecting whether the last returned pair was removed. */ + gl_map_t map; + size_t count; + /* Other, implementation-private fields. */ + void *p; void *q; + size_t i; size_t j; +} gl_map_iterator_t; + +#if 0 /* These are defined inline below. */ + +/* Create an iterator traversing a map. + The map's contents must not be modified while the iterator is in use, + except for modifying the value of the last returned key or removing the + last returned pair. */ +extern gl_map_iterator_t gl_map_iterator (gl_map_t map); + +/* If there is a next pair, store the next pair in *KEYP and *VALUEP, advance + the iterator, and return true. Otherwise, return false. */ +extern bool gl_map_iterator_next (gl_map_iterator_t *iterator, + const void **keyp, const void **valuep); + +/* Free an iterator. */ +extern void gl_map_iterator_free (gl_map_iterator_t *iterator); + +#endif /* End of inline functions. */ + +/* ------------------------- Implementation Details ------------------------- */ + +struct gl_map_implementation +{ + /* gl_map_t functions. */ + gl_map_t (*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); + size_t (*size) (gl_map_t map); + bool (*search) (gl_map_t map, const void *key, const void **valuep); + int (*nx_getput) (gl_map_t map, const void *key, const void *value, + const void **oldvaluep); + bool (*getremove) (gl_map_t map, const void *key, const void **oldvaluep); + void (*map_free) (gl_map_t map); + /* gl_map_iterator_t functions. */ + gl_map_iterator_t (*iterator) (gl_map_t map); + bool (*iterator_next) (gl_map_iterator_t *iterator, + const void **keyp, const void **valuep); + void (*iterator_free) (gl_map_iterator_t *iterator); +}; + +struct gl_map_impl_base +{ + const struct gl_map_implementation *vtable; + gl_mapkey_equals_fn equals_fn; + gl_mapkey_dispose_fn kdispose_fn; + gl_mapvalue_dispose_fn vdispose_fn; +}; + +/* Define most functions of this file as accesses to the + struct gl_map_implementation. */ + +GL_MAP_INLINE gl_map_t +gl_map_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) +{ + return implementation->nx_create_empty (implementation, + equals_fn, hashcode_fn, + kdispose_fn, vdispose_fn); +} + +GL_MAP_INLINE size_t +gl_map_size (gl_map_t map) +{ + return ((const struct gl_map_impl_base *) map)->vtable->size (map); +} + +GL_MAP_INLINE bool +gl_map_search (gl_map_t map, const void *key, const void **valuep) +{ + return ((const struct gl_map_impl_base *) map)->vtable + ->search (map, key, valuep); +} + +GL_MAP_INLINE int +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) + __attribute__ ((__warn_unused_result__)) +#endif +gl_map_nx_getput (gl_map_t map, const void *key, const void *value, + const void **oldvaluep) +{ + return ((const struct gl_map_impl_base *) map)->vtable + ->nx_getput (map, key, value, oldvaluep); +} + +GL_MAP_INLINE bool +gl_map_getremove (gl_map_t map, const void *key, const void **oldvaluep) +{ + return ((const struct gl_map_impl_base *) map)->vtable + ->getremove (map, key, oldvaluep); +} + +GL_MAP_INLINE void +gl_map_free (gl_map_t map) +{ + ((const struct gl_map_impl_base *) map)->vtable->map_free (map); +} + +GL_MAP_INLINE gl_map_iterator_t +gl_map_iterator (gl_map_t map) +{ + return ((const struct gl_map_impl_base *) map)->vtable->iterator (map); +} + +GL_MAP_INLINE bool +gl_map_iterator_next (gl_map_iterator_t *iterator, + const void **keyp, const void **valuep) +{ + return iterator->vtable->iterator_next (iterator, keyp, valuep); +} + +GL_MAP_INLINE void +gl_map_iterator_free (gl_map_iterator_t *iterator) +{ + iterator->vtable->iterator_free (iterator); +} + +/* Define the convenience functions, that is, the functions that are independent + of the implementation. */ + +GL_MAP_INLINE const void * +gl_map_get (gl_map_t map, const void *key) +{ + const void *value = NULL; + gl_map_search (map, key, &value); + return value; +} + +GL_MAP_INLINE int +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) + __attribute__ ((__warn_unused_result__)) +#endif +gl_map_nx_put (gl_map_t map, const void *key, const void *value) +{ + const void *oldvalue; + int result = gl_map_nx_getput (map, key, value, &oldvalue); + if (result == 0) + { + gl_mapvalue_dispose_fn vdispose_fn = + ((const struct gl_map_impl_base *) map)->vdispose_fn; + if (vdispose_fn != NULL) + vdispose_fn (oldvalue); + } + return result; +} + +GL_MAP_INLINE bool +gl_map_remove (gl_map_t map, const void *key) +{ + const void *oldvalue; + bool result = gl_map_getremove (map, key, &oldvalue); + if (result) + { + gl_mapvalue_dispose_fn vdispose_fn = + ((const struct gl_map_impl_base *) map)->vdispose_fn; + if (vdispose_fn != NULL) + vdispose_fn (oldvalue); + } + return result; +} + +#ifdef __cplusplus +} +#endif + +_GL_INLINE_HEADER_END + +#endif /* _GL_MAP_H */ diff --git a/lib/gl_omap.h b/lib/gl_omap.h index aa82e33..da41bc4 100644 --- a/lib/gl_omap.h +++ b/lib/gl_omap.h @@ -81,6 +81,8 @@ extern "C" { NULL denotes pointer comparison. */ typedef int (*gl_mapkey_compar_fn) (const void *key1, const void *key2); +#ifndef _GL_MAP_DISPOSE_FNS_DEFINED + /* Type of function used to dispose a key once a (key, value) pair is removed from a map. NULL denotes a no-op. */ typedef void (*gl_mapkey_dispose_fn) (const void *key); @@ -89,6 +91,9 @@ typedef void (*gl_mapkey_dispose_fn) (const void *key); from a map. NULL denotes a no-op. */ typedef void (*gl_mapvalue_dispose_fn) (const void *value); +# define _GL_MAP_DISPOSE_FNS_DEFINED 1 +#endif + /* Type of function used to compare a key with a threshold. Return true if the key is greater or equal than the threshold. */ typedef bool (*gl_mapkey_threshold_fn) (const void *key, const void *threshold); diff --git a/modules/map b/modules/map new file mode 100644 index 0000000..0b611ed --- /dev/null +++ b/modules/map @@ -0,0 +1,24 @@ +Description: +Abstract map data type. + +Files: +lib/gl_map.h +lib/gl_map.c + +Depends-on: +extern-inline +stdbool + +configure.ac: + +Makefile.am: +lib_SOURCES += gl_map.h gl_map.c + +Include: +"gl_map.h" + +License: +GPL + +Maintainer: +all -- 2.7.4
>From e0e9153a933c9c8d135515f5f612ed228d875d01 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Wed, 12 Dec 2018 01:25:04 +0100 Subject: [PATCH 2/8] array-map: New module. * lib/gl_array_map.h: New file. * lib/gl_array_map.c: New file. * modules/array-map: New file. --- ChangeLog | 5 + lib/gl_array_map.c | 299 +++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/gl_array_map.h | 34 ++++++ modules/array-map | 24 +++++ 4 files changed, 362 insertions(+) create mode 100644 lib/gl_array_map.c create mode 100644 lib/gl_array_map.h create mode 100644 modules/array-map diff --git a/ChangeLog b/ChangeLog index a49412b..a24e5c1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2018-12-11 Bruno Haible <br...@clisp.org> + array-map: New module. + * lib/gl_array_map.h: New file. + * lib/gl_array_map.c: New file. + * modules/array-map: New file. + map: New module. * lib/gl_map.h: New file. * lib/gl_map.c: New file. diff --git a/lib/gl_array_map.c b/lib/gl_array_map.c new file mode 100644 index 0000000..e855c66 --- /dev/null +++ b/lib/gl_array_map.c @@ -0,0 +1,299 @@ +/* Map data type implemented by an array. + Copyright (C) 2006-2018 Free Software Foundation, Inc. + Written by Bruno Haible <br...@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_array_map.h" + +#include <stdlib.h> + +/* Checked size_t computations. */ +#include "xsize.h" + +#ifndef uintptr_t +# define uintptr_t unsigned long +#endif + +/* --------------------------- gl_map_t Data Type --------------------------- */ + +struct pair +{ + const void *key; + const void *value; +}; + +/* Concrete gl_map_impl type, valid for this file only. */ +struct gl_map_impl +{ + struct gl_map_impl_base base; + /* An array of ALLOCATED pairs, of which the first COUNT are used. + 0 <= COUNT <= ALLOCATED. */ + struct pair *pairs; + size_t count; + size_t allocated; +}; + +static gl_map_t +gl_array_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->pairs = NULL; + map->count = 0; + map->allocated = 0; + + return map; +} + +static size_t +gl_array_size (gl_map_t map) +{ + return map->count; +} + +static size_t +gl_array_indexof (gl_map_t map, const void *key) +{ + size_t count = map->count; + + if (count > 0) + { + gl_mapkey_equals_fn equals = map->base.equals_fn; + if (equals != NULL) + { + size_t i; + + for (i = 0; i < count; i++) + if (equals (map->pairs[i].key, key)) + return i; + } + else + { + size_t i; + + for (i = 0; i < count; i++) + if (map->pairs[i].key == key) + return i; + } + } + return (size_t)(-1); +} + +static bool +gl_array_search (gl_map_t map, const void *key, const void **valuep) +{ + size_t index = gl_array_indexof (map, key); + if (index != (size_t)(-1)) + { + *valuep = map->pairs[index].value; + return true; + } + else + return false; +} + +/* Ensure that map->allocated > map->count. + Return 0 upon success, -1 upon out-of-memory. */ +static int +grow (gl_map_t map) +{ + size_t new_allocated; + size_t memory_size; + struct pair *memory; + + new_allocated = xtimes (map->allocated, 2); + new_allocated = xsum (new_allocated, 1); + memory_size = xtimes (new_allocated, sizeof (struct pair)); + if (size_overflow_p (memory_size)) + /* Overflow, would lead to out of memory. */ + return -1; + memory = (struct pair *) realloc (map->pairs, memory_size); + if (memory == NULL) + /* Out of memory. */ + return -1; + map->pairs = memory; + map->allocated = new_allocated; + return 0; +} + +static int +gl_array_nx_getput (gl_map_t map, const void *key, const void *value, + const void **oldvaluep) +{ + size_t index = gl_array_indexof (map, key); + if (index != (size_t)(-1)) + { + *oldvaluep = map->pairs[index].value; + map->pairs[index].value = value; + return 0; + } + else + { + size_t count = map->count; + struct pair *pairs; + + if (count == map->allocated) + if (grow (map) < 0) + return -1; + pairs = map->pairs; + pairs[count].key = key; + pairs[count].value = value; + map->count = count + 1; + return 1; + } +} + +/* Remove the pair at the given position, + 0 <= position < gl_map_size (map). */ +static void +gl_array_remove_at (gl_map_t map, size_t position) +{ + size_t count = map->count; + struct pair *pairs; + size_t i; + + pairs = map->pairs; + if (map->base.kdispose_fn != NULL) + map->base.kdispose_fn (pairs[position].key); + for (i = position + 1; i < count; i++) + pairs[i - 1] = pairs[i]; + map->count = count - 1; +} + +static bool +gl_array_getremove (gl_map_t map, const void *key, const void **oldvaluep) +{ + size_t index = gl_array_indexof (map, key); + if (index != (size_t)(-1)) + { + *oldvaluep = map->pairs[index].value; + gl_array_remove_at (map, index); + return true; + } + else + return false; +} + +static void +gl_array_free (gl_map_t map) +{ + if (map->pairs != NULL) + { + if (map->base.kdispose_fn != NULL || map->base.vdispose_fn != NULL) + { + size_t count = map->count; + + if (count > 0) + { + gl_mapkey_dispose_fn kdispose = map->base.kdispose_fn; + gl_mapvalue_dispose_fn vdispose = map->base.vdispose_fn; + struct pair *pairs = map->pairs; + + do + { + if (vdispose) + vdispose (pairs->value); + if (kdispose) + kdispose (pairs->key); + pairs++; + } + while (--count > 0); + } + } + free (map->pairs); + } + free (map); +} + +/* ---------------------- gl_map_iterator_t Data Type ---------------------- */ + +static gl_map_iterator_t +gl_array_iterator (gl_map_t map) +{ + gl_map_iterator_t result; + + result.vtable = map->base.vtable; + result.map = map; + result.count = map->count; + result.p = map->pairs + 0; + result.q = map->pairs + map->count; +#if defined GCC_LINT || defined lint + result.i = 0; + result.j = 0; +#endif + + return result; +} + +static bool +gl_array_iterator_next (gl_map_iterator_t *iterator, + const void **keyp, const void **valuep) +{ + gl_map_t map = iterator->map; + if (iterator->count != map->count) + { + if (iterator->count != map->count + 1) + /* Concurrent modifications were done on the map. */ + abort (); + /* The last returned pair was removed. */ + iterator->count--; + iterator->p = (struct pair *) iterator->p - 1; + iterator->q = (struct pair *) iterator->q - 1; + } + if (iterator->p < iterator->q) + { + struct pair *p = (struct pair *) iterator->p; + *keyp = p->key; + *valuep = p->value; + iterator->p = p + 1; + return true; + } + else + return false; +} + +static void +gl_array_iterator_free (gl_map_iterator_t *iterator) +{ +} + + +const struct gl_map_implementation gl_array_map_implementation = + { + gl_array_nx_create_empty, + gl_array_size, + gl_array_search, + gl_array_nx_getput, + gl_array_getremove, + gl_array_free, + gl_array_iterator, + gl_array_iterator_next, + gl_array_iterator_free + }; diff --git a/lib/gl_array_map.h b/lib/gl_array_map.h new file mode 100644 index 0000000..e9ff412 --- /dev/null +++ b/lib/gl_array_map.h @@ -0,0 +1,34 @@ +/* Map data type implemented by an array. + Copyright (C) 2006, 2009-2018 Free Software Foundation, Inc. + Written by Bruno Haible <br...@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/>. */ + +#ifndef _GL_ARRAY_MAP_H +#define _GL_ARRAY_MAP_H + +#include "gl_map.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const struct gl_map_implementation gl_array_map_implementation; +#define GL_ARRAY_MAP &gl_array_map_implementation + +#ifdef __cplusplus +} +#endif + +#endif /* _GL_ARRAY_MAP_H */ diff --git a/modules/array-map b/modules/array-map new file mode 100644 index 0000000..abdef16 --- /dev/null +++ b/modules/array-map @@ -0,0 +1,24 @@ +Description: +Set data type implemented by an array. + +Files: +lib/gl_array_map.h +lib/gl_array_map.c + +Depends-on: +map +xsize + +configure.ac: + +Makefile.am: +lib_SOURCES += gl_array_map.h gl_array_map.c + +Include: +"gl_array_map.h" + +License: +GPL + +Maintainer: +all -- 2.7.4
>From 4d2c4598b2c8d1a726dd2480f6ee06c1f32ddd92 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Wed, 12 Dec 2018 01:27:08 +0100 Subject: [PATCH 3/8] linkedhash-map: New module. * lib/gl_linkedhash_map.h: New file. * lib/gl_linkedhash_map.c: New file. * lib/gl_anyhash1.h: Update comments. * lib/gl_anyhash2.h: Likewise. * modules/linkedhash-map: New file. --- ChangeLog | 7 + lib/gl_anyhash1.h | 3 +- lib/gl_anyhash2.h | 3 +- lib/gl_linkedhash_map.c | 334 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/gl_linkedhash_map.h | 34 +++++ modules/linkedhash-map | 28 ++++ 6 files changed, 407 insertions(+), 2 deletions(-) create mode 100644 lib/gl_linkedhash_map.c create mode 100644 lib/gl_linkedhash_map.h create mode 100644 modules/linkedhash-map diff --git a/ChangeLog b/ChangeLog index a24e5c1..4b7407e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,12 @@ 2018-12-11 Bruno Haible <br...@clisp.org> + linkedhash-map: New module. + * lib/gl_linkedhash_map.h: New file. + * lib/gl_linkedhash_map.c: New file. + * lib/gl_anyhash1.h: Update comments. + * lib/gl_anyhash2.h: Likewise. + * modules/linkedhash-map: New file. + array-map: New module. * lib/gl_array_map.h: New file. * lib/gl_array_map.c: New file. diff --git a/lib/gl_anyhash1.h b/lib/gl_anyhash1.h index 86bdee3..dda29fe 100644 --- a/lib/gl_anyhash1.h +++ b/lib/gl_anyhash1.h @@ -17,7 +17,8 @@ /* Common code of gl_linkedhash_list.c, gl_avltreehash_list.c, gl_rbtreehash_list.c, - gl_linkedhash_set.c, gl_hash_set.c. */ + gl_linkedhash_set.c, gl_hash_set.c, + gl_linkedhash_map.c, gl_hash_map.c. */ /* Hash table entry. */ struct gl_hash_entry diff --git a/lib/gl_anyhash2.h b/lib/gl_anyhash2.h index d4c5430..c7ece04 100644 --- a/lib/gl_anyhash2.h +++ b/lib/gl_anyhash2.h @@ -17,7 +17,8 @@ /* Common code of gl_linkedhash_list.c, gl_avltreehash_list.c, gl_rbtreehash_list.c, - gl_linkedhash_set.c, gl_hash_set.c. */ + gl_linkedhash_set.c, gl_hash_set.c, + gl_linkedhash_map.c, gl_hash_map.c. */ #include "gl_anyhash_primes.h" diff --git a/lib/gl_linkedhash_map.c b/lib/gl_linkedhash_map.c new file mode 100644 index 0000000..2cf3199 --- /dev/null +++ b/lib/gl_linkedhash_map.c @@ -0,0 +1,334 @@ +/* Map data type implemented by a hash table with a linked list. + Copyright (C) 2006, 2008-2018 Free Software Foundation, Inc. + Written by Bruno Haible <br...@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 SIZE_MAX */ +#include <stdlib.h> + +#include "xsize.h" + +#ifndef uintptr_t +# define uintptr_t unsigned long +#endif + +/* --------------------------- 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. */ + struct gl_hash_entry **table; + size_t 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. */ + 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_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. */ + { + 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; + + 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. */ + 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 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; + gl_list_node_t node; + + for (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 + }; diff --git a/lib/gl_linkedhash_map.h b/lib/gl_linkedhash_map.h new file mode 100644 index 0000000..4ea98ba --- /dev/null +++ b/lib/gl_linkedhash_map.h @@ -0,0 +1,34 @@ +/* Map data type implemented by a hash table with a linked list. + Copyright (C) 2006, 2009-2018 Free Software Foundation, Inc. + Written by Bruno Haible <br...@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/>. */ + +#ifndef _GL_LINKEDHASH_MAP_H +#define _GL_LINKEDHASH_MAP_H + +#include "gl_map.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const struct gl_map_implementation gl_linkedhash_map_implementation; +#define GL_LINKEDHASH_MAP &gl_linkedhash_map_implementation + +#ifdef __cplusplus +} +#endif + +#endif /* _GL_LINKEDHASH_MAP_H */ diff --git a/modules/linkedhash-map b/modules/linkedhash-map new file mode 100644 index 0000000..3424660 --- /dev/null +++ b/modules/linkedhash-map @@ -0,0 +1,28 @@ +Description: +Set data type implemented by a hash table with a linked list. + +Files: +lib/gl_linkedhash_map.h +lib/gl_linkedhash_map.c +lib/gl_anyhash1.h +lib/gl_anyhash2.h +lib/gl_anyhash_primes.h + +Depends-on: +map +stdint +xsize + +configure.ac: + +Makefile.am: +lib_SOURCES += gl_linkedhash_map.h gl_linkedhash_map.c gl_anyhash1.h gl_anyhash2.h gl_anyhash_primes.h + +Include: +"gl_linkedhash_map.h" + +License: +GPL + +Maintainer: +all -- 2.7.4
>From b23673c63782f8468fb8261fbab9f7bab3758609 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Wed, 12 Dec 2018 01:27:47 +0100 Subject: [PATCH 4/8] hash-map: New module. * lib/gl_hash_map.h: New file. * lib/gl_hash_map.c: New file. * modules/hash-map: New file. --- ChangeLog | 5 + lib/gl_hash_map.c | 337 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/gl_hash_map.h | 34 ++++++ modules/hash-map | 28 +++++ 4 files changed, 404 insertions(+) create mode 100644 lib/gl_hash_map.c create mode 100644 lib/gl_hash_map.h create mode 100644 modules/hash-map diff --git a/ChangeLog b/ChangeLog index 4b7407e..817998e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2018-12-11 Bruno Haible <br...@clisp.org> + hash-map: New module. + * lib/gl_hash_map.h: New file. + * lib/gl_hash_map.c: New file. + * modules/hash-map: New file. + linkedhash-map: New module. * lib/gl_linkedhash_map.h: New file. * lib/gl_linkedhash_map.c: New file. diff --git a/lib/gl_hash_map.c b/lib/gl_hash_map.c new file mode 100644 index 0000000..7d68e7b --- /dev/null +++ b/lib/gl_hash_map.c @@ -0,0 +1,337 @@ +/* Map data type implemented by a hash table. + Copyright (C) 2006, 2008-2018 Free Software Foundation, Inc. + Written by Bruno Haible <br...@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_map.h" + +#include <stdint.h> /* for SIZE_MAX */ +#include <stdlib.h> + +#include "xsize.h" + +#ifndef uintptr_t +# define uintptr_t unsigned long +#endif + +/* --------------------------- 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 + }; diff --git a/lib/gl_hash_map.h b/lib/gl_hash_map.h new file mode 100644 index 0000000..1ae8085 --- /dev/null +++ b/lib/gl_hash_map.h @@ -0,0 +1,34 @@ +/* Map data type implemented by a hash table. + Copyright (C) 2006, 2009-2018 Free Software Foundation, Inc. + Written by Bruno Haible <br...@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/>. */ + +#ifndef _GL_HASH_MAP_H +#define _GL_HASH_MAP_H + +#include "gl_map.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const struct gl_map_implementation gl_hash_map_implementation; +#define GL_HASH_MAP &gl_hash_map_implementation + +#ifdef __cplusplus +} +#endif + +#endif /* _GL_HASH_MAP_H */ diff --git a/modules/hash-map b/modules/hash-map new file mode 100644 index 0000000..5cc5c68 --- /dev/null +++ b/modules/hash-map @@ -0,0 +1,28 @@ +Description: +Set data type implemented by a hash table. + +Files: +lib/gl_hash_map.h +lib/gl_hash_map.c +lib/gl_anyhash1.h +lib/gl_anyhash2.h +lib/gl_anyhash_primes.h + +Depends-on: +map +stdint +xsize + +configure.ac: + +Makefile.am: +lib_SOURCES += gl_hash_map.h gl_hash_map.c gl_anyhash1.h gl_anyhash2.h gl_anyhash_primes.h + +Include: +"gl_hash_map.h" + +License: +GPL + +Maintainer: +all -- 2.7.4
>From 3b4e211ff049fb5d1a5a92a04b96952c28c7e06b Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Wed, 12 Dec 2018 01:29:10 +0100 Subject: [PATCH 5/8] xmap: New module. * lib/gl_xmap.h: New file. * lib/gl_xmap.c: New file. * modules/xmap: New file. --- ChangeLog | 5 ++++ lib/gl_xmap.c | 3 ++ lib/gl_xmap.h | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ modules/xmap | 26 +++++++++++++++++ 4 files changed, 125 insertions(+) create mode 100644 lib/gl_xmap.c create mode 100644 lib/gl_xmap.h create mode 100644 modules/xmap diff --git a/ChangeLog b/ChangeLog index 817998e..8e3189e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2018-12-11 Bruno Haible <br...@clisp.org> + xmap: New module. + * lib/gl_xmap.h: New file. + * lib/gl_xmap.c: New file. + * modules/xmap: New file. + hash-map: New module. * lib/gl_hash_map.h: New file. * lib/gl_hash_map.c: New file. diff --git a/lib/gl_xmap.c b/lib/gl_xmap.c new file mode 100644 index 0000000..3d39fdb --- /dev/null +++ b/lib/gl_xmap.c @@ -0,0 +1,3 @@ +#include <config.h> +#define GL_XMAP_INLINE _GL_EXTERN_INLINE +#include "gl_xmap.h" diff --git a/lib/gl_xmap.h b/lib/gl_xmap.h new file mode 100644 index 0000000..6eb7fff --- /dev/null +++ b/lib/gl_xmap.h @@ -0,0 +1,91 @@ +/* Abstract map data type, with out-of-memory checking. + Copyright (C) 2009-2018 Free Software Foundation, Inc. + Written by Bruno Haible <br...@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/>. */ + +#ifndef _GL_XMAP_H +#define _GL_XMAP_H + +#include "gl_map.h" +#include "xalloc.h" + +#ifndef _GL_INLINE_HEADER_BEGIN + #error "Please include config.h first." +#endif +_GL_INLINE_HEADER_BEGIN +#ifndef GL_XMAP_INLINE +# define GL_XMAP_INLINE _GL_INLINE +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +/* These functions are thin wrappers around the corresponding functions with + _nx_ infix from gl_map.h. Upon out-of-memory, they invoke xalloc_die (), + instead of returning an error indicator. */ +#if 0 /* These are defined inline below. */ +extern gl_map_t gl_map_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); +extern bool gl_map_put (gl_map_t map, const void *key, const void *value); +extern bool gl_map_getput (gl_map_t map, const void *key, const void *value, + const void **oldvaluep); +#endif + +GL_XMAP_INLINE gl_map_t +gl_map_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) +{ + gl_map_t result = + gl_map_nx_create_empty (implementation, equals_fn, hashcode_fn, + kdispose_fn, vdispose_fn); + if (result == NULL) + xalloc_die (); + return result; +} + +GL_XMAP_INLINE bool +gl_map_put (gl_map_t map, const void *key, const void *value) +{ + int result = gl_map_nx_put (map, key, value); + if (result < 0) + xalloc_die (); + return result; +} + +GL_XMAP_INLINE bool +gl_map_getput (gl_map_t map, const void *key, const void *value, + const void **oldvaluep) +{ + int result = gl_map_nx_getput (map, key, value, oldvaluep); + if (result < 0) + xalloc_die (); + return result; +} + +#ifdef __cplusplus +} +#endif + +_GL_INLINE_HEADER_END + +#endif /* _GL_XMAP_H */ diff --git a/modules/xmap b/modules/xmap new file mode 100644 index 0000000..2a7cead --- /dev/null +++ b/modules/xmap @@ -0,0 +1,26 @@ +Description: +Abstract map data type, with out-of-memory checking. + +Files: +lib/gl_xmap.h +lib/gl_xmap.c + +Depends-on: +map +extern-inline +stdbool +xalloc-die + +configure.ac: + +Makefile.am: +lib_SOURCES += gl_xmap.h gl_xmap.c + +Include: +"gl_xmap.h" + +License: +GPL + +Maintainer: +all -- 2.7.4
>From 4e8100e0fd4594a43f841403de8006d08e7f930b Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Wed, 12 Dec 2018 01:29:52 +0100 Subject: [PATCH 6/8] array-map: Add tests. * tests/test-array_map.c: New file. * modules/array-map-tests: New file. --- ChangeLog | 4 + modules/array-map-tests | 14 ++++ tests/test-array_map.c | 219 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 237 insertions(+) create mode 100644 modules/array-map-tests create mode 100644 tests/test-array_map.c diff --git a/ChangeLog b/ChangeLog index 8e3189e..c9ddd7c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2018-12-11 Bruno Haible <br...@clisp.org> + array-map: Add tests. + * tests/test-array_map.c: New file. + * modules/array-map-tests: New file. + xmap: New module. * lib/gl_xmap.h: New file. * lib/gl_xmap.c: New file. diff --git a/modules/array-map-tests b/modules/array-map-tests new file mode 100644 index 0000000..7dbcd73 --- /dev/null +++ b/modules/array-map-tests @@ -0,0 +1,14 @@ +Files: +tests/test-array_map.c +tests/macros.h + +Depends-on: +xlist +array-list +xalloc + +configure.ac: + +Makefile.am: +TESTS += test-array_map +check_PROGRAMS += test-array_map diff --git a/tests/test-array_map.c b/tests/test-array_map.c new file mode 100644 index 0000000..5a35d2a --- /dev/null +++ b/tests/test-array_map.c @@ -0,0 +1,219 @@ +/* Test of map data type implementation. + Copyright (C) 2006-2018 Free Software Foundation, Inc. + Written by Bruno Haible <br...@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> + +#include "gl_array_map.h" + +#include <stdlib.h> +#include <string.h> + +#include "gl_xlist.h" +#include "gl_array_list.h" +#include "macros.h" + +static const char *objects[30] = + { + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", + "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "<", ">", "[", "]" + }; + +#define SIZE_BITS (sizeof (size_t) * CHAR_BIT) + +static bool +string_equals (const void *x1, const void *x2) +{ + const char *s1 = x1; + const char *s2 = x2; + return strcmp (s1, s2) == 0; +} + +/* A hash function for NUL-terminated char* strings using + the method described by Bruno Haible. + See https://www.haible.de/bruno/hashfunc.html. */ +static size_t +string_hash (const void *x) +{ + const char *s = x; + size_t h = 0; + + for (; *s; s++) + h = *s + ((h << 9) | (h >> (SIZE_BITS - 9))); + + return h; +} + +#define RANDOM(n) (rand () % (n)) +#define RANDOM_OBJECT() objects[RANDOM (SIZEOF (objects))] + +struct pair +{ + const void *key; + const void *value; +}; + +static int +cmp_pairs_in_array (const void *pairptr1, const void *pairptr2) +{ + const void *key1 = ((struct pair const *)pairptr1)->key; + const void *key2 = ((struct pair const *)pairptr2)->key; + return strcmp ((const char *) key1, (const char *) key2); +} + +static void +check_equals (gl_map_t map1, gl_list_t keys, gl_list_t values) +{ + size_t n = gl_map_size (map1); + struct pair *pairs_of_map1 = XNMALLOC (n, struct pair); + + gl_map_iterator_t iter1; + const void *key1; + const void *value1; + size_t i; + + ASSERT (gl_list_size (keys) == n); + ASSERT (gl_list_size (values) == n); + iter1 = gl_map_iterator (map1); + for (i = 0; i < n; i++) + { + ASSERT (gl_map_iterator_next (&iter1, &key1, &value1)); + pairs_of_map1[i].key = key1; + pairs_of_map1[i].value = value1; + } + ASSERT (!gl_map_iterator_next (&iter1, &key1, &value1)); + gl_map_iterator_free (&iter1); + + if (n > 0) + qsort (pairs_of_map1, n, sizeof (struct pair), cmp_pairs_in_array); + for (i = 0; i < n; i++) + { + ASSERT (pairs_of_map1[i].key == gl_list_get_at (keys, i)); + ASSERT (pairs_of_map1[i].value == gl_list_get_at (values, i)); + } + free (pairs_of_map1); +} + +static void +check_all (gl_map_t map1, gl_list_t keys, gl_list_t values) +{ + check_equals (map1, keys, values); +} + +int +main (int argc, char *argv[]) +{ + gl_map_t map1; + gl_list_t keys; + gl_list_t values; + + /* Allow the user to provide a non-default random seed on the command line. */ + if (argc > 1) + srand (atoi (argv[1])); + + { + size_t initial_size = RANDOM (20); + size_t i; + unsigned int repeat; + + /* Create map1. */ + map1 = gl_map_nx_create_empty (GL_ARRAY_MAP, + string_equals, string_hash, NULL, NULL); + ASSERT (map1 != NULL); + + /* Create keys and values. */ + keys = gl_list_create_empty (GL_ARRAY_LIST, NULL, NULL, NULL, false); + values = gl_list_create_empty (GL_ARRAY_LIST, NULL, NULL, NULL, false); + + check_all (map1, keys, values); + + /* Initialize them. */ + for (i = 0; i < initial_size; i++) + { + const char *key = RANDOM_OBJECT (); + const char *value = RANDOM_OBJECT (); + bool added = gl_map_nx_put (map1, key, value); + size_t index = gl_sortedlist_indexof (keys, (gl_listelement_compar_fn)strcmp, key); + ASSERT (added == (index == (size_t)(-1))); + if (added) + { + gl_sortedlist_add (keys, (gl_listelement_compar_fn)strcmp, key); + index = gl_sortedlist_indexof (keys, (gl_listelement_compar_fn)strcmp, key); + gl_list_add_at (values, index, value); + } + else + gl_list_set_at (values, index, value); + check_all (map1, keys, values); + } + + for (repeat = 0; repeat < 100000; repeat++) + { + unsigned int operation = RANDOM (3); + switch (operation) + { + case 0: + { + const char *key = RANDOM_OBJECT (); + const void *ret = gl_map_get (map1, key); + size_t index = + gl_sortedlist_indexof (keys, (gl_listelement_compar_fn)strcmp, key); + ASSERT (ret + == (index != (size_t)(-1) ? gl_list_get_at (values, index) : NULL)); + } + break; + case 1: + { + const char *key = RANDOM_OBJECT (); + const char *value = RANDOM_OBJECT (); + bool added = gl_map_nx_put (map1, key, value); + size_t index = + gl_sortedlist_indexof (keys, (gl_listelement_compar_fn)strcmp, key); + ASSERT (added == (index == (size_t)(-1))); + if (added) + { + gl_sortedlist_add (keys, (gl_listelement_compar_fn)strcmp, key); + index = gl_sortedlist_indexof (keys, (gl_listelement_compar_fn)strcmp, key); + gl_list_add_at (values, index, value); + } + else + gl_list_set_at (values, index, value); + } + break; + case 2: + { + const char *key = RANDOM_OBJECT (); + bool removed = gl_map_remove (map1, key); + size_t index = + gl_sortedlist_indexof (keys, (gl_listelement_compar_fn)strcmp, key); + ASSERT (removed == (index != (size_t)(-1))); + if (removed) + { + gl_list_remove_at (keys, index); + gl_list_remove_at (values, index); + } + } + break; + } + check_all (map1, keys, values); + } + + gl_map_free (map1); + gl_list_free (keys); + gl_list_free (values); + } + + return 0; +} -- 2.7.4
>From 83824758898696aa9d3825b8d885205233d73812 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Wed, 12 Dec 2018 01:30:25 +0100 Subject: [PATCH 7/8] linkedhash-map: Add tests. * tests/test-linkedhash_map.c: New file. * modules/linkedhash-map-tests: New file. --- ChangeLog | 4 + modules/linkedhash-map-tests | 13 +++ tests/test-linkedhash_map.c | 195 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 212 insertions(+) create mode 100644 modules/linkedhash-map-tests create mode 100644 tests/test-linkedhash_map.c diff --git a/ChangeLog b/ChangeLog index c9ddd7c..9a706f6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2018-12-11 Bruno Haible <br...@clisp.org> + linkedhash-map: Add tests. + * tests/test-linkedhash_map.c: New file. + * modules/linkedhash-map-tests: New file. + array-map: Add tests. * tests/test-array_map.c: New file. * modules/array-map-tests: New file. diff --git a/modules/linkedhash-map-tests b/modules/linkedhash-map-tests new file mode 100644 index 0000000..a21dad3 --- /dev/null +++ b/modules/linkedhash-map-tests @@ -0,0 +1,13 @@ +Files: +tests/test-linkedhash_map.c +tests/macros.h + +Depends-on: +array-map +xalloc + +configure.ac: + +Makefile.am: +TESTS += test-linkedhash_map +check_PROGRAMS += test-linkedhash_map diff --git a/tests/test-linkedhash_map.c b/tests/test-linkedhash_map.c new file mode 100644 index 0000000..780105e --- /dev/null +++ b/tests/test-linkedhash_map.c @@ -0,0 +1,195 @@ +/* Test of map data type implementation. + Copyright (C) 2006-2018 Free Software Foundation, Inc. + Written by Bruno Haible <br...@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> + +#include "gl_linkedhash_map.h" + +#include <stdlib.h> +#include <string.h> + +#include "gl_array_map.h" +#include "xalloc.h" +#include "macros.h" + +static const char *objects[30] = + { + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", + "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "<", ">", "[", "]" + }; + +#define SIZE_BITS (sizeof (size_t) * CHAR_BIT) + +static bool +string_equals (const void *x1, const void *x2) +{ + const char *s1 = x1; + const char *s2 = x2; + return strcmp (s1, s2) == 0; +} + +/* A hash function for NUL-terminated char* strings using + the method described by Bruno Haible. + See https://www.haible.de/bruno/hashfunc.html. */ +static size_t +string_hash (const void *x) +{ + const char *s = x; + size_t h = 0; + + for (; *s; s++) + h = *s + ((h << 9) | (h >> (SIZE_BITS - 9))); + + return h; +} + +#define RANDOM(n) (rand () % (n)) +#define RANDOM_OBJECT() objects[RANDOM (SIZEOF (objects))] + +struct pair +{ + const void *key; + const void *value; +}; + +static int +cmp_pairs_in_array (const void *pairptr1, const void *pairptr2) +{ + const void *key1 = ((struct pair const *)pairptr1)->key; + const void *key2 = ((struct pair const *)pairptr2)->key; + return strcmp ((const char *) key1, (const char *) key2); +} + +static void +check_equals (gl_map_t map1, gl_map_t map2) +{ + size_t n = gl_map_size (map1); + struct pair *pairs_of_map1 = XNMALLOC (n, struct pair); + struct pair *pairs_of_map2 = XNMALLOC (n, struct pair); + + gl_map_iterator_t iter1, iter2; + const void *key1; + const void *value1; + const void *key2; + const void *value2; + size_t i; + + iter1 = gl_map_iterator (map1); + iter2 = gl_map_iterator (map2); + for (i = 0; i < n; i++) + { + ASSERT (gl_map_iterator_next (&iter1, &key1, &value1)); + ASSERT (gl_map_iterator_next (&iter2, &key2, &value2)); + pairs_of_map1[i].key = key1; + pairs_of_map1[i].value = value1; + pairs_of_map2[i].key = key2; + pairs_of_map2[i].value = value2; + } + ASSERT (!gl_map_iterator_next (&iter1, &key1, &value1)); + ASSERT (!gl_map_iterator_next (&iter2, &key2, &value2)); + gl_map_iterator_free (&iter1); + gl_map_iterator_free (&iter2); + + if (n > 0) + { + qsort (pairs_of_map1, n, sizeof (struct pair), cmp_pairs_in_array); + qsort (pairs_of_map2, n, sizeof (struct pair), cmp_pairs_in_array); + } + for (i = 0; i < n; i++) + { + ASSERT (pairs_of_map1[i].key == pairs_of_map2[i].key); + ASSERT (pairs_of_map1[i].value == pairs_of_map2[i].value); + } + free (pairs_of_map2); + free (pairs_of_map1); +} + +static void +check_all (gl_map_t map1, gl_map_t map2) +{ + check_equals (map1, map2); +} + +int +main (int argc, char *argv[]) +{ + gl_map_t map1, map2; + + /* Allow the user to provide a non-default random seed on the command line. */ + if (argc > 1) + srand (atoi (argv[1])); + + { + size_t initial_size = RANDOM (20); + size_t i; + unsigned int repeat; + + /* Create map1. */ + map1 = gl_map_nx_create_empty (GL_ARRAY_MAP, + string_equals, string_hash, NULL, NULL); + ASSERT (map1 != NULL); + + /* Create map2. */ + map2 = gl_map_nx_create_empty (GL_LINKEDHASH_MAP, + string_equals, string_hash, NULL, NULL); + ASSERT (map2 != NULL); + + check_all (map1, map2); + + /* Initialize them. */ + for (i = 0; i < initial_size; i++) + { + const char *key = RANDOM_OBJECT (); + const char *value = RANDOM_OBJECT (); + ASSERT (gl_map_nx_put (map1, key, value) == gl_map_nx_put (map2, key, value)); + check_all (map1, map2); + } + + for (repeat = 0; repeat < 100000; repeat++) + { + unsigned int operation = RANDOM (3); + switch (operation) + { + case 0: + { + const char *key = RANDOM_OBJECT (); + ASSERT (gl_map_get (map1, key) == gl_map_get (map2, key)); + } + break; + case 1: + { + const char *key = RANDOM_OBJECT (); + const char *value = RANDOM_OBJECT (); + ASSERT (gl_map_nx_put (map1, key, value) == gl_map_nx_put (map2, key, value)); + } + break; + case 2: + { + const char *key = RANDOM_OBJECT (); + ASSERT (gl_map_remove (map1, key) == gl_map_remove (map2, key)); + } + break; + } + check_all (map1, map2); + } + + gl_map_free (map1); + gl_map_free (map2); + } + + return 0; +} -- 2.7.4
>From 62932421ced5c8e90865ab46ebd9740d14ef68b8 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Wed, 12 Dec 2018 01:30:56 +0100 Subject: [PATCH 8/8] hash-map: Add tests. * tests/test-hash_map.c: New file. * modules/hash-map-tests: New file. --- ChangeLog | 4 + modules/hash-map-tests | 13 ++++ tests/test-hash_map.c | 195 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 212 insertions(+) create mode 100644 modules/hash-map-tests create mode 100644 tests/test-hash_map.c diff --git a/ChangeLog b/ChangeLog index 9a706f6..795f47c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2018-12-11 Bruno Haible <br...@clisp.org> + hash-map: Add tests. + * tests/test-hash_map.c: New file. + * modules/hash-map-tests: New file. + linkedhash-map: Add tests. * tests/test-linkedhash_map.c: New file. * modules/linkedhash-map-tests: New file. diff --git a/modules/hash-map-tests b/modules/hash-map-tests new file mode 100644 index 0000000..693a026 --- /dev/null +++ b/modules/hash-map-tests @@ -0,0 +1,13 @@ +Files: +tests/test-hash_map.c +tests/macros.h + +Depends-on: +array-map +xalloc + +configure.ac: + +Makefile.am: +TESTS += test-hash_map +check_PROGRAMS += test-hash_map diff --git a/tests/test-hash_map.c b/tests/test-hash_map.c new file mode 100644 index 0000000..9982173 --- /dev/null +++ b/tests/test-hash_map.c @@ -0,0 +1,195 @@ +/* Test of map data type implementation. + Copyright (C) 2006-2018 Free Software Foundation, Inc. + Written by Bruno Haible <br...@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> + +#include "gl_hash_map.h" + +#include <stdlib.h> +#include <string.h> + +#include "gl_array_map.h" +#include "xalloc.h" +#include "macros.h" + +static const char *objects[30] = + { + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", + "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "<", ">", "[", "]" + }; + +#define SIZE_BITS (sizeof (size_t) * CHAR_BIT) + +static bool +string_equals (const void *x1, const void *x2) +{ + const char *s1 = x1; + const char *s2 = x2; + return strcmp (s1, s2) == 0; +} + +/* A hash function for NUL-terminated char* strings using + the method described by Bruno Haible. + See https://www.haible.de/bruno/hashfunc.html. */ +static size_t +string_hash (const void *x) +{ + const char *s = x; + size_t h = 0; + + for (; *s; s++) + h = *s + ((h << 9) | (h >> (SIZE_BITS - 9))); + + return h; +} + +#define RANDOM(n) (rand () % (n)) +#define RANDOM_OBJECT() objects[RANDOM (SIZEOF (objects))] + +struct pair +{ + const void *key; + const void *value; +}; + +static int +cmp_pairs_in_array (const void *pairptr1, const void *pairptr2) +{ + const void *key1 = ((struct pair const *)pairptr1)->key; + const void *key2 = ((struct pair const *)pairptr2)->key; + return strcmp ((const char *) key1, (const char *) key2); +} + +static void +check_equals (gl_map_t map1, gl_map_t map2) +{ + size_t n = gl_map_size (map1); + struct pair *pairs_of_map1 = XNMALLOC (n, struct pair); + struct pair *pairs_of_map2 = XNMALLOC (n, struct pair); + + gl_map_iterator_t iter1, iter2; + const void *key1; + const void *value1; + const void *key2; + const void *value2; + size_t i; + + iter1 = gl_map_iterator (map1); + iter2 = gl_map_iterator (map2); + for (i = 0; i < n; i++) + { + ASSERT (gl_map_iterator_next (&iter1, &key1, &value1)); + ASSERT (gl_map_iterator_next (&iter2, &key2, &value2)); + pairs_of_map1[i].key = key1; + pairs_of_map1[i].value = value1; + pairs_of_map2[i].key = key2; + pairs_of_map2[i].value = value2; + } + ASSERT (!gl_map_iterator_next (&iter1, &key1, &value1)); + ASSERT (!gl_map_iterator_next (&iter2, &key2, &value2)); + gl_map_iterator_free (&iter1); + gl_map_iterator_free (&iter2); + + if (n > 0) + { + qsort (pairs_of_map1, n, sizeof (struct pair), cmp_pairs_in_array); + qsort (pairs_of_map2, n, sizeof (struct pair), cmp_pairs_in_array); + } + for (i = 0; i < n; i++) + { + ASSERT (pairs_of_map1[i].key == pairs_of_map2[i].key); + ASSERT (pairs_of_map1[i].value == pairs_of_map2[i].value); + } + free (pairs_of_map2); + free (pairs_of_map1); +} + +static void +check_all (gl_map_t map1, gl_map_t map2) +{ + check_equals (map1, map2); +} + +int +main (int argc, char *argv[]) +{ + gl_map_t map1, map2; + + /* Allow the user to provide a non-default random seed on the command line. */ + if (argc > 1) + srand (atoi (argv[1])); + + { + size_t initial_size = RANDOM (20); + size_t i; + unsigned int repeat; + + /* Create map1. */ + map1 = gl_map_nx_create_empty (GL_ARRAY_MAP, + string_equals, string_hash, NULL, NULL); + ASSERT (map1 != NULL); + + /* Create map2. */ + map2 = gl_map_nx_create_empty (GL_HASH_MAP, + string_equals, string_hash, NULL, NULL); + ASSERT (map2 != NULL); + + check_all (map1, map2); + + /* Initialize them. */ + for (i = 0; i < initial_size; i++) + { + const char *key = RANDOM_OBJECT (); + const char *value = RANDOM_OBJECT (); + ASSERT (gl_map_nx_put (map1, key, value) == gl_map_nx_put (map2, key, value)); + check_all (map1, map2); + } + + for (repeat = 0; repeat < 100000; repeat++) + { + unsigned int operation = RANDOM (3); + switch (operation) + { + case 0: + { + const char *key = RANDOM_OBJECT (); + ASSERT (gl_map_get (map1, key) == gl_map_get (map2, key)); + } + break; + case 1: + { + const char *key = RANDOM_OBJECT (); + const char *value = RANDOM_OBJECT (); + ASSERT (gl_map_nx_put (map1, key, value) == gl_map_nx_put (map2, key, value)); + } + break; + case 2: + { + const char *key = RANDOM_OBJECT (); + ASSERT (gl_map_remove (map1, key) == gl_map_remove (map2, key)); + } + break; + } + check_all (map1, map2); + } + + gl_map_free (map1); + gl_map_free (map2); + } + + return 0; +} -- 2.7.4