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