lib: New data structure - smap.
[sliver-openvswitch.git] / lib / smap.c
1 /* Copyright (c) 2012 Nicira, Inc.
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License. */
14
15 #include <config.h>
16 #include "smap.h"
17
18 #include <assert.h>
19
20 #include "hash.h"
21
22 static struct smap_node *smap_add__(struct smap *, char *, void *,
23                                     size_t hash);
24 static struct smap_node *smap_find__(const struct smap *, const char *key,
25                                      size_t key_len, size_t hash);
26 static int compare_nodes_by_key(const void *, const void *);
27 \f
28 /* Public Functions. */
29
30 void
31 smap_init(struct smap *smap)
32 {
33     hmap_init(&smap->map);
34 }
35
36 void
37 smap_destroy(struct smap *smap)
38 {
39     if (smap) {
40         smap_clear(smap);
41         hmap_destroy(&smap->map);
42     }
43 }
44
45 /* Adds 'key' paired with 'value' to 'smap'.  It is the caller's responsibility
46  * to avoid duplicate keys if desirable. */
47 struct smap_node *
48 smap_add(struct smap *smap, const char *key, const char *value)
49 {
50     size_t key_len = strlen(key);
51     return smap_add__(smap, xmemdup0(key, key_len), xstrdup(value),
52                       hash_bytes(key, key_len, 0));
53 }
54
55 /* Attempts to add 'key' to 'smap' associated with 'value'.  If 'key' already
56  * exists in 'smap', does nothing and returns false.  Otherwise, performs the
57  * addition and returns true. */
58 bool
59 smap_add_once(struct smap *smap, const char *key, const char *value)
60 {
61     if (!smap_get(smap, key)) {
62         smap_add(smap, key, value);
63         return true;
64     } else {
65         return false;
66     }
67 }
68
69 /* Adds 'key' paired with a value derived from 'format' (similar to printf).
70  * It is the caller's responsibility to avoid duplicate keys if desirable. */
71 void
72 smap_add_format(struct smap *smap, const char *key, const char *format, ...)
73 {
74     size_t key_len;
75     va_list args;
76     char *value;
77
78     va_start(args, format);
79     value = xvasprintf(format, args);
80     va_end(args);
81
82     key_len = strlen(key);
83     smap_add__(smap, xmemdup0(key, key_len), value,
84                hash_bytes(key, key_len, 0));
85 }
86
87 /* Searches for 'key' in 'smap'.  If it does not already exists, adds it.
88  * Otherwise, changes its value to 'value'. */
89 void
90 smap_replace(struct smap *smap, const char *key, const char *value)
91 {
92     size_t  key_len = strlen(key);
93     size_t hash = hash_bytes(key, key_len, 0);
94
95     struct smap_node *node;
96
97     node = smap_find__(smap, key, key_len, hash);
98     if (node) {
99         free(node->value);
100         node->value = xstrdup(value);
101     } else {
102         smap_add__(smap, xmemdup0(key, key_len), xstrdup(value), hash);
103     }
104 }
105
106 /* If 'key' is in 'smap', removes it.  Otherwise does nothing. */
107 void
108 smap_remove(struct smap *smap, const char *key)
109 {
110     struct smap_node *node = smap_get_node(smap, key);
111
112     if (node) {
113         smap_remove_node(smap, node);
114     }
115 }
116
117 /* Removes 'node' from 'smap'. */
118 void
119 smap_remove_node(struct smap *smap, struct smap_node *node)
120 {
121     hmap_remove(&smap->map, &node->node);
122     free(node->key);
123     free(node->value);
124     free(node);
125 }
126
127 /* Removes all key-value pairs from 'smap'. */
128 void
129 smap_clear(struct smap *smap)
130 {
131     struct smap_node *node, *next;
132
133     SMAP_FOR_EACH_SAFE (node, next, smap) {
134         smap_remove_node(smap, node);
135     }
136 }
137
138 /* Returns the value associated with 'key' in 'smap', or NULL. */
139 const char *
140 smap_get(const struct smap *smap, const char *key)
141 {
142     struct smap_node *node = smap_get_node(smap, key);
143     return node ? node->value : NULL;
144 }
145
146 /* Returns the node associated with 'key' in 'smap', or NULL. */
147 struct smap_node *
148 smap_get_node(const struct smap *smap, const char *key)
149 {
150     size_t key_len = strlen(key);
151     return smap_find__(smap, key, key_len, hash_bytes(key, key_len, 0));
152 }
153
154 /* Gets the value associated with 'key' in 'smap' and converts it to a boolean.
155  * If 'key' is not in 'smap', or its value is neither "true" nor "false",
156  * returns 'def'. */
157 bool
158 smap_get_bool(const struct smap *smap, const char *key, bool def)
159 {
160     const char *value = smap_get(smap, key);
161
162     if (!value) {
163         return def;
164     }
165
166     if (def) {
167         return strcasecmp("false", value) != 0;
168     } else {
169         return !strcasecmp("true", value);
170     }
171 }
172
173 /* Gets the value associated with 'key' in 'smap' and converts it to an int
174  * using atoi().  If 'key' is not in 'smap', returns 'def'. */
175 int
176 smap_get_int(const struct smap *smap, const char *key, int def)
177 {
178     const char *value = smap_get(smap, key);
179
180     return value ? atoi(value) : def;
181 }
182
183 /* Returns true of there are no elements in 'smap'. */
184 bool
185 smap_is_empty(const struct smap *smap)
186 {
187     return hmap_is_empty(&smap->map);
188 }
189
190 /* Returns the number of elements in 'smap'. */
191 size_t
192 smap_count(const struct smap *smap)
193 {
194     return hmap_count(&smap->map);
195 }
196
197 /* Initializes 'dst' as a clone of 'src. */
198 void
199 smap_clone(struct smap *dst, struct smap *src)
200 {
201     struct smap_node *node;
202
203     smap_init(dst);
204     SMAP_FOR_EACH (node, src) {
205         smap_add__(dst, xstrdup(node->key), xstrdup(node->value),
206                    node->node.hash);
207     }
208 }
209
210 /* Returns an array of nodes sorted on key or NULL if 'smap' is empty.  The
211  * caller is responsible for freeing this array. */
212 const struct smap_node **
213 smap_sort(const struct smap *smap)
214 {
215     if (smap_is_empty(smap)) {
216         return NULL;
217     } else {
218         const struct smap_node **nodes;
219         struct smap_node *node;
220         size_t i, n;
221
222         n = smap_count(smap);
223         nodes = xmalloc(n * sizeof *nodes);
224         i = 0;
225         SMAP_FOR_EACH (node, smap) {
226             nodes[i++] = node;
227         }
228         assert(i == n);
229
230         qsort(nodes, n, sizeof *nodes, compare_nodes_by_key);
231
232         return nodes;
233     }
234 }
235 \f
236 /* Private Helpers. */
237
238 static struct smap_node *
239 smap_add__(struct smap *smap, char *key, void *value, size_t hash)
240 {
241     struct smap_node *node = xmalloc(sizeof *node);
242     node->key = key;
243     node->value = value;
244     hmap_insert(&smap->map, &node->node, hash);
245     return node;
246 }
247
248 static struct smap_node *
249 smap_find__(const struct smap *smap, const char *key, size_t key_len,
250             size_t hash)
251 {
252     struct smap_node *node;
253
254     HMAP_FOR_EACH_WITH_HASH (node, node, hash, &smap->map) {
255         if (!strncmp(node->key, key, key_len) && !node->key[key_len]) {
256             return node;
257         }
258     }
259
260     return NULL;
261 }
262
263 static int
264 compare_nodes_by_key(const void *a_, const void *b_)
265 {
266     const struct smap_node *const *a = a_;
267     const struct smap_node *const *b = b_;
268     return strcmp((*a)->key, (*b)->key);
269 }