1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2009, 2010, 2012 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
17 /* If you add routines in this file, please add a corresponding test to
18 stringi-set-test.c. */
22 #include "libpspp/stringi-set.h"
27 #include "libpspp/cast.h"
28 #include "libpspp/hash-functions.h"
29 #include "libpspp/i18n.h"
31 #include "gl/xalloc.h"
33 static struct stringi_set_node *stringi_set_find_node__ (
34 const struct stringi_set *, const char *, unsigned int hash);
35 static void stringi_set_insert__ (struct stringi_set *, char *,
37 static bool stringi_set_delete__ (struct stringi_set *, const char *,
40 /* Initializes SET as a new string set that is initially empty. */
42 stringi_set_init (struct stringi_set *set)
44 hmap_init (&set->hmap);
47 /* Initializes SET as a new string set that initially contains the same strings
50 stringi_set_clone (struct stringi_set *set, const struct stringi_set *old)
52 const struct stringi_set_node *node;
55 stringi_set_init (set);
56 hmap_reserve (&set->hmap, stringi_set_count (old));
57 STRINGI_SET_FOR_EACH (s, node, old)
58 stringi_set_insert__ (set, xstrdup (s), node->hmap_node.hash);
61 /* Exchanges the contents of string sets A and B. */
63 stringi_set_swap (struct stringi_set *a, struct stringi_set *b)
65 hmap_swap (&a->hmap, &b->hmap);
68 /* Frees SET and its nodes and strings. */
70 stringi_set_destroy (struct stringi_set *set)
74 stringi_set_clear (set);
75 hmap_destroy (&set->hmap);
79 /* Returns true if SET contains S (or a similar string with different case),
82 stringi_set_contains (const struct stringi_set *set, const char *s)
84 return stringi_set_find_node (set, s) != NULL;
87 /* Returns the node in SET that contains S, or a null pointer if SET does not
89 struct stringi_set_node *
90 stringi_set_find_node (const struct stringi_set *set, const char *s)
92 return stringi_set_find_node__ (set, s, utf8_hash_case_string (s, 0));
95 /* Inserts a copy of S into SET. Returns true if successful, false if SET
96 is unchanged because it already contained S. */
98 stringi_set_insert (struct stringi_set *set, const char *s)
100 unsigned int hash = utf8_hash_case_string (s, 0);
101 if (!stringi_set_find_node__ (set, s, hash))
103 stringi_set_insert__ (set, xstrdup (s), hash);
110 /* Inserts S, which must be a malloc'd string, into SET, transferring ownership
111 of S to SET. Returns true if successful, false if SET is unchanged because
112 it already contained a copy of S. (In the latter case, S is freed.) */
114 stringi_set_insert_nocopy (struct stringi_set *set, char *s)
116 unsigned int hash = utf8_hash_case_string (s, 0);
117 if (!stringi_set_find_node__ (set, s, hash))
119 stringi_set_insert__ (set, s, hash);
129 /* Deletes S from SET. Returns true if successful, false if SET is unchanged
130 because it did not contain a copy of S. */
132 stringi_set_delete (struct stringi_set *set, const char *s)
134 return stringi_set_delete__ (set, s, utf8_hash_case_string (s, 0));
137 /* Deletes NODE from SET, and frees NODE and its string. */
139 stringi_set_delete_node (struct stringi_set *set,
140 struct stringi_set_node *node)
142 free (stringi_set_delete_nofree (set, node));
145 /* Deletes NODE from SET and frees NODE. Returns the string that NODE
146 contained, transferring ownership to the caller. */
148 stringi_set_delete_nofree (struct stringi_set *set,
149 struct stringi_set_node *node)
151 char *string = node->string;
152 hmap_delete (&set->hmap, &node->hmap_node);
157 /* Removes all nodes from SET. */
159 stringi_set_clear (struct stringi_set *set)
161 struct stringi_set_node *node, *next;
163 HMAP_FOR_EACH_SAFE (node, next, struct stringi_set_node, hmap_node,
165 stringi_set_delete_node (set, node);
168 /* Calculates A = union(A, B).
170 If B may be modified, stringi_set_union_and_intersection() is
171 faster than this function. */
173 stringi_set_union (struct stringi_set *a, const struct stringi_set *b)
175 struct stringi_set_node *node;
176 HMAP_FOR_EACH (node, struct stringi_set_node, hmap_node, &b->hmap)
177 if (!stringi_set_find_node__ (a, node->string, node->hmap_node.hash))
178 stringi_set_insert__ (a, xstrdup (node->string), node->hmap_node.hash);
181 /* Calculates A = union(A, B) and B = intersect(A, B).
183 If only the intersection is needed, stringi_set_intersect() is
186 stringi_set_union_and_intersection (struct stringi_set *a,
187 struct stringi_set *b)
189 struct stringi_set_node *node, *next;
191 HMAP_FOR_EACH_SAFE (node, next, struct stringi_set_node, hmap_node,
193 if (!stringi_set_find_node__ (a, node->string, node->hmap_node.hash))
195 hmap_delete (&b->hmap, &node->hmap_node);
196 hmap_insert (&a->hmap, &node->hmap_node, node->hmap_node.hash);
200 /* Calculates A = intersect(A, B). */
202 stringi_set_intersect (struct stringi_set *a, const struct stringi_set *b)
204 struct stringi_set_node *node, *next;
206 HMAP_FOR_EACH_SAFE (node, next, struct stringi_set_node, hmap_node,
208 if (!stringi_set_find_node__ (b, node->string, node->hmap_node.hash))
209 stringi_set_delete_node (a, node);
212 /* Removes from A all of the strings in B. */
214 stringi_set_subtract (struct stringi_set *a, const struct stringi_set *b)
216 struct stringi_set_node *node, *next;
218 if (stringi_set_count (a) < stringi_set_count (b))
220 HMAP_FOR_EACH_SAFE (node, next, struct stringi_set_node, hmap_node,
222 if (stringi_set_find_node__ (b, node->string, node->hmap_node.hash))
223 stringi_set_delete_node (a, node);
227 HMAP_FOR_EACH (node, struct stringi_set_node, hmap_node, &b->hmap)
228 stringi_set_delete__ (a, node->string, node->hmap_node.hash);
232 /* Allocates and returns an array that points to each of the strings in SET.
233 The caller must not free or modify any of the strings. Removing a string
234 from SET invalidates the corresponding element of the returned array. The
235 caller it is responsible for freeing the returned array itself (with
238 The returned array is in the same order as observed by stringi_set_first()
239 and stringi_set_next(), that is, no particular order. */
241 stringi_set_get_array (const struct stringi_set *set)
243 const struct stringi_set_node *node;
248 array = xnmalloc (stringi_set_count (set), sizeof *array);
251 STRINGI_SET_FOR_EACH (s, node, set)
252 array[i++] = CONST_CAST (char *, s);
258 compare_strings (const void *a_, const void *b_)
260 const char *const *a = a_;
261 const char *const *b = b_;
262 return utf8_strcasecmp (*a, *b);
265 /* Allocates and returns an array that points to each of the strings in SET.
266 The caller must not free or modify any of the strings. Removing a string
267 from SET invalidates the corresponding element of the returned array. The
268 caller it is responsible for freeing the returned array itself (with
271 The returned array is ordered according to utf8_strcasecmp(). */
273 stringi_set_get_sorted_array (const struct stringi_set *set)
275 char **array = stringi_set_get_array (set);
276 qsort (array, stringi_set_count (set), sizeof *array, compare_strings);
280 /* Internal functions. */
282 static struct stringi_set_node *
283 stringi_set_find_node__ (const struct stringi_set *set, const char *s,
286 struct stringi_set_node *node;
288 HMAP_FOR_EACH_WITH_HASH (node, struct stringi_set_node, hmap_node,
290 if (!utf8_strcasecmp (s, node->string))
297 stringi_set_insert__ (struct stringi_set *set, char *s, unsigned int hash)
299 struct stringi_set_node *node = xmalloc (sizeof *node);
301 hmap_insert (&set->hmap, &node->hmap_node, hash);
305 stringi_set_delete__ (struct stringi_set *set, const char *s,
308 struct stringi_set_node *node = stringi_set_find_node__ (set, s, hash);
311 stringi_set_delete_node (set, node);