1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2009, 2011 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include "libpspp/string-map.h"
20
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include "libpspp/hash-functions.h"
25 #include "libpspp/string-set.h"
26
27 #include "gl/xalloc.h"
28 #include "gl/xmemdup0.h"
29
30 static struct string_map_node *string_map_find_node_with_hash (
31 const struct string_map *, const char *key, size_t length,
32 unsigned int hash);
33 static bool string_map_delete__ (struct string_map *, const char *key,
34 unsigned int hash);
35 static struct string_map_node *string_map_insert__ (struct string_map *,
36 char *key, char *value,
37 unsigned int hash);
38
39 /* Sets NODE's value to a copy of NEW_VALUE and returns the node's previous
40 value. The caller is responsible for freeing the returned string (with
41 free()). */
42 char *
string_map_node_swap_value(struct string_map_node * node,const char * new_value)43 string_map_node_swap_value (struct string_map_node *node,
44 const char *new_value)
45 {
46 return string_map_node_swap_value_nocopy (node, xstrdup (new_value));
47 }
48
49 /* Sets NODE's value to NEW_VALUE, which must be a malloc()'d string,
50 transferring ownership of NEW_VALUE to the node. Returns the node's
51 previous value, which the caller is responsible for freeing (with
52 free()). */
53 char *
string_map_node_swap_value_nocopy(struct string_map_node * node,char * new_value)54 string_map_node_swap_value_nocopy (struct string_map_node *node,
55 char *new_value)
56 {
57 char *old_value = node->value;
58 node->value = new_value;
59 return old_value;
60 }
61
62 /* Replaces NODE's value by a copy of VALUE. */
63 void
string_map_node_set_value(struct string_map_node * node,const char * value)64 string_map_node_set_value (struct string_map_node *node, const char *value)
65 {
66 string_map_node_set_value_nocopy (node, xstrdup (value));
67 }
68
69 /* Replaces NODE's value by VALUE, which must be a malloc()'d string,
70 transferring ownership of VALUE to the node.. */
71 void
string_map_node_set_value_nocopy(struct string_map_node * node,char * value)72 string_map_node_set_value_nocopy (struct string_map_node *node, char *value)
73 {
74 free (node->value);
75 node->value = value;
76 }
77
78 /* Frees NODE and and its key and value. Ordinarily nodes are owned by
79 string_maps, but this function should only be used by a caller that owns
80 NODE, such as one that has called string_map_delete_nofree() for the
81 node. */
82 void
string_map_node_destroy(struct string_map_node * node)83 string_map_node_destroy (struct string_map_node *node)
84 {
85 free (node->key);
86 free (node->value);
87 free (node);
88 }
89
90 /* Initializes MAP as an initially empty string map. */
91 void
string_map_init(struct string_map * map)92 string_map_init (struct string_map *map)
93 {
94 hmap_init (&map->hmap);
95 }
96
97 /* Initializes MAP as a new string map that initially contains the same pairs
98 as OLD. */
99 void
string_map_clone(struct string_map * map,const struct string_map * old)100 string_map_clone (struct string_map *map, const struct string_map *old)
101 {
102 const struct string_map_node *node;
103 const char *key, *value;
104
105 string_map_init (map);
106 hmap_reserve (&map->hmap, string_map_count (old));
107 STRING_MAP_FOR_EACH_KEY_VALUE (key, value, node, old)
108 string_map_insert__ (map, xstrdup (key), xstrdup (value),
109 node->hmap_node.hash);
110 }
111
112 /* Exchanges the contents of string maps A and B. */
113 void
string_map_swap(struct string_map * a,struct string_map * b)114 string_map_swap (struct string_map *a, struct string_map *b)
115 {
116 hmap_swap (&a->hmap, &b->hmap);
117 }
118
119 /* Frees MAP and its nodes and key-value pairs. */
120 void
string_map_destroy(struct string_map * map)121 string_map_destroy (struct string_map *map)
122 {
123 if (map != NULL)
124 {
125 string_map_clear (map);
126 hmap_destroy (&map->hmap);
127 }
128 }
129
130 /* Returns true if MAP contains KEY as a key, otherwise false. */
131 bool
string_map_contains(const struct string_map * map,const char * key)132 string_map_contains (const struct string_map *map, const char *key)
133 {
134 return string_map_find_node (map, key) != NULL;
135 }
136
137 /* If MAP contains KEY, which is LENGTH bytes long, as a key, returns the
138 corresponding value. Otherwise, returns a null pointer. */
139 const char *
string_map_find__(const struct string_map * map,const char * key,size_t length)140 string_map_find__ (const struct string_map *map, const char *key,
141 size_t length)
142 {
143 const struct string_map_node *node = string_map_find_node__ (map, key,
144 length);
145 return node != NULL ? node->value : NULL;
146 }
147
148 /* If MAP contains KEY as a key, returns the corresponding value. Otherwise,
149 returns a null pointer. */
150 const char *
string_map_find(const struct string_map * map,const char * key)151 string_map_find (const struct string_map *map, const char *key)
152 {
153 const struct string_map_node *node = string_map_find_node (map, key);
154 return node != NULL ? node->value : NULL;
155 }
156
157 /* If MAP contains KEY as a key, returns the corresponding node. Otherwise,
158 returns a null pointer. */
159 struct string_map_node *
string_map_find_node__(const struct string_map * map,const char * key,size_t length)160 string_map_find_node__ (const struct string_map *map, const char *key,
161 size_t length)
162 {
163 return string_map_find_node_with_hash (map, key, length,
164 hash_bytes (key, length, 0));
165 }
166
167 /* If MAP contains KEY as a key, returns the corresponding node. Otherwise,
168 returns a null pointer. */
169 struct string_map_node *
string_map_find_node(const struct string_map * map,const char * key)170 string_map_find_node (const struct string_map *map, const char *key)
171 {
172 return string_map_find_node__ (map, key, strlen (key));
173 }
174
175 /* If MAP contains KEY as a key, deletes that key's node and returns its value,
176 which the caller is responsible for freeing (using free()). Otherwise,
177 returns a null pointer. */
178 char *
string_map_find_and_delete(struct string_map * map,const char * key)179 string_map_find_and_delete (struct string_map *map, const char *key)
180 {
181 struct string_map_node *node = string_map_find_node (map, key);
182 char *value = NULL;
183 if (node != NULL)
184 {
185 value = node->value;
186 node->value = NULL;
187 string_map_delete_node (map, node);
188 }
189 return value;
190 }
191
192 /* If MAP does not contain KEY as a key, inserts a new node containing copies
193 of KEY and VALUE and returns the new node. Otherwise, returns the existing
194 node that contains KEY. */
195 struct string_map_node *
string_map_insert(struct string_map * map,const char * key,const char * value)196 string_map_insert (struct string_map *map, const char *key, const char *value)
197 {
198 size_t length = strlen (key);
199 unsigned int hash = hash_bytes (key, length, 0);
200 struct string_map_node *node = string_map_find_node_with_hash (map, key,
201 length, hash);
202 if (node == NULL)
203 node = string_map_insert__ (map, xmemdup0 (key, length), xstrdup (value),
204 hash);
205 return node;
206 }
207
208 /* If MAP does not contain KEY as a key, inserts a new node containing KEY and
209 VALUE and returns the new node. Otherwise, returns the existing node that
210 contains KEY. Either way, ownership of KEY and VALUE is transferred to
211 MAP. */
212 struct string_map_node *
string_map_insert_nocopy(struct string_map * map,char * key,char * value)213 string_map_insert_nocopy (struct string_map *map, char *key, char *value)
214 {
215 size_t length = strlen (key);
216 unsigned int hash = hash_bytes (key, length, 0);
217 struct string_map_node *node = string_map_find_node_with_hash (map, key,
218 length, hash);
219 if (node == NULL)
220 node = string_map_insert__ (map, key, value, hash);
221 else
222 {
223 free (key);
224 free (value);
225 }
226 return node;
227 }
228
229 /* If MAP does not contain KEY as a key, inserts a new node containing copies
230 of KEY and VALUE. Otherwise, replaces the existing node's value by a copy
231 of VALUE. Returns the node. */
232 struct string_map_node *
string_map_replace(struct string_map * map,const char * key,const char * value)233 string_map_replace (struct string_map *map, const char *key, const char *value)
234 {
235 size_t length = strlen (key);
236 unsigned int hash = hash_bytes (key, length, 0);
237 struct string_map_node *node = string_map_find_node_with_hash (map, key,
238 length, hash);
239 if (node == NULL)
240 node = string_map_insert__ (map, xmemdup0 (key, length),
241 xstrdup (value), hash);
242 else
243 string_map_node_set_value (node, value);
244 return node;
245 }
246
247 /* If MAP does not contain KEY as a key, inserts a new node containing KEY and
248 VALUE. Otherwise, replaces the existing node's value by VALUE. Either way,
249 ownership of KEY and VALUE is transferred to MAP. Returns the node. */
250 struct string_map_node *
string_map_replace_nocopy(struct string_map * map,char * key,char * value)251 string_map_replace_nocopy (struct string_map *map, char *key, char *value)
252 {
253 size_t length = strlen (key);
254 unsigned int hash = hash_bytes (key, length, 0);
255 struct string_map_node *node = string_map_find_node_with_hash (map, key,
256 length, hash);
257 if (node == NULL)
258 node = string_map_insert__ (map, key, value, hash);
259 else
260 {
261 free (key);
262 string_map_node_set_value_nocopy (node, value);
263 }
264 return node;
265 }
266
267 /* Searches MAP for a node with KEY as its key. If found, deletes the node
268 and its key and value and returns true. Otherwise, returns false without
269 modifying MAP. */
270 bool
string_map_delete(struct string_map * map,const char * key)271 string_map_delete (struct string_map *map, const char *key)
272 {
273 return string_map_delete__ (map, key, hash_string (key, 0));
274 }
275
276 /* Deletes NODE from MAP and destroys the node and its key and value. */
277 void
string_map_delete_node(struct string_map * map,struct string_map_node * node)278 string_map_delete_node (struct string_map *map, struct string_map_node *node)
279 {
280 string_map_delete_nofree (map, node);
281 string_map_node_destroy (node);
282 }
283
284 /* Deletes NODE from MAP. Transfers ownership of NODE to the caller, which
285 becomes responsible for destroying it. */
286 void
string_map_delete_nofree(struct string_map * map,struct string_map_node * node)287 string_map_delete_nofree (struct string_map *map, struct string_map_node *node)
288 {
289 hmap_delete (&map->hmap, &node->hmap_node);
290 }
291
292 /* Removes all nodes from MAP and frees them and their keys and values. */
293 void
string_map_clear(struct string_map * map)294 string_map_clear (struct string_map *map)
295 {
296 struct string_map_node *node, *next;
297
298 STRING_MAP_FOR_EACH_NODE_SAFE (node, next, map)
299 string_map_delete_node (map, node);
300 }
301
302 /* Inserts a copy of each of the nodes in SRC into DST. When SRC and DST both
303 have a particular key, the value in DST's node is left unchanged. */
304 void
string_map_insert_map(struct string_map * dst,const struct string_map * src)305 string_map_insert_map (struct string_map *dst, const struct string_map *src)
306 {
307 const struct string_map_node *node;
308
309 STRING_MAP_FOR_EACH_NODE (node, src)
310 {
311 if (!string_map_find_node_with_hash (dst, node->key, strlen (node->key),
312 node->hmap_node.hash))
313 string_map_insert__ (dst, xstrdup (node->key), xstrdup (node->value),
314 node->hmap_node.hash);
315 }
316 }
317
318 /* Inserts a copy of each of the nodes in SRC into DST. When SRC and DST both
319 have a particular key, the value in DST's node is replaced by a copy of the
320 value in SRC's node. */
321 void
string_map_replace_map(struct string_map * dst,const struct string_map * src)322 string_map_replace_map (struct string_map *dst, const struct string_map *src)
323 {
324 const struct string_map_node *snode;
325
326 STRING_MAP_FOR_EACH_NODE (snode, src)
327 {
328 struct string_map_node *dnode;
329 dnode = string_map_find_node_with_hash (dst, snode->key,
330 strlen (snode->key),
331 snode->hmap_node.hash);
332 if (dnode != NULL)
333 string_map_node_set_value (dnode, snode->value);
334 else
335 string_map_insert__ (dst, xstrdup (snode->key), xstrdup (snode->value),
336 snode->hmap_node.hash);
337 }
338 }
339
340 /* Inserts each of the keys in MAP into KEYS. KEYS must already have been
341 initialized (using string_set_init()). */
342 void
string_map_get_keys(const struct string_map * map,struct string_set * keys)343 string_map_get_keys (const struct string_map *map, struct string_set *keys)
344 {
345 const struct string_map_node *node;
346 const char *key;
347
348 STRING_MAP_FOR_EACH_KEY (key, node, map)
349 string_set_insert (keys, key);
350 }
351
352 /* Inserts each of the values in MAP into VALUES. VALUES must already have
353 been initialized (using string_set_init()). */
354 void
string_map_get_values(const struct string_map * map,struct string_set * values)355 string_map_get_values (const struct string_map *map, struct string_set *values)
356 {
357 const struct string_map_node *node;
358 const char *value;
359
360 STRING_MAP_FOR_EACH_VALUE (value, node, map)
361 string_set_insert (values, value);
362 }
363
364 /* Returns true if A and B have the same content, false otherwise. */
365 bool
string_map_equals(const struct string_map * a,const struct string_map * b)366 string_map_equals (const struct string_map *a, const struct string_map *b)
367 {
368 if (string_map_count (a) != string_map_count (b))
369 return false;
370
371 const struct string_map_node *a_node;
372 STRING_MAP_FOR_EACH_NODE (a_node, a)
373 {
374 const struct string_map_node *b_node = string_map_find_node_with_hash (
375 b, a_node->key, strlen (a_node->key), a_node->hmap_node.hash);
376 if (!b_node || strcmp (a_node->value, b_node->value))
377 return false;
378 }
379
380 return true;
381 }
382
383 static struct string_map_node *
string_map_find_node_with_hash(const struct string_map * map,const char * key,size_t length,unsigned int hash)384 string_map_find_node_with_hash (const struct string_map *map, const char *key,
385 size_t length, unsigned int hash)
386 {
387 struct string_map_node *node;
388
389 HMAP_FOR_EACH_WITH_HASH (node, struct string_map_node, hmap_node,
390 hash, &map->hmap)
391 if (!strncmp (key, node->key, length) && node->key[length] == '\0')
392 return node;
393
394 return NULL;
395 }
396
397 static bool
string_map_delete__(struct string_map * map,const char * key,unsigned int hash)398 string_map_delete__ (struct string_map *map, const char *key,
399 unsigned int hash)
400 {
401 struct string_map_node *node
402 = string_map_find_node_with_hash (map, key, strlen (key), hash);
403 if (node != NULL)
404 {
405 string_map_delete_node (map, node);
406 return true;
407 }
408 else
409 return false;
410 }
411
412 static struct string_map_node *
string_map_insert__(struct string_map * map,char * key,char * value,unsigned int hash)413 string_map_insert__ (struct string_map *map, char *key, char *value,
414 unsigned int hash)
415 {
416 struct string_map_node *node = xmalloc (sizeof *node);
417 node->key = key;
418 node->value = value;
419 hmap_insert (&map->hmap, &node->hmap_node, hash);
420 return node;
421 }
422
423