/src/openvswitch/lib/shash.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc. |
3 | | * |
4 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | * you may not use this file except in compliance with the License. |
6 | | * You may obtain a copy of the License at: |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | |
17 | | #include <config.h> |
18 | | #include "openvswitch/shash.h" |
19 | | #include "hash.h" |
20 | | #include "util.h" |
21 | | |
22 | | static struct shash_node *shash_find__(const struct shash *, |
23 | | const char *name, size_t name_len, |
24 | | size_t hash); |
25 | | |
26 | | static size_t |
27 | | hash_name(const char *name) |
28 | 0 | { |
29 | 0 | return hash_string(name, 0); |
30 | 0 | } |
31 | | |
32 | | void |
33 | | shash_init(struct shash *sh) |
34 | 0 | { |
35 | 0 | hmap_init(&sh->map); |
36 | 0 | } |
37 | | |
38 | | void |
39 | | shash_destroy(struct shash *sh) |
40 | 0 | { |
41 | 0 | if (sh) { |
42 | 0 | shash_clear(sh); |
43 | 0 | hmap_destroy(&sh->map); |
44 | 0 | } |
45 | 0 | } |
46 | | |
47 | | /* Like shash_destroy(), but also free() each node's 'data'. */ |
48 | | void |
49 | | shash_destroy_free_data(struct shash *sh) |
50 | 0 | { |
51 | 0 | if (sh) { |
52 | 0 | shash_clear_free_data(sh); |
53 | 0 | hmap_destroy(&sh->map); |
54 | 0 | } |
55 | 0 | } |
56 | | |
57 | | void |
58 | | shash_swap(struct shash *a, struct shash *b) |
59 | 0 | { |
60 | 0 | hmap_swap(&a->map, &b->map); |
61 | 0 | } |
62 | | |
63 | | void |
64 | | shash_moved(struct shash *sh) |
65 | 0 | { |
66 | 0 | hmap_moved(&sh->map); |
67 | 0 | } |
68 | | |
69 | | void |
70 | | shash_clear(struct shash *sh) |
71 | 0 | { |
72 | 0 | struct shash_node *node; |
73 | |
|
74 | 0 | SHASH_FOR_EACH_SAFE (node, sh) { |
75 | 0 | hmap_remove(&sh->map, &node->node); |
76 | 0 | free(node->name); |
77 | 0 | free(node); |
78 | 0 | } |
79 | 0 | } |
80 | | |
81 | | /* Like shash_clear(), but also free() each node's 'data'. */ |
82 | | void |
83 | | shash_clear_free_data(struct shash *sh) |
84 | 0 | { |
85 | 0 | struct shash_node *node; |
86 | |
|
87 | 0 | SHASH_FOR_EACH_SAFE (node, sh) { |
88 | 0 | hmap_remove(&sh->map, &node->node); |
89 | 0 | free(node->data); |
90 | 0 | free(node->name); |
91 | 0 | free(node); |
92 | 0 | } |
93 | 0 | } |
94 | | |
95 | | bool |
96 | | shash_is_empty(const struct shash *shash) |
97 | 0 | { |
98 | 0 | return hmap_is_empty(&shash->map); |
99 | 0 | } |
100 | | |
101 | | size_t |
102 | | shash_count(const struct shash *shash) |
103 | 0 | { |
104 | 0 | ovs_assert(shash); |
105 | 0 | return hmap_count(&shash->map); |
106 | 0 | } |
107 | | |
108 | | static struct shash_node * |
109 | | shash_add_nocopy__(struct shash *sh, char *name, const void *data, size_t hash) |
110 | 0 | { |
111 | 0 | struct shash_node *node = xmalloc(sizeof *node); |
112 | 0 | node->name = name; |
113 | 0 | node->data = CONST_CAST(void *, data); |
114 | 0 | hmap_insert(&sh->map, &node->node, hash); |
115 | 0 | return node; |
116 | 0 | } |
117 | | |
118 | | /* It is the caller's responsibility to avoid duplicate names, if that is |
119 | | * desirable. */ |
120 | | struct shash_node * |
121 | | shash_add_nocopy(struct shash *sh, char *name, const void *data) |
122 | 0 | { |
123 | 0 | return shash_add_nocopy__(sh, name, data, hash_name(name)); |
124 | 0 | } |
125 | | |
126 | | /* It is the caller's responsibility to avoid duplicate names, if that is |
127 | | * desirable. */ |
128 | | struct shash_node * |
129 | | shash_add(struct shash *sh, const char *name, const void *data) |
130 | 0 | { |
131 | 0 | return shash_add_nocopy(sh, xstrdup(name), data); |
132 | 0 | } |
133 | | |
134 | | bool |
135 | | shash_add_once(struct shash *sh, const char *name, const void *data) |
136 | 0 | { |
137 | 0 | if (!shash_find(sh, name)) { |
138 | 0 | shash_add(sh, name, data); |
139 | 0 | return true; |
140 | 0 | } else { |
141 | 0 | return false; |
142 | 0 | } |
143 | 0 | } |
144 | | |
145 | | void |
146 | | shash_add_assert(struct shash *sh, const char *name, const void *data) |
147 | 0 | { |
148 | 0 | ovs_assert(shash_add_once(sh, name, data)); |
149 | 0 | } |
150 | | |
151 | | /* Searches for 'name' in 'sh'. If it does not already exist, adds it along |
152 | | * with 'data' and returns NULL. If it does already exist, replaces its data |
153 | | * by 'data' and returns the data that it formerly contained. */ |
154 | | void * |
155 | | shash_replace(struct shash *sh, const char *name, const void *data) |
156 | 0 | { |
157 | 0 | size_t hash = hash_name(name); |
158 | 0 | struct shash_node *node; |
159 | |
|
160 | 0 | node = shash_find__(sh, name, strlen(name), hash); |
161 | 0 | if (!node) { |
162 | 0 | shash_add_nocopy__(sh, xstrdup(name), data, hash); |
163 | 0 | return NULL; |
164 | 0 | } else { |
165 | 0 | void *old_data = node->data; |
166 | 0 | node->data = CONST_CAST(void *, data); |
167 | 0 | return old_data; |
168 | 0 | } |
169 | 0 | } |
170 | | |
171 | | /* Searches for 'name' in 'sh'. If it does not already exist, adds it along |
172 | | * with 'data' and returns NULL. If it does already exist, replaces its data |
173 | | * by 'data' and returns the data that it formerly contained. |
174 | | * |
175 | | * Takes ownership of 'name'. */ |
176 | | void * |
177 | | shash_replace_nocopy(struct shash *sh, char *name, const void *data) |
178 | 0 | { |
179 | 0 | size_t hash = hash_name(name); |
180 | 0 | struct shash_node *node; |
181 | |
|
182 | 0 | node = shash_find__(sh, name, strlen(name), hash); |
183 | 0 | if (!node) { |
184 | 0 | shash_add_nocopy__(sh, name, data, hash); |
185 | 0 | return NULL; |
186 | 0 | } else { |
187 | 0 | free(name); |
188 | 0 | void *old_data = node->data; |
189 | 0 | node->data = CONST_CAST(void *, data); |
190 | 0 | return old_data; |
191 | 0 | } |
192 | 0 | } |
193 | | |
194 | | /* Deletes 'node' from 'sh' and frees the node's name. The caller is still |
195 | | * responsible for freeing the node's data, if necessary. */ |
196 | | void |
197 | | shash_delete(struct shash *sh, struct shash_node *node) |
198 | 0 | { |
199 | 0 | free(shash_steal(sh, node)); |
200 | 0 | } |
201 | | |
202 | | /* Deletes 'node' from 'sh'. Neither the node's name nor its data is freed; |
203 | | * instead, ownership is transferred to the caller. Returns the node's |
204 | | * name. */ |
205 | | char * |
206 | | shash_steal(struct shash *sh, struct shash_node *node) |
207 | 0 | { |
208 | 0 | if (!node) { |
209 | 0 | return NULL; |
210 | 0 | } |
211 | | |
212 | 0 | char *name = node->name; |
213 | |
|
214 | 0 | hmap_remove(&sh->map, &node->node); |
215 | 0 | free(node); |
216 | 0 | return name; |
217 | 0 | } |
218 | | |
219 | | static struct shash_node * |
220 | | shash_find__(const struct shash *sh, const char *name, size_t name_len, |
221 | | size_t hash) |
222 | 0 | { |
223 | 0 | struct shash_node *node; |
224 | |
|
225 | 0 | HMAP_FOR_EACH_WITH_HASH (node, node, hash, &sh->map) { |
226 | 0 | if (!strncmp(node->name, name, name_len) && !node->name[name_len]) { |
227 | 0 | return node; |
228 | 0 | } |
229 | 0 | } |
230 | 0 | return NULL; |
231 | 0 | } |
232 | | |
233 | | /* If there are duplicates, returns a random element. */ |
234 | | struct shash_node * |
235 | | shash_find(const struct shash *sh, const char *name) |
236 | 0 | { |
237 | 0 | return shash_find__(sh, name, strlen(name), hash_name(name)); |
238 | 0 | } |
239 | | |
240 | | /* Finds and returns a shash_node within 'sh' that has the given 'name' that is |
241 | | * exactly 'len' bytes long. Returns NULL if no node in 'sh' has that name. */ |
242 | | struct shash_node * |
243 | | shash_find_len(const struct shash *sh, const char *name, size_t len) |
244 | 0 | { |
245 | 0 | return shash_find__(sh, name, len, hash_bytes(name, len, 0)); |
246 | 0 | } |
247 | | |
248 | | void * |
249 | | shash_find_data(const struct shash *sh, const char *name) |
250 | 0 | { |
251 | 0 | struct shash_node *node = shash_find(sh, name); |
252 | 0 | return node ? node->data : NULL; |
253 | 0 | } |
254 | | |
255 | | void * |
256 | | shash_find_and_delete(struct shash *sh, const char *name) |
257 | 0 | { |
258 | 0 | struct shash_node *node = shash_find(sh, name); |
259 | 0 | if (node) { |
260 | 0 | void *data = node->data; |
261 | 0 | shash_delete(sh, node); |
262 | 0 | return data; |
263 | 0 | } else { |
264 | 0 | return NULL; |
265 | 0 | } |
266 | 0 | } |
267 | | |
268 | | void * |
269 | | shash_find_and_delete_assert(struct shash *sh, const char *name) |
270 | 0 | { |
271 | 0 | void *data = shash_find_and_delete(sh, name); |
272 | 0 | ovs_assert(data); |
273 | 0 | return data; |
274 | 0 | } |
275 | | |
276 | | struct shash_node * |
277 | | shash_first(const struct shash *shash) |
278 | 0 | { |
279 | 0 | struct hmap_node *node = hmap_first(&shash->map); |
280 | 0 | return node ? CONTAINER_OF(node, struct shash_node, node) : NULL; |
281 | 0 | } |
282 | | |
283 | | static int |
284 | | compare_nodes_by_name(const void *a_, const void *b_) |
285 | 0 | { |
286 | 0 | const struct shash_node *const *a = a_; |
287 | 0 | const struct shash_node *const *b = b_; |
288 | 0 | return strcmp((*a)->name, (*b)->name); |
289 | 0 | } |
290 | | |
291 | | const struct shash_node ** |
292 | | shash_sort(const struct shash *sh) |
293 | 0 | { |
294 | 0 | if (shash_is_empty(sh)) { |
295 | 0 | return NULL; |
296 | 0 | } else { |
297 | 0 | const struct shash_node **nodes; |
298 | 0 | struct shash_node *node; |
299 | 0 | size_t i, n; |
300 | |
|
301 | 0 | n = shash_count(sh); |
302 | 0 | nodes = xmalloc(n * sizeof *nodes); |
303 | 0 | i = 0; |
304 | 0 | SHASH_FOR_EACH (node, sh) { |
305 | 0 | nodes[i++] = node; |
306 | 0 | } |
307 | 0 | ovs_assert(i == n); |
308 | |
|
309 | 0 | qsort(nodes, n, sizeof *nodes, compare_nodes_by_name); |
310 | |
|
311 | 0 | return nodes; |
312 | 0 | } |
313 | 0 | } |
314 | | |
315 | | /* Returns true if 'a' and 'b' contain the same keys (regardless of their |
316 | | * values), false otherwise. */ |
317 | | bool |
318 | | shash_equal_keys(const struct shash *a, const struct shash *b) |
319 | 0 | { |
320 | 0 | struct shash_node *node; |
321 | |
|
322 | 0 | if (hmap_count(&a->map) != hmap_count(&b->map)) { |
323 | 0 | return false; |
324 | 0 | } |
325 | 0 | SHASH_FOR_EACH (node, a) { |
326 | 0 | if (!shash_find(b, node->name)) { |
327 | 0 | return false; |
328 | 0 | } |
329 | 0 | } |
330 | 0 | return true; |
331 | 0 | } |
332 | | |
333 | | /* Chooses and returns a randomly selected node from 'sh', which must not be |
334 | | * empty. |
335 | | * |
336 | | * I wouldn't depend on this algorithm to be fair, since I haven't analyzed it. |
337 | | * But it does at least ensure that any node in 'sh' can be chosen. */ |
338 | | struct shash_node * |
339 | | shash_random_node(struct shash *sh) |
340 | 0 | { |
341 | 0 | return CONTAINER_OF(hmap_random_node(&sh->map), struct shash_node, node); |
342 | 0 | } |