Hi, In gnulib, so far, we have generic containers for the abstract concepts of - list, - ordered set. This proposed series of patches adds a container for - set.
The difference between "ordered set" and "set" is that for ordered set, there is a comparison function that introduces a total order [1], whereas for set, we only have a comparison for equality. While the operations for ordered set are: Operation ARRAY TREE gl_oset_size O(1) O(1) gl_oset_add O(n) O(log n) gl_oset_remove O(n) O(log n) gl_oset_search O(log n) O(log n) gl_oset_search_atleast O(log n) O(log n) gl_oset_iterator O(1) O(log n) gl_oset_iterator_next O(1) O(log n) the ones for a set are: Operation ARRAY LINKEDHASH LINKED HASH gl_set_size O(1) O(1) gl_set_add O(n) O(1) gl_set_remove O(n) O(1) gl_set_search O(n) O(1) gl_set_iterator O(1) O(1) gl_set_iterator_next O(1) O(1) Given that the ARRAY and LINKED (= linked-list) implementations of "set" have the same asymptotic average performance, and LINKED eats more memory and does more memory allocations than ARRAY, I left out the LINKED implementation. This patch series is a preparation for the "map" concept, which I claimed to be desirable [2]. Review and comments welcome! Bruno [1] https://en.wikipedia.org/wiki/Total_order [2] https://lists.gnu.org/archive/html/bug-gnulib/2018-12/msg00012.html
>From b34b0da5f82eb6b0c163820ad9aa0e4e5bfe7590 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Tue, 4 Dec 2018 00:41:24 +0100 Subject: [PATCH 1/8] set: New module. * lib/gl_set.h: New file. * lib/gl_set.c: New file. * lib/gl_oset.h (gl_setelement_dispose_fn): Avoid conflict with gl_set.h. * modules/set: New file. --- ChangeLog | 9 ++ lib/gl_oset.h | 5 +- lib/gl_set.c | 3 + lib/gl_set.h | 280 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ modules/set | 24 +++++ 5 files changed, 320 insertions(+), 1 deletion(-) create mode 100644 lib/gl_set.c create mode 100644 lib/gl_set.h create mode 100644 modules/set diff --git a/ChangeLog b/ChangeLog index 76d6966..1761c1d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2018-12-03 Bruno Haible <br...@clisp.org> + + set: New module. + * lib/gl_set.h: New file. + * lib/gl_set.c: New file. + * lib/gl_oset.h (gl_setelement_dispose_fn): Avoid conflict with + gl_set.h. + * modules/set: New file. + 2018-12-01 Bruno Haible <br...@clisp.org> gnupload: Document short options. diff --git a/lib/gl_oset.h b/lib/gl_oset.h index 5cad161..abe3eb4 100644 --- a/lib/gl_oset.h +++ b/lib/gl_oset.h @@ -77,9 +77,12 @@ extern "C" { NULL denotes pointer comparison. */ typedef int (*gl_setelement_compar_fn) (const void *elt1, const void *elt2); +#ifndef _GL_SETELEMENT_DISPOSE_FN_DEFINED /* Type of function used to dispose an element once it's removed from a set. NULL denotes a no-op. */ typedef void (*gl_setelement_dispose_fn) (const void *elt); +# define _GL_SETELEMENT_DISPOSE_FN_DEFINED 1 +#endif /* Type of function used to compare an element with a threshold. Return true if the element is greater or equal than the threshold. */ @@ -144,7 +147,7 @@ extern bool gl_oset_remove (gl_oset_t set, const void *elt); (But this call does not free the elements of the set.) */ extern void gl_oset_free (gl_oset_t set); -#endif /* End of inline and gl_xlist.h-defined functions. */ +#endif /* End of inline and gl_xoset.h-defined functions. */ /* --------------------- gl_oset_iterator_t Data Type --------------------- */ diff --git a/lib/gl_set.c b/lib/gl_set.c new file mode 100644 index 0000000..e00d202 --- /dev/null +++ b/lib/gl_set.c @@ -0,0 +1,3 @@ +#include <config.h> +#define GL_SET_INLINE _GL_EXTERN_INLINE +#include "gl_set.h" diff --git a/lib/gl_set.h b/lib/gl_set.h new file mode 100644 index 0000000..bc615c3 --- /dev/null +++ b/lib/gl_set.h @@ -0,0 +1,280 @@ +/* Abstract set 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_SET_H +#define _GL_SET_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_SET_INLINE +# define GL_SET_INLINE _GL_INLINE +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +/* gl_set is an abstract set data type. It can contain any number of objects + ('void *' or 'const void *' pointers); the order does not matter. + Duplicates (in the sense of the comparator) are forbidden. + + There are several implementations of this set datatype, optimized for + different operations or for memory. You can start using the simplest set + implementation, GL_ARRAY_SET, 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_set_create + call. + + The implementations are: + GL_ARRAY_SET a growable array + GL_LINKEDHASH_SET a hash table with a linked list + GL_HASH_SET a hash table + + The memory consumption is asymptotically the same: O(1) for every object + in the set. When looking more closely at the average memory consumed + for an object, GL_ARRAY_SET is the most compact representation, then comes + GL_HASH_SET, and GL_LINKEDHASH_SET needs the most memory. + + The guaranteed average performance of the operations is, for a set of + n elements: + + Operation ARRAY LINKEDHASH + HASH + + gl_set_size O(1) O(1) + gl_set_add O(n) O(1) + gl_set_remove O(n) O(1) + gl_set_search O(n) O(1) + gl_set_iterator O(1) O(1) + gl_set_iterator_next O(1) O(1) + */ + +/* --------------------------- gl_set_t Data Type --------------------------- */ + +/* Type of function used to compare two elements. + NULL denotes pointer comparison. */ +typedef bool (*gl_setelement_equals_fn) (const void *elt1, const void *elt2); + +/* Type of function used to compute a hash code. + NULL denotes a function that depends only on the pointer itself. */ +typedef size_t (*gl_setelement_hashcode_fn) (const void *elt); + +#ifndef _GL_SETELEMENT_DISPOSE_FN_DEFINED +/* Type of function used to dispose an element once it's removed from a set. + NULL denotes a no-op. */ +typedef void (*gl_setelement_dispose_fn) (const void *elt); +# define _GL_SETELEMENT_DISPOSE_FN_DEFINED 1 +#endif + +struct gl_set_impl; +/* Type representing an entire set. */ +typedef struct gl_set_impl * gl_set_t; + +struct gl_set_implementation; +/* Type representing a set datatype implementation. */ +typedef const struct gl_set_implementation * gl_set_implementation_t; + +#if 0 /* Unless otherwise specified, these are defined inline below. */ + +/* Create an empty set. + IMPLEMENTATION is one of GL_ARRAY_SET, GL_LINKEDHASH_SET, GL_HASH_SET. + EQUALS_FN is an element comparison function or NULL. + HASHCODE_FN is an element hash code function or NULL. + DISPOSE_FN is an element disposal function or NULL. */ +/* declared in gl_xset.h */ +extern gl_set_t gl_set_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); +/* Likewise. Return NULL upon out-of-memory. */ +extern gl_set_t gl_set_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); + +/* Return the current number of elements in a set. */ +extern size_t gl_set_size (gl_set_t set); + +/* Search whether an element is already in the set. + Return true if found, or false if not present in the set. */ +extern bool gl_set_search (gl_set_t set, const void *elt); + +/* Add an element to a set. + Return true if it was not already in the set and added, false otherwise. */ +/* declared in gl_xset.h */ +extern bool gl_set_add (gl_set_t set, const void *elt); +/* Likewise. Return -1 upon out-of-memory. */ +extern int gl_set_nx_add (gl_set_t set, const void *elt) +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) + __attribute__ ((__warn_unused_result__)) +#endif + ; + +/* Remove an element from a set. + Return true if it was found and removed. */ +extern bool gl_set_remove (gl_set_t set, const void *elt); + +/* Free an entire set. + (But this call does not free the elements of the set.) */ +extern void gl_set_free (gl_set_t set); + +#endif /* End of inline and gl_xset.h-defined functions. */ + +/* ---------------------- gl_set_iterator_t Data Type ---------------------- */ + +/* Functions for iterating through a set. + Note: Iterating through a set of type GL_HASH_SET returns the elements in an + unpredictable order. If you need a predictable order, use GL_LINKEDHASH_SET + instead of GL_HASH_SET. */ + +/* Type of an iterator that traverses a set. + 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_set_iterator_next. */ + const struct gl_set_implementation *vtable; + /* For detecting whether the last returned element was removed. */ + gl_set_t set; + size_t count; + /* Other, implementation-private fields. */ + void *p; void *q; + size_t i; size_t j; +} gl_set_iterator_t; + +#if 0 /* These are defined inline below. */ + +/* Create an iterator traversing a set. + The set's contents must not be modified while the iterator is in use, + except for removing the last returned element. */ +extern gl_set_iterator_t gl_set_iterator (gl_set_t set); + +/* If there is a next element, store the next element in *ELTP, advance the + iterator and return true. Otherwise, return false. */ +extern bool gl_set_iterator_next (gl_set_iterator_t *iterator, + const void **eltp); + +/* Free an iterator. */ +extern void gl_set_iterator_free (gl_set_iterator_t *iterator); + +#endif /* End of inline functions. */ + +/* ------------------------ Implementation Details ------------------------ */ + +struct gl_set_implementation +{ + /* gl_set_t functions. */ + gl_set_t (*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); + size_t (*size) (gl_set_t set); + bool (*search) (gl_set_t set, const void *elt); + int (*nx_add) (gl_set_t set, const void *elt); + bool (*remove_elt) (gl_set_t set, const void *elt); + void (*set_free) (gl_set_t set); + /* gl_set_iterator_t functions. */ + gl_set_iterator_t (*iterator) (gl_set_t set); + bool (*iterator_next) (gl_set_iterator_t *iterator, const void **eltp); + void (*iterator_free) (gl_set_iterator_t *iterator); +}; + +struct gl_set_impl_base +{ + const struct gl_set_implementation *vtable; + gl_setelement_equals_fn equals_fn; + gl_setelement_dispose_fn dispose_fn; +}; + +/* Define all functions of this file as accesses to the + struct gl_set_implementation. */ + +GL_SET_INLINE gl_set_t +gl_set_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) +{ + return implementation->nx_create_empty (implementation, equals_fn, + hashcode_fn, dispose_fn); +} + +GL_SET_INLINE size_t +gl_set_size (gl_set_t set) +{ + return ((const struct gl_set_impl_base *) set)->vtable->size (set); +} + +GL_SET_INLINE bool +gl_set_search (gl_set_t set, const void *elt) +{ + return ((const struct gl_set_impl_base *) set)->vtable->search (set, elt); +} + +GL_SET_INLINE int +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) + __attribute__ ((__warn_unused_result__)) +#endif +gl_set_nx_add (gl_set_t set, const void *elt) +{ + return ((const struct gl_set_impl_base *) set)->vtable->nx_add (set, elt); +} + +GL_SET_INLINE bool +gl_set_remove (gl_set_t set, const void *elt) +{ + return ((const struct gl_set_impl_base *) set)->vtable->remove_elt (set, elt); +} + +GL_SET_INLINE void +gl_set_free (gl_set_t set) +{ + ((const struct gl_set_impl_base *) set)->vtable->set_free (set); +} + +GL_SET_INLINE gl_set_iterator_t +gl_set_iterator (gl_set_t set) +{ + return ((const struct gl_set_impl_base *) set)->vtable->iterator (set); +} + +GL_SET_INLINE bool +gl_set_iterator_next (gl_set_iterator_t *iterator, const void **eltp) +{ + return iterator->vtable->iterator_next (iterator, eltp); +} + +GL_SET_INLINE void +gl_set_iterator_free (gl_set_iterator_t *iterator) +{ + iterator->vtable->iterator_free (iterator); +} + +#ifdef __cplusplus +} +#endif + +_GL_INLINE_HEADER_END + +#endif /* _GL_SET_H */ diff --git a/modules/set b/modules/set new file mode 100644 index 0000000..29cec74 --- /dev/null +++ b/modules/set @@ -0,0 +1,24 @@ +Description: +Abstract set data type. + +Files: +lib/gl_set.h +lib/gl_set.c + +Depends-on: +extern-inline +stdbool + +configure.ac: + +Makefile.am: +lib_SOURCES += gl_set.h gl_set.c + +Include: +"gl_set.h" + +License: +GPL + +Maintainer: +all -- 2.7.4
>From 21350bc5a1291e0a2ce9442b60463c0f9797c4b9 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Tue, 4 Dec 2018 00:43:22 +0100 Subject: [PATCH 2/8] array-set: New module. * lib/gl_array_set.h: New file. * lib/gl_array_set.c: New file. * modules/array-set: New file. --- ChangeLog | 7 ++ lib/gl_array_set.c | 256 +++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/gl_array_set.h | 34 +++++++ modules/array-set | 24 +++++ 4 files changed, 321 insertions(+) create mode 100644 lib/gl_array_set.c create mode 100644 lib/gl_array_set.h create mode 100644 modules/array-set diff --git a/ChangeLog b/ChangeLog index 1761c1d..a74e9a2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,12 @@ 2018-12-03 Bruno Haible <br...@clisp.org> + array-set: New module. + * lib/gl_array_set.h: New file. + * lib/gl_array_set.c: New file. + * modules/array-set: New file. + +2018-12-03 Bruno Haible <br...@clisp.org> + set: New module. * lib/gl_set.h: New file. * lib/gl_set.c: New file. diff --git a/lib/gl_array_set.c b/lib/gl_array_set.c new file mode 100644 index 0000000..dd77ef1 --- /dev/null +++ b/lib/gl_array_set.c @@ -0,0 +1,256 @@ +/* Set 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_set.h" + +#include <stdlib.h> + +/* Checked size_t computations. */ +#include "xsize.h" + +#ifndef uintptr_t +# define uintptr_t unsigned long +#endif + +/* --------------------------- gl_set_t Data Type --------------------------- */ + +/* Concrete gl_set_impl type, valid for this file only. */ +struct gl_set_impl +{ + struct gl_set_impl_base base; + /* An array of ALLOCATED elements, of which the first COUNT are used. + 0 <= COUNT <= ALLOCATED. */ + const void **elements; + size_t count; + size_t allocated; +}; + +static gl_set_t +gl_array_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->elements = NULL; + set->count = 0; + set->allocated = 0; + + return set; +} + +static size_t +gl_array_size (gl_set_t set) +{ + return set->count; +} + +static bool +gl_array_search (gl_set_t set, const void *elt) +{ + size_t count = set->count; + + if (count > 0) + { + gl_setelement_equals_fn equals = set->base.equals_fn; + const void **elements = set->elements; + size_t i; + + for (i = 0; i < count; i++) + if (equals != NULL ? equals (elements[i], elt) : elements[i] == elt) + return true; + } + return false; +} + +/* Ensure that set->allocated > set->count. + Return 0 upon success, -1 upon out-of-memory. */ +static int +grow (gl_set_t set) +{ + size_t new_allocated; + size_t memory_size; + const void **memory; + + new_allocated = xtimes (set->allocated, 2); + new_allocated = xsum (new_allocated, 1); + memory_size = xtimes (new_allocated, sizeof (const void *)); + if (size_overflow_p (memory_size)) + /* Overflow, would lead to out of memory. */ + return -1; + memory = (const void **) realloc (set->elements, memory_size); + if (memory == NULL) + /* Out of memory. */ + return -1; + set->elements = memory; + set->allocated = new_allocated; + return 0; +} + +static int +gl_array_nx_add (gl_set_t set, const void *elt) +{ + if (gl_array_search (set, elt)) + return 0; + else + { + size_t count = set->count; + + if (count == set->allocated) + if (grow (set) < 0) + return -1; + set->elements[count] = elt; + set->count = count + 1; + return 1; + } +} + +/* Remove the element at the given position, + 0 <= position < gl_set_size (set). */ +static void +gl_array_remove_at (gl_set_t set, size_t position) +{ + size_t count = set->count; + const void **elements = set->elements; + size_t i; + + if (set->base.dispose_fn != NULL) + set->base.dispose_fn (elements[position]); + for (i = position + 1; i < count; i++) + elements[i - 1] = elements[i]; + set->count = count - 1; +} + +static bool +gl_array_remove (gl_set_t set, const void *elt) +{ + size_t count = set->count; + + if (count > 0) + { + gl_setelement_equals_fn equals = set->base.equals_fn; + const void **elements = set->elements; + size_t i; + + for (i = 0; i < count; i++) + if (equals != NULL ? equals (elements[i], elt) : elements[i] == elt) + { + gl_array_remove_at (set, i); + return true; + } + } + return false; +} + +static void +gl_array_free (gl_set_t set) +{ + if (set->elements != NULL) + { + if (set->base.dispose_fn != NULL) + { + size_t count = set->count; + + if (count > 0) + { + gl_setelement_dispose_fn dispose = set->base.dispose_fn; + const void **elements = set->elements; + + do + dispose (*elements++); + while (--count > 0); + } + } + free (set->elements); + } + free (set); +} + +/* ---------------------- gl_set_iterator_t Data Type ---------------------- */ + +static gl_set_iterator_t +gl_array_iterator (gl_set_t set) +{ + gl_set_iterator_t result; + + result.vtable = set->base.vtable; + result.set = set; + result.count = set->count; + result.p = set->elements + 0; + result.q = set->elements + set->count; +#if defined GCC_LINT || defined lint + result.i = 0; + result.j = 0; +#endif + + return result; +} + +static bool +gl_array_iterator_next (gl_set_iterator_t *iterator, const void **eltp) +{ + gl_set_t set = iterator->set; + if (iterator->count != set->count) + { + if (iterator->count != set->count + 1) + /* Concurrent modifications were done on the set. */ + abort (); + /* The last returned element was removed. */ + iterator->count--; + iterator->p = (const void **) iterator->p - 1; + iterator->q = (const void **) iterator->q - 1; + } + if (iterator->p < iterator->q) + { + const void **p = (const void **) iterator->p; + *eltp = *p; + iterator->p = p + 1; + return true; + } + else + return false; +} + +static void +gl_array_iterator_free (gl_set_iterator_t *iterator) +{ +} + + +const struct gl_set_implementation gl_array_set_implementation = + { + gl_array_nx_create_empty, + gl_array_size, + gl_array_search, + gl_array_nx_add, + gl_array_remove, + gl_array_free, + gl_array_iterator, + gl_array_iterator_next, + gl_array_iterator_free + }; diff --git a/lib/gl_array_set.h b/lib/gl_array_set.h new file mode 100644 index 0000000..ea9fea3 --- /dev/null +++ b/lib/gl_array_set.h @@ -0,0 +1,34 @@ +/* Set 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_SET_H +#define _GL_ARRAY_SET_H + +#include "gl_set.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const struct gl_set_implementation gl_array_set_implementation; +#define GL_ARRAY_SET &gl_array_set_implementation + +#ifdef __cplusplus +} +#endif + +#endif /* _GL_ARRAY_SET_H */ diff --git a/modules/array-set b/modules/array-set new file mode 100644 index 0000000..bb03425 --- /dev/null +++ b/modules/array-set @@ -0,0 +1,24 @@ +Description: +Set data type implemented by an array. + +Files: +lib/gl_array_set.h +lib/gl_array_set.c + +Depends-on: +set +xsize + +configure.ac: + +Makefile.am: +lib_SOURCES += gl_array_set.h gl_array_set.c + +Include: +"gl_array_set.h" + +License: +GPL + +Maintainer: +all -- 2.7.4
>From a3a32aa9cee0a7224b9cd21bf251ac46dd4e4f83 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Tue, 4 Dec 2018 00:53:12 +0100 Subject: [PATCH 3/8] linkedhash-set: New module. * lib/gl_linkedhash_set.h: New file. * lib/gl_linkedhash_set.c: New file. * lib/gl_anyhash_set1.h: New file, based on lib/gl_anyhash_list1.h. * lib/gl_anyhash_set2.h: New file, based on lib/gl_anyhash_list2.h. * lib/gl_anyhash_primes.h: New file, extracted from lib/gl_anyhash_list2.h. * lib/gl_anyhash_list2.h: Include it. (primes, next_prime): Remove definitions. * modules/linkedhash-set: New file. * modules/avltreehash-list (Files): Add lib/gl_anyhash_primes.h. (Makefile.am): Add gl_anyhash_primes.h to lib_SOURCES. * modules/linkedhash-list (Files): Add lib/gl_anyhash_primes.h. (Makefile.am): Add gl_anyhash_primes.h to lib_SOURCES. * modules/rbtreehash-list (Files): Add lib/gl_anyhash_primes.h. (Makefile.am): Add gl_anyhash_primes.h to lib_SOURCES. --- ChangeLog | 19 +++ lib/gl_anyhash_list2.h | 71 +---------- lib/gl_anyhash_primes.h | 87 +++++++++++++ lib/gl_anyhash_set1.h | 27 ++++ lib/gl_anyhash_set2.h | 79 ++++++++++++ lib/gl_linkedhash_set.c | 313 +++++++++++++++++++++++++++++++++++++++++++++++ lib/gl_linkedhash_set.h | 34 +++++ modules/avltreehash-list | 3 +- modules/linkedhash-list | 3 +- modules/linkedhash-set | 28 +++++ modules/rbtreehash-list | 3 +- 11 files changed, 594 insertions(+), 73 deletions(-) create mode 100644 lib/gl_anyhash_primes.h create mode 100644 lib/gl_anyhash_set1.h create mode 100644 lib/gl_anyhash_set2.h create mode 100644 lib/gl_linkedhash_set.c create mode 100644 lib/gl_linkedhash_set.h create mode 100644 modules/linkedhash-set diff --git a/ChangeLog b/ChangeLog index a74e9a2..5f0ae48 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,24 @@ 2018-12-03 Bruno Haible <br...@clisp.org> + linkedhash-set: New module. + * lib/gl_linkedhash_set.h: New file. + * lib/gl_linkedhash_set.c: New file. + * lib/gl_anyhash_set1.h: New file, based on lib/gl_anyhash_list1.h. + * lib/gl_anyhash_set2.h: New file, based on lib/gl_anyhash_list2.h. + * lib/gl_anyhash_primes.h: New file, extracted from + lib/gl_anyhash_list2.h. + * lib/gl_anyhash_list2.h: Include it. + (primes, next_prime): Remove definitions. + * modules/linkedhash-set: New file. + * modules/avltreehash-list (Files): Add lib/gl_anyhash_primes.h. + (Makefile.am): Add gl_anyhash_primes.h to lib_SOURCES. + * modules/linkedhash-list (Files): Add lib/gl_anyhash_primes.h. + (Makefile.am): Add gl_anyhash_primes.h to lib_SOURCES. + * modules/rbtreehash-list (Files): Add lib/gl_anyhash_primes.h. + (Makefile.am): Add gl_anyhash_primes.h to lib_SOURCES. + +2018-12-03 Bruno Haible <br...@clisp.org> + array-set: New module. * lib/gl_array_set.h: New file. * lib/gl_array_set.c: New file. diff --git a/lib/gl_anyhash_list2.h b/lib/gl_anyhash_list2.h index dac8bb5..d1a5dfb 100644 --- a/lib/gl_anyhash_list2.h +++ b/lib/gl_anyhash_list2.h @@ -18,76 +18,7 @@ /* Common code of gl_linkedhash_list.c, gl_avltreehash_list.c, gl_rbtreehash_list.c. */ -/* Array of primes, approximately in steps of factor 1.2. - This table was computed by executing the Common Lisp expression - (dotimes (i 244) (format t "nextprime(~D)~%" (ceiling (expt 1.2d0 i)))) - and feeding the result to PARI/gp. */ -static const size_t primes[] = - { - 11, 13, 17, 19, 23, 29, 37, 41, 47, 59, 67, 83, 97, 127, 139, 167, 199, - 239, 293, 347, 419, 499, 593, 709, 853, 1021, 1229, 1471, 1777, 2129, 2543, - 3049, 3659, 4391, 5273, 6323, 7589, 9103, 10937, 13109, 15727, 18899, - 22651, 27179, 32609, 39133, 46957, 56359, 67619, 81157, 97369, 116849, - 140221, 168253, 201907, 242309, 290761, 348889, 418667, 502409, 602887, - 723467, 868151, 1041779, 1250141, 1500181, 1800191, 2160233, 2592277, - 3110741, 3732887, 4479463, 5375371, 6450413, 7740517, 9288589, 11146307, - 13375573, 16050689, 19260817, 23112977, 27735583, 33282701, 39939233, - 47927081, 57512503, 69014987, 82818011, 99381577, 119257891, 143109469, - 171731387, 206077643, 247293161, 296751781, 356102141, 427322587, - 512787097, 615344489, 738413383, 886096061, 1063315271, 1275978331, - 1531174013, 1837408799, 2204890543UL, 2645868653UL, 3175042391UL, - 3810050851UL, -#if SIZE_MAX > 4294967295UL - 4572061027UL, 5486473229UL, 6583767889UL, 7900521449UL, 9480625733UL, - 11376750877UL, 13652101063UL, 16382521261UL, 19659025513UL, 23590830631UL, - 28308996763UL, 33970796089UL, 40764955463UL, 48917946377UL, 58701535657UL, - 70441842749UL, 84530211301UL, 101436253561UL, 121723504277UL, - 146068205131UL, 175281846149UL, 210338215379UL, 252405858521UL, - 302887030151UL, 363464436191UL, 436157323417UL, 523388788231UL, - 628066545713UL, 753679854847UL, 904415825857UL, 1085298991109UL, - 1302358789181UL, 1562830547009UL, 1875396656429UL, 2250475987709UL, - 2700571185239UL, 3240685422287UL, 3888822506759UL, 4666587008147UL, - 5599904409713UL, 6719885291641UL, 8063862349969UL, 9676634819959UL, - 11611961783951UL, 13934354140769UL, 16721224968907UL, 20065469962669UL, - 24078563955191UL, 28894276746229UL, 34673132095507UL, 41607758514593UL, - 49929310217531UL, 59915172260971UL, 71898206713183UL, 86277848055823UL, - 103533417666967UL, 124240101200359UL, 149088121440451UL, 178905745728529UL, - 214686894874223UL, 257624273849081UL, 309149128618903UL, 370978954342639UL, - 445174745211143UL, 534209694253381UL, 641051633104063UL, 769261959724877UL, - 923114351670013UL, 1107737222003791UL, 1329284666404567UL, - 1595141599685509UL, 1914169919622551UL, 2297003903547091UL, - 2756404684256459UL, 3307685621107757UL, 3969222745329323UL, - 4763067294395177UL, 5715680753274209UL, 6858816903929113UL, - 8230580284714831UL, 9876696341657791UL, 11852035609989371UL, - 14222442731987227UL, 17066931278384657UL, 20480317534061597UL, - 24576381040873903UL, 29491657249048679UL, 35389988698858471UL, - 42467986438630267UL, 50961583726356109UL, 61153900471627387UL, - 73384680565952851UL, 88061616679143347UL, 105673940014972061UL, - 126808728017966413UL, 152170473621559703UL, 182604568345871671UL, - 219125482015045997UL, 262950578418055169UL, 315540694101666193UL, - 378648832921999397UL, 454378599506399233UL, 545254319407679131UL, - 654305183289214771UL, 785166219947057701UL, 942199463936469157UL, - 1130639356723763129UL, 1356767228068515623UL, 1628120673682218619UL, - 1953744808418662409UL, 2344493770102394881UL, 2813392524122873857UL, - 3376071028947448339UL, 4051285234736937517UL, 4861542281684325481UL, - 5833850738021191727UL, 7000620885625427969UL, 8400745062750513217UL, - 10080894075300616261UL, 12097072890360739951UL, 14516487468432885797UL, - 17419784962119465179UL, -#endif - SIZE_MAX /* sentinel, to ensure the search terminates */ - }; - -/* Return a suitable prime >= ESTIMATE. */ -static size_t -next_prime (size_t estimate) -{ - size_t i; - - for (i = 0; i < sizeof (primes) / sizeof (primes[0]); i++) - if (primes[i] >= estimate) - return primes[i]; - return SIZE_MAX; /* not a prime, but better than nothing */ -} +#include "gl_anyhash_primes.h" /* Resize the hash table with a new estimated size. */ static void diff --git a/lib/gl_anyhash_primes.h b/lib/gl_anyhash_primes.h new file mode 100644 index 0000000..79d2695 --- /dev/null +++ b/lib/gl_anyhash_primes.h @@ -0,0 +1,87 @@ +/* Table of primes, for use by hash tables. + Copyright (C) 2006, 2009-2018 Free Software Foundation, Inc. + Written by Bruno Haible <br...@clisp.org>, 2006. + + 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/>. */ + +/* Array of primes, approximately in steps of factor 1.2. + This table was computed by executing the Common Lisp expression + (dotimes (i 244) (format t "nextprime(~D)~%" (ceiling (expt 1.2d0 i)))) + and feeding the result to PARI/gp. */ +static const size_t primes[] = + { + 11, 13, 17, 19, 23, 29, 37, 41, 47, 59, 67, 83, 97, 127, 139, 167, 199, + 239, 293, 347, 419, 499, 593, 709, 853, 1021, 1229, 1471, 1777, 2129, 2543, + 3049, 3659, 4391, 5273, 6323, 7589, 9103, 10937, 13109, 15727, 18899, + 22651, 27179, 32609, 39133, 46957, 56359, 67619, 81157, 97369, 116849, + 140221, 168253, 201907, 242309, 290761, 348889, 418667, 502409, 602887, + 723467, 868151, 1041779, 1250141, 1500181, 1800191, 2160233, 2592277, + 3110741, 3732887, 4479463, 5375371, 6450413, 7740517, 9288589, 11146307, + 13375573, 16050689, 19260817, 23112977, 27735583, 33282701, 39939233, + 47927081, 57512503, 69014987, 82818011, 99381577, 119257891, 143109469, + 171731387, 206077643, 247293161, 296751781, 356102141, 427322587, + 512787097, 615344489, 738413383, 886096061, 1063315271, 1275978331, + 1531174013, 1837408799, 2204890543UL, 2645868653UL, 3175042391UL, + 3810050851UL, +#if SIZE_MAX > 4294967295UL + 4572061027UL, 5486473229UL, 6583767889UL, 7900521449UL, 9480625733UL, + 11376750877UL, 13652101063UL, 16382521261UL, 19659025513UL, 23590830631UL, + 28308996763UL, 33970796089UL, 40764955463UL, 48917946377UL, 58701535657UL, + 70441842749UL, 84530211301UL, 101436253561UL, 121723504277UL, + 146068205131UL, 175281846149UL, 210338215379UL, 252405858521UL, + 302887030151UL, 363464436191UL, 436157323417UL, 523388788231UL, + 628066545713UL, 753679854847UL, 904415825857UL, 1085298991109UL, + 1302358789181UL, 1562830547009UL, 1875396656429UL, 2250475987709UL, + 2700571185239UL, 3240685422287UL, 3888822506759UL, 4666587008147UL, + 5599904409713UL, 6719885291641UL, 8063862349969UL, 9676634819959UL, + 11611961783951UL, 13934354140769UL, 16721224968907UL, 20065469962669UL, + 24078563955191UL, 28894276746229UL, 34673132095507UL, 41607758514593UL, + 49929310217531UL, 59915172260971UL, 71898206713183UL, 86277848055823UL, + 103533417666967UL, 124240101200359UL, 149088121440451UL, 178905745728529UL, + 214686894874223UL, 257624273849081UL, 309149128618903UL, 370978954342639UL, + 445174745211143UL, 534209694253381UL, 641051633104063UL, 769261959724877UL, + 923114351670013UL, 1107737222003791UL, 1329284666404567UL, + 1595141599685509UL, 1914169919622551UL, 2297003903547091UL, + 2756404684256459UL, 3307685621107757UL, 3969222745329323UL, + 4763067294395177UL, 5715680753274209UL, 6858816903929113UL, + 8230580284714831UL, 9876696341657791UL, 11852035609989371UL, + 14222442731987227UL, 17066931278384657UL, 20480317534061597UL, + 24576381040873903UL, 29491657249048679UL, 35389988698858471UL, + 42467986438630267UL, 50961583726356109UL, 61153900471627387UL, + 73384680565952851UL, 88061616679143347UL, 105673940014972061UL, + 126808728017966413UL, 152170473621559703UL, 182604568345871671UL, + 219125482015045997UL, 262950578418055169UL, 315540694101666193UL, + 378648832921999397UL, 454378599506399233UL, 545254319407679131UL, + 654305183289214771UL, 785166219947057701UL, 942199463936469157UL, + 1130639356723763129UL, 1356767228068515623UL, 1628120673682218619UL, + 1953744808418662409UL, 2344493770102394881UL, 2813392524122873857UL, + 3376071028947448339UL, 4051285234736937517UL, 4861542281684325481UL, + 5833850738021191727UL, 7000620885625427969UL, 8400745062750513217UL, + 10080894075300616261UL, 12097072890360739951UL, 14516487468432885797UL, + 17419784962119465179UL, +#endif + SIZE_MAX /* sentinel, to ensure the search terminates */ + }; + +/* Return a suitable prime >= ESTIMATE. */ +static size_t +next_prime (size_t estimate) +{ + size_t i; + + for (i = 0; i < sizeof (primes) / sizeof (primes[0]); i++) + if (primes[i] >= estimate) + return primes[i]; + return SIZE_MAX; /* not a prime, but better than nothing */ +} diff --git a/lib/gl_anyhash_set1.h b/lib/gl_anyhash_set1.h new file mode 100644 index 0000000..0816a45 --- /dev/null +++ b/lib/gl_anyhash_set1.h @@ -0,0 +1,27 @@ +/* Set data type implemented by a hash table with another list. + 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/>. */ + +/* Common code of + gl_linkedhash_set.c, gl_hash_set.c. */ + +/* Hash table entry. */ +struct gl_hash_entry +{ + struct gl_hash_entry *hash_next; /* chain of entries in same bucket */ + size_t hashcode; /* cache of the hash code of the value */ +}; +typedef struct gl_hash_entry * gl_hash_entry_t; diff --git a/lib/gl_anyhash_set2.h b/lib/gl_anyhash_set2.h new file mode 100644 index 0000000..98dc0cb --- /dev/null +++ b/lib/gl_anyhash_set2.h @@ -0,0 +1,79 @@ +/* Set data type implemented by a hash table with another list. + 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/>. */ + +/* Common code of + gl_linkedhash_set.c, gl_hash_set.c. */ + +#include "gl_anyhash_primes.h" + +/* Resize the hash table with a new estimated size. */ +static void +hash_resize (gl_set_t set, size_t estimate) +{ + size_t new_size = next_prime (estimate); + + if (new_size > set->table_size) + { + gl_hash_entry_t *old_table = set->table; + /* Allocate the new table. */ + gl_hash_entry_t *new_table; + size_t i; + + if (size_overflow_p (xtimes (new_size, sizeof (gl_hash_entry_t)))) + goto fail; + new_table = + (gl_hash_entry_t *) calloc (new_size, sizeof (gl_hash_entry_t)); + if (new_table == NULL) + goto fail; + + /* Iterate through the entries of the old table. */ + for (i = set->table_size; i > 0; ) + { + gl_hash_entry_t node = old_table[--i]; + + while (node != NULL) + { + gl_hash_entry_t next = node->hash_next; + /* Add the entry to the new table. */ + size_t bucket = node->hashcode % new_size; + node->hash_next = new_table[bucket]; + new_table[bucket] = node; + + node = next; + } + } + + set->table = new_table; + set->table_size = new_size; + free (old_table); + } + return; + + fail: + /* Just continue without resizing the table. */ + return; +} + +/* Resize the hash table if needed, after set->count was incremented. */ +static void +hash_resize_after_add (gl_set_t set) +{ + size_t count = set->count; + size_t estimate = xsum (count, count / 2); /* 1.5 * count */ + if (estimate > set->table_size) + hash_resize (set, estimate); +} diff --git a/lib/gl_linkedhash_set.c b/lib/gl_linkedhash_set.c new file mode 100644 index 0000000..9f2d730 --- /dev/null +++ b/lib/gl_linkedhash_set.c @@ -0,0 +1,313 @@ +/* Set 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_set.h" + +#include <stdint.h> /* for SIZE_MAX */ +#include <stdlib.h> + +#include "xsize.h" + +#ifndef uintptr_t +# define uintptr_t unsigned long +#endif + +/* --------------------------- gl_set_t Data Type --------------------------- */ + +#include "gl_anyhash_set1.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 *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; + /* 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; +}; + +#include "gl_anyhash_set2.h" + +/* If the symbol SIGNAL_SAFE_SET is defined, the code is compiled in such + a way that a gl_set_t data structure may be used from within a signal + handler. The operations allowed in the signal handler are: + gl_set_iterator, gl_set_iterator_next, gl_set_iterator_free. + The set and node fields that are therefore accessed from the signal handler + are: + set->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_SET +# define ASYNCSAFE(type) *(type volatile *)& +#else +# define ASYNCSAFE(type) +#endif + +/* --------------------------- gl_set_t Data Type --------------------------- */ + +static gl_set_t +gl_linkedhash_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->root.next = &set->root; + set->root.prev = &set->root; + set->count = 0; + + return set; + + fail: + free (set); + return NULL; +} + +static size_t _GL_ATTRIBUTE_PURE +gl_linkedhash_size (gl_set_t set) +{ + return set->count; +} + +static bool _GL_ATTRIBUTE_PURE +gl_linkedhash_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_linkedhash_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; + + ASYNCSAFE(const void *) 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. */ + ASYNCSAFE(gl_list_node_t) node->next = &set->root; + node->prev = set->root.prev; + ASYNCSAFE(gl_list_node_t) node->prev->next = node; + set->root.prev = node; + set->count++; + + hash_resize_after_add (set); + + return 1; +} + +static bool +gl_linkedhash_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 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; + } + set->count--; + + if (set->base.dispose_fn != NULL) + set->base.dispose_fn (node->value); + free (node); + return true; + } + } + + return false; +} + +static void +gl_linkedhash_free (gl_set_t set) +{ + gl_setelement_dispose_fn dispose = set->base.dispose_fn; + gl_list_node_t node; + + for (node = set->root.next; node != &set->root; ) + { + gl_list_node_t next = node->next; + if (dispose != NULL) + dispose (node->value); + free (node); + node = next; + } + free (set->table); + free (set); +} + +/* ---------------------- gl_set_iterator_t Data Type ---------------------- */ + +/* Iterate through the list, not through the hash buckets, so that the order + in which the elements are returned is predictable. */ + +static gl_set_iterator_t +gl_linkedhash_iterator (gl_set_t set) +{ + gl_set_iterator_t result; + + result.vtable = set->base.vtable; + result.set = set; + result.p = set->root.next; + result.q = &set->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_set_iterator_t *iterator, const void **eltp) +{ + if (iterator->p != iterator->q) + { + gl_list_node_t node = (gl_list_node_t) iterator->p; + *eltp = node->value; + iterator->p = node->next; + return true; + } + else + return false; +} + +static void +gl_linkedhash_iterator_free (gl_set_iterator_t *iterator) +{ +} + + +const struct gl_set_implementation gl_linkedhash_set_implementation = + { + gl_linkedhash_nx_create_empty, + gl_linkedhash_size, + gl_linkedhash_search, + gl_linkedhash_nx_add, + gl_linkedhash_remove, + gl_linkedhash_free, + gl_linkedhash_iterator, + gl_linkedhash_iterator_next, + gl_linkedhash_iterator_free + }; diff --git a/lib/gl_linkedhash_set.h b/lib/gl_linkedhash_set.h new file mode 100644 index 0000000..5aacd4f --- /dev/null +++ b/lib/gl_linkedhash_set.h @@ -0,0 +1,34 @@ +/* Set 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_SET_H +#define _GL_LINKEDHASH_SET_H + +#include "gl_set.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const struct gl_set_implementation gl_linkedhash_set_implementation; +#define GL_LINKEDHASH_SET &gl_linkedhash_set_implementation + +#ifdef __cplusplus +} +#endif + +#endif /* _GL_LINKEDHASH_SET_H */ diff --git a/modules/avltreehash-list b/modules/avltreehash-list index a7aa75e..afd4b25 100644 --- a/modules/avltreehash-list +++ b/modules/avltreehash-list @@ -6,6 +6,7 @@ lib/gl_avltreehash_list.h lib/gl_avltreehash_list.c lib/gl_anyhash_list1.h lib/gl_anyhash_list2.h +lib/gl_anyhash_primes.h lib/gl_anyavltree_list1.h lib/gl_anyavltree_list2.h lib/gl_anytree_list1.h @@ -23,7 +24,7 @@ xsize configure.ac: Makefile.am: -lib_SOURCES += gl_avltreehash_list.h gl_avltreehash_list.c gl_anyhash_list1.h gl_anyhash_list2.h gl_anyavltree_list1.h gl_anyavltree_list2.h gl_anytree_list1.h gl_anytree_list2.h gl_anytreehash_list1.h gl_anytreehash_list2.h +lib_SOURCES += gl_avltreehash_list.h gl_avltreehash_list.c gl_anyhash_list1.h gl_anyhash_list2.h gl_anyhash_primes.h gl_anyavltree_list1.h gl_anyavltree_list2.h gl_anytree_list1.h gl_anytree_list2.h gl_anytreehash_list1.h gl_anytreehash_list2.h Include: "gl_avltreehash_list.h" diff --git a/modules/linkedhash-list b/modules/linkedhash-list index f601a00..35de8c4 100644 --- a/modules/linkedhash-list +++ b/modules/linkedhash-list @@ -6,6 +6,7 @@ lib/gl_linkedhash_list.h lib/gl_linkedhash_list.c lib/gl_anyhash_list1.h lib/gl_anyhash_list2.h +lib/gl_anyhash_primes.h lib/gl_anylinked_list1.h lib/gl_anylinked_list2.h @@ -17,7 +18,7 @@ xsize configure.ac: Makefile.am: -lib_SOURCES += gl_linkedhash_list.h gl_linkedhash_list.c gl_anyhash_list1.h gl_anyhash_list2.h gl_anylinked_list1.h gl_anylinked_list2.h +lib_SOURCES += gl_linkedhash_list.h gl_linkedhash_list.c gl_anyhash_list1.h gl_anyhash_list2.h gl_anyhash_primes.h gl_anylinked_list1.h gl_anylinked_list2.h Include: "gl_linkedhash_list.h" diff --git a/modules/linkedhash-set b/modules/linkedhash-set new file mode 100644 index 0000000..5ca2544 --- /dev/null +++ b/modules/linkedhash-set @@ -0,0 +1,28 @@ +Description: +Set data type implemented by a hash table with a linked list. + +Files: +lib/gl_linkedhash_set.h +lib/gl_linkedhash_set.c +lib/gl_anyhash_set1.h +lib/gl_anyhash_set2.h +lib/gl_anyhash_primes.h + +Depends-on: +set +stdint +xsize + +configure.ac: + +Makefile.am: +lib_SOURCES += gl_linkedhash_set.h gl_linkedhash_set.c gl_anyhash_set1.h gl_anyhash_set2.h gl_anyhash_primes.h + +Include: +"gl_linkedhash_set.h" + +License: +GPL + +Maintainer: +all diff --git a/modules/rbtreehash-list b/modules/rbtreehash-list index 2b4add5..7957d51 100644 --- a/modules/rbtreehash-list +++ b/modules/rbtreehash-list @@ -6,6 +6,7 @@ lib/gl_rbtreehash_list.h lib/gl_rbtreehash_list.c lib/gl_anyhash_list1.h lib/gl_anyhash_list2.h +lib/gl_anyhash_primes.h lib/gl_anyrbtree_list1.h lib/gl_anyrbtree_list2.h lib/gl_anytree_list1.h @@ -23,7 +24,7 @@ xsize configure.ac: Makefile.am: -lib_SOURCES += gl_rbtreehash_list.h gl_rbtreehash_list.c gl_anyhash_list1.h gl_anyhash_list2.h gl_anyrbtree_list1.h gl_anyrbtree_list2.h gl_anytree_list1.h gl_anytree_list2.h gl_anytreehash_list1.h gl_anytreehash_list2.h +lib_SOURCES += gl_rbtreehash_list.h gl_rbtreehash_list.c gl_anyhash_list1.h gl_anyhash_list2.h gl_anyhash_primes.h gl_anyrbtree_list1.h gl_anyrbtree_list2.h gl_anytree_list1.h gl_anytree_list2.h gl_anytreehash_list1.h gl_anytreehash_list2.h Include: "gl_rbtreehash_list.h" -- 2.7.4
>From 96499e27955e55b86d0dd13d36c33decf149aacd Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Tue, 4 Dec 2018 00:54:30 +0100 Subject: [PATCH 4/8] hash-set: New module. * lib/gl_hash_set.h: New file. * lib/gl_hash_set.c: New file. * modules/hash-set: New file. --- ChangeLog | 7 ++ lib/gl_hash_set.c | 315 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/gl_hash_set.h | 34 ++++++ modules/hash-set | 28 +++++ 4 files changed, 384 insertions(+) create mode 100644 lib/gl_hash_set.c create mode 100644 lib/gl_hash_set.h create mode 100644 modules/hash-set diff --git a/ChangeLog b/ChangeLog index 5f0ae48..a8f2314 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,12 @@ 2018-12-03 Bruno Haible <br...@clisp.org> + hash-set: New module. + * lib/gl_hash_set.h: New file. + * lib/gl_hash_set.c: New file. + * modules/hash-set: New file. + +2018-12-03 Bruno Haible <br...@clisp.org> + linkedhash-set: New module. * lib/gl_linkedhash_set.h: New file. * lib/gl_linkedhash_set.c: New file. diff --git a/lib/gl_hash_set.c b/lib/gl_hash_set.c new file mode 100644 index 0000000..1f2f141 --- /dev/null +++ b/lib/gl_hash_set.c @@ -0,0 +1,315 @@ +/* Set 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_set.h" + +#include <stdint.h> /* for SIZE_MAX */ +#include <stdlib.h> + +#include "xsize.h" + +#ifndef uintptr_t +# define uintptr_t unsigned long +#endif + +/* --------------------------- gl_set_t Data Type --------------------------- */ + +#include "gl_anyhash_set1.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; +}; + +#include "gl_anyhash_set2.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 + }; diff --git a/lib/gl_hash_set.h b/lib/gl_hash_set.h new file mode 100644 index 0000000..7d3d500 --- /dev/null +++ b/lib/gl_hash_set.h @@ -0,0 +1,34 @@ +/* Set 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_SET_H +#define _GL_HASH_SET_H + +#include "gl_set.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const struct gl_set_implementation gl_hash_set_implementation; +#define GL_HASH_SET &gl_hash_set_implementation + +#ifdef __cplusplus +} +#endif + +#endif /* _GL_HASH_SET_H */ diff --git a/modules/hash-set b/modules/hash-set new file mode 100644 index 0000000..cd8ac8b --- /dev/null +++ b/modules/hash-set @@ -0,0 +1,28 @@ +Description: +Set data type implemented by a hash table. + +Files: +lib/gl_hash_set.h +lib/gl_hash_set.c +lib/gl_anyhash_set1.h +lib/gl_anyhash_set2.h +lib/gl_anyhash_primes.h + +Depends-on: +set +stdint +xsize + +configure.ac: + +Makefile.am: +lib_SOURCES += gl_hash_set.h gl_hash_set.c gl_anyhash_set1.h gl_anyhash_set2.h gl_anyhash_primes.h + +Include: +"gl_hash_set.h" + +License: +GPL + +Maintainer: +all -- 2.7.4
>From 9d0b41b846c392c787f4e05407da57ee1c6acde2 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Tue, 4 Dec 2018 00:55:34 +0100 Subject: [PATCH 5/8] xset: New module. * lib/gl_xset.h: New file. * lib/gl_xset.c: New file. * modules/xset: New file. --- ChangeLog | 7 ++++++ lib/gl_xset.c | 3 +++ lib/gl_xset.h | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ modules/xset | 26 ++++++++++++++++++++ 4 files changed, 112 insertions(+) create mode 100644 lib/gl_xset.c create mode 100644 lib/gl_xset.h create mode 100644 modules/xset diff --git a/ChangeLog b/ChangeLog index a8f2314..5d26ed2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,12 @@ 2018-12-03 Bruno Haible <br...@clisp.org> + xset: New module. + * lib/gl_xset.h: New file. + * lib/gl_xset.c: New file. + * modules/xset: New file. + +2018-12-03 Bruno Haible <br...@clisp.org> + hash-set: New module. * lib/gl_hash_set.h: New file. * lib/gl_hash_set.c: New file. diff --git a/lib/gl_xset.c b/lib/gl_xset.c new file mode 100644 index 0000000..83557c0 --- /dev/null +++ b/lib/gl_xset.c @@ -0,0 +1,3 @@ +#include <config.h> +#define GL_XSET_INLINE _GL_EXTERN_INLINE +#include "gl_xset.h" diff --git a/lib/gl_xset.h b/lib/gl_xset.h new file mode 100644 index 0000000..f907b06 --- /dev/null +++ b/lib/gl_xset.h @@ -0,0 +1,76 @@ +/* Abstract set data type, with out-of-memory checking. + Copyright (C) 2009-2018 Free Software Foundation, Inc. + Written by Bruno Haible <br...@clisp.org>, 2009. + + 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_XSET_H +#define _GL_XSET_H + +#include "gl_set.h" +#include "xalloc.h" + +#ifndef _GL_INLINE_HEADER_BEGIN + #error "Please include config.h first." +#endif +_GL_INLINE_HEADER_BEGIN +#ifndef GL_XSET_INLINE +# define GL_XSET_INLINE _GL_INLINE +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +/* These functions are thin wrappers around the corresponding functions with + _nx_ infix from gl_set.h. Upon out-of-memory, they invoke xalloc_die (), + instead of returning an error indicator. */ +#if 0 /* These are defined inline below. */ +extern gl_set_t gl_set_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); +extern bool gl_set_add (gl_set_t set, const void *elt); +#endif + +GL_XSET_INLINE gl_set_t +gl_set_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) +{ + gl_set_t result = + gl_set_nx_create_empty (implementation, equals_fn, hashcode_fn, dispose_fn); + if (result == NULL) + xalloc_die (); + return result; +} + +GL_XSET_INLINE bool +gl_set_add (gl_set_t set, const void *elt) +{ + int result = gl_set_nx_add (set, elt); + if (result < 0) + xalloc_die (); + return result; +} + +#ifdef __cplusplus +} +#endif + +_GL_INLINE_HEADER_END + +#endif /* _GL_XSET_H */ diff --git a/modules/xset b/modules/xset new file mode 100644 index 0000000..d78d5fc --- /dev/null +++ b/modules/xset @@ -0,0 +1,26 @@ +Description: +Abstract set data type, with out-of-memory checking. + +Files: +lib/gl_xset.h +lib/gl_xset.c + +Depends-on: +set +extern-inline +stdbool +xalloc-die + +configure.ac: + +Makefile.am: +lib_SOURCES += gl_xset.h gl_xset.c + +Include: +"gl_xset.h" + +License: +GPL + +Maintainer: +all -- 2.7.4
>From 21184e0eab43ca2e5fa9b308e97621b26020ab94 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Tue, 4 Dec 2018 00:56:31 +0100 Subject: [PATCH 6/8] array-set: Add tests. * tests/test-array_set.c: New file. * modules/array-set-tests: New file. --- ChangeLog | 6 ++ modules/array-set-tests | 15 +++++ tests/test-array_set.c | 149 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 modules/array-set-tests create mode 100644 tests/test-array_set.c diff --git a/ChangeLog b/ChangeLog index 5d26ed2..651aa85 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2018-12-03 Bruno Haible <br...@clisp.org> + array-set: Add tests. + * tests/test-array_set.c: New file. + * modules/array-set-tests: New file. + +2018-12-03 Bruno Haible <br...@clisp.org> + xset: New module. * lib/gl_xset.h: New file. * lib/gl_xset.c: New file. diff --git a/modules/array-set-tests b/modules/array-set-tests new file mode 100644 index 0000000..36b077e --- /dev/null +++ b/modules/array-set-tests @@ -0,0 +1,15 @@ +Files: +tests/test-array_set.c +tests/macros.h + +Depends-on: +xoset +array-oset +xalloc + +configure.ac: + +Makefile.am: +TESTS += test-array_set +check_PROGRAMS += test-array_set + diff --git a/tests/test-array_set.c b/tests/test-array_set.c new file mode 100644 index 0000000..35465ac --- /dev/null +++ b/tests/test-array_set.c @@ -0,0 +1,149 @@ +/* Test of set 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_set.h" + +#include <stdlib.h> +#include <string.h> + +#include "gl_xoset.h" +#include "gl_array_oset.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 RANDOM(n) (rand () % (n)) +#define RANDOM_OBJECT() objects[RANDOM (SIZEOF (objects))] + +static void +check_equals (gl_set_t set1, gl_oset_t set2) +{ + size_t n = gl_set_size (set1); + const void **elements_of_set1 = XNMALLOC (n, const void *); + const void **elements_of_set2 = XNMALLOC (n, const void *); + + gl_set_iterator_t iter1; + gl_oset_iterator_t iter2; + const void *elt1; + const void *elt2; + size_t i; + + iter1 = gl_set_iterator (set1); + iter2 = gl_oset_iterator (set2); + for (i = 0; i < n; i++) + { + ASSERT (gl_set_iterator_next (&iter1, &elt1)); + ASSERT (gl_oset_iterator_next (&iter2, &elt2)); + elements_of_set1[i] = elt1; + elements_of_set2[i] = elt2; + } + ASSERT (!gl_set_iterator_next (&iter1, &elt1)); + ASSERT (!gl_oset_iterator_next (&iter2, &elt2)); + gl_set_iterator_free (&iter1); + gl_oset_iterator_free (&iter2); + + if (n > 0) + { + qsort (elements_of_set1, n, sizeof (const void *), + (int (*) (const void *, const void *)) strcmp); + qsort (elements_of_set2, n, sizeof (const void *), + (int (*) (const void *, const void *)) strcmp); + } + for (i = 0; i < n; i++) + ASSERT (elements_of_set1[i] == elements_of_set2[i]); + free (elements_of_set2); + free (elements_of_set1); +} + +static void +check_all (gl_set_t set1, gl_oset_t set2) +{ + check_equals (set1, set2); +} + +int +main (int argc, char *argv[]) +{ + gl_set_t set1; + gl_oset_t set2; + + /* 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 set1. */ + set1 = gl_set_nx_create_empty (GL_ARRAY_SET, NULL, NULL, NULL); + ASSERT (set1 != NULL); + + /* Create set2. */ + set2 = gl_oset_create_empty (GL_ARRAY_OSET, (gl_setelement_compar_fn) strcmp, NULL); + + check_all (set1, set2); + + /* Initialize them. */ + for (i = 0; i < initial_size; i++) + { + const char *obj = RANDOM_OBJECT (); + ASSERT (gl_set_nx_add (set1, obj) == gl_oset_add (set2, obj)); + check_all (set1, set2); + } + + for (repeat = 0; repeat < 100000; repeat++) + { + unsigned int operation = RANDOM (3); + switch (operation) + { + case 0: + { + const char *obj = RANDOM_OBJECT (); + ASSERT (gl_set_search (set1, obj) == gl_oset_search (set2, obj)); + } + break; + case 1: + { + const char *obj = RANDOM_OBJECT (); + ASSERT (gl_set_nx_add (set1, obj) == gl_oset_add (set2, obj)); + } + break; + case 2: + { + const char *obj = RANDOM_OBJECT (); + ASSERT (gl_set_remove (set1, obj) == gl_oset_remove (set2, obj)); + } + break; + } + check_all (set1, set2); + } + + gl_set_free (set1); + gl_oset_free (set2); + } + + return 0; +} -- 2.7.4
>From 378334d804a1f7f96a87b10eba8715a1cdeaca26 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Tue, 4 Dec 2018 00:57:19 +0100 Subject: [PATCH 7/8] linkedhash-set: Add tests. * tests/test-linkedhash_set.c: New file. * modules/linkedhash-set-tests: New file. --- ChangeLog | 6 ++ modules/linkedhash-set-tests | 13 ++++ tests/test-linkedhash_set.c | 164 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 183 insertions(+) create mode 100644 modules/linkedhash-set-tests create mode 100644 tests/test-linkedhash_set.c diff --git a/ChangeLog b/ChangeLog index 651aa85..cf678cc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2018-12-03 Bruno Haible <br...@clisp.org> + linkedhash-set: Add tests. + * tests/test-linkedhash_set.c: New file. + * modules/linkedhash-set-tests: New file. + +2018-12-03 Bruno Haible <br...@clisp.org> + array-set: Add tests. * tests/test-array_set.c: New file. * modules/array-set-tests: New file. diff --git a/modules/linkedhash-set-tests b/modules/linkedhash-set-tests new file mode 100644 index 0000000..a22c9e0 --- /dev/null +++ b/modules/linkedhash-set-tests @@ -0,0 +1,13 @@ +Files: +tests/test-linkedhash_set.c +tests/macros.h + +Depends-on: +array-set +xalloc + +configure.ac: + +Makefile.am: +TESTS += test-linkedhash_set +check_PROGRAMS += test-linkedhash_set diff --git a/tests/test-linkedhash_set.c b/tests/test-linkedhash_set.c new file mode 100644 index 0000000..7ff92e8 --- /dev/null +++ b/tests/test-linkedhash_set.c @@ -0,0 +1,164 @@ +/* Test of set 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_set.h" + +#include <stdlib.h> +#include <string.h> + +#include "gl_array_set.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 RANDOM(n) (rand () % (n)) +#define RANDOM_OBJECT() objects[RANDOM (SIZEOF (objects))] + +static void +check_equals (gl_set_t set1, gl_set_t set2) +{ + size_t n = gl_set_size (set1); + const void **elements_of_set1 = XNMALLOC (n, const void *); + const void **elements_of_set2 = XNMALLOC (n, const void *); + + gl_set_iterator_t iter1; + gl_set_iterator_t iter2; + const void *elt1; + const void *elt2; + size_t i; + + iter1 = gl_set_iterator (set1); + iter2 = gl_set_iterator (set2); + for (i = 0; i < n; i++) + { + ASSERT (gl_set_iterator_next (&iter1, &elt1)); + ASSERT (gl_set_iterator_next (&iter2, &elt2)); + elements_of_set1[i] = elt1; + elements_of_set2[i] = elt2; + } + ASSERT (!gl_set_iterator_next (&iter1, &elt1)); + ASSERT (!gl_set_iterator_next (&iter2, &elt2)); + gl_set_iterator_free (&iter1); + gl_set_iterator_free (&iter2); + + if (n > 0) + { + qsort (elements_of_set1, n, sizeof (const void *), + (int (*) (const void *, const void *)) strcmp); + qsort (elements_of_set2, n, sizeof (const void *), + (int (*) (const void *, const void *)) strcmp); + } + for (i = 0; i < n; i++) + ASSERT (elements_of_set1[i] == elements_of_set2[i]); + free (elements_of_set2); + free (elements_of_set1); +} + +static void +check_all (gl_set_t set1, gl_set_t set2) +{ + check_equals (set1, set2); +} + +static bool +string_equals (const void *elt1, const void *elt2) +{ + return strcmp ((const char *) elt1, (const char *) elt2) == 0; +} + +static size_t +string_hashcode (const void *elt) +{ + size_t hashcode = 0; + const char *s; + for (s = (const char *) elt; *s != '\0'; s++) + hashcode += (unsigned char) *s; + return hashcode; +} + +int +main (int argc, char *argv[]) +{ + gl_set_t set1, set2; + + /* 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 set1. */ + set1 = gl_set_nx_create_empty (GL_ARRAY_SET, string_equals, string_hashcode, NULL); + ASSERT (set1 != NULL); + + /* Create set2. */ + set2 = gl_set_nx_create_empty (GL_LINKEDHASH_SET, string_equals, string_hashcode, NULL); + ASSERT (set2 != NULL); + + check_all (set1, set2); + + /* Initialize them. */ + for (i = 0; i < initial_size; i++) + { + const char *obj = RANDOM_OBJECT (); + ASSERT (gl_set_nx_add (set1, obj) == gl_set_nx_add (set2, obj)); + check_all (set1, set2); + } + + for (repeat = 0; repeat < 100000; repeat++) + { + unsigned int operation = RANDOM (3); + switch (operation) + { + case 0: + { + const char *obj = RANDOM_OBJECT (); + ASSERT (gl_set_search (set1, obj) == gl_set_search (set2, obj)); + } + break; + case 1: + { + const char *obj = RANDOM_OBJECT (); + ASSERT (gl_set_nx_add (set1, obj) == gl_set_nx_add (set2, obj)); + } + break; + case 2: + { + const char *obj = RANDOM_OBJECT (); + ASSERT (gl_set_remove (set1, obj) == gl_set_remove (set2, obj)); + } + break; + } + check_all (set1, set2); + } + + gl_set_free (set1); + gl_set_free (set2); + } + + return 0; +} -- 2.7.4
>From f7f4d0684141dc9bb703f7a29add16c04350fc3c Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Tue, 4 Dec 2018 00:57:58 +0100 Subject: [PATCH 8/8] hash-set: Add tests. * tests/test-hash_set.c: New file. * modules/hash-set-tests: New file. --- ChangeLog | 6 ++ modules/hash-set-tests | 13 ++++ tests/test-hash_set.c | 164 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 183 insertions(+) create mode 100644 modules/hash-set-tests create mode 100644 tests/test-hash_set.c diff --git a/ChangeLog b/ChangeLog index cf678cc..b96d8e6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2018-12-03 Bruno Haible <br...@clisp.org> + hash-set: Add tests. + * tests/test-hash_set.c: New file. + * modules/hash-set-tests: New file. + +2018-12-03 Bruno Haible <br...@clisp.org> + linkedhash-set: Add tests. * tests/test-linkedhash_set.c: New file. * modules/linkedhash-set-tests: New file. diff --git a/modules/hash-set-tests b/modules/hash-set-tests new file mode 100644 index 0000000..c98898c --- /dev/null +++ b/modules/hash-set-tests @@ -0,0 +1,13 @@ +Files: +tests/test-hash_set.c +tests/macros.h + +Depends-on: +array-set +xalloc + +configure.ac: + +Makefile.am: +TESTS += test-hash_set +check_PROGRAMS += test-hash_set diff --git a/tests/test-hash_set.c b/tests/test-hash_set.c new file mode 100644 index 0000000..d791074 --- /dev/null +++ b/tests/test-hash_set.c @@ -0,0 +1,164 @@ +/* Test of set 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_set.h" + +#include <stdlib.h> +#include <string.h> + +#include "gl_array_set.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 RANDOM(n) (rand () % (n)) +#define RANDOM_OBJECT() objects[RANDOM (SIZEOF (objects))] + +static void +check_equals (gl_set_t set1, gl_set_t set2) +{ + size_t n = gl_set_size (set1); + const void **elements_of_set1 = XNMALLOC (n, const void *); + const void **elements_of_set2 = XNMALLOC (n, const void *); + + gl_set_iterator_t iter1; + gl_set_iterator_t iter2; + const void *elt1; + const void *elt2; + size_t i; + + iter1 = gl_set_iterator (set1); + iter2 = gl_set_iterator (set2); + for (i = 0; i < n; i++) + { + ASSERT (gl_set_iterator_next (&iter1, &elt1)); + ASSERT (gl_set_iterator_next (&iter2, &elt2)); + elements_of_set1[i] = elt1; + elements_of_set2[i] = elt2; + } + ASSERT (!gl_set_iterator_next (&iter1, &elt1)); + ASSERT (!gl_set_iterator_next (&iter2, &elt2)); + gl_set_iterator_free (&iter1); + gl_set_iterator_free (&iter2); + + if (n > 0) + { + qsort (elements_of_set1, n, sizeof (const void *), + (int (*) (const void *, const void *)) strcmp); + qsort (elements_of_set2, n, sizeof (const void *), + (int (*) (const void *, const void *)) strcmp); + } + for (i = 0; i < n; i++) + ASSERT (elements_of_set1[i] == elements_of_set2[i]); + free (elements_of_set2); + free (elements_of_set1); +} + +static void +check_all (gl_set_t set1, gl_set_t set2) +{ + check_equals (set1, set2); +} + +static bool +string_equals (const void *elt1, const void *elt2) +{ + return strcmp ((const char *) elt1, (const char *) elt2) == 0; +} + +static size_t +string_hashcode (const void *elt) +{ + size_t hashcode = 0; + const char *s; + for (s = (const char *) elt; *s != '\0'; s++) + hashcode += (unsigned char) *s; + return hashcode; +} + +int +main (int argc, char *argv[]) +{ + gl_set_t set1, set2; + + /* 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 set1. */ + set1 = gl_set_nx_create_empty (GL_ARRAY_SET, string_equals, string_hashcode, NULL); + ASSERT (set1 != NULL); + + /* Create set2. */ + set2 = gl_set_nx_create_empty (GL_HASH_SET, string_equals, string_hashcode, NULL); + ASSERT (set2 != NULL); + + check_all (set1, set2); + + /* Initialize them. */ + for (i = 0; i < initial_size; i++) + { + const char *obj = RANDOM_OBJECT (); + ASSERT (gl_set_nx_add (set1, obj) == gl_set_nx_add (set2, obj)); + check_all (set1, set2); + } + + for (repeat = 0; repeat < 100000; repeat++) + { + unsigned int operation = RANDOM (3); + switch (operation) + { + case 0: + { + const char *obj = RANDOM_OBJECT (); + ASSERT (gl_set_search (set1, obj) == gl_set_search (set2, obj)); + } + break; + case 1: + { + const char *obj = RANDOM_OBJECT (); + ASSERT (gl_set_nx_add (set1, obj) == gl_set_nx_add (set2, obj)); + } + break; + case 2: + { + const char *obj = RANDOM_OBJECT (); + ASSERT (gl_set_remove (set1, obj) == gl_set_remove (set2, obj)); + } + break; + } + check_all (set1, set2); + } + + gl_set_free (set1); + gl_set_free (set2); + } + + return 0; +} -- 2.7.4