/src/openvswitch/lib/sset.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2011, 2012, 2013, 2015, 2016 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 | | |
19 | | #include "sset.h" |
20 | | |
21 | | #include "openvswitch/dynamic-string.h" |
22 | | #include "hash.h" |
23 | | |
24 | | static uint32_t |
25 | | hash_name__(const char *name, size_t length) |
26 | 0 | { |
27 | 0 | return hash_bytes(name, length, 0); |
28 | 0 | } |
29 | | |
30 | | static uint32_t |
31 | | hash_name(const char *name) |
32 | 0 | { |
33 | 0 | return hash_name__(name, strlen(name)); |
34 | 0 | } |
35 | | |
36 | | static struct sset_node * |
37 | | sset_find__(const struct sset *set, const char *name, size_t hash) |
38 | 0 | { |
39 | 0 | struct sset_node *node; |
40 | |
|
41 | 0 | HMAP_FOR_EACH_WITH_HASH (node, hmap_node, hash, &set->map) { |
42 | 0 | if (!strcmp(node->name, name)) { |
43 | 0 | return node; |
44 | 0 | } |
45 | 0 | } |
46 | 0 | return NULL; |
47 | 0 | } |
48 | | |
49 | | static struct sset_node * |
50 | | sset_add__(struct sset *set, const char *name, size_t length, size_t hash) |
51 | 0 | { |
52 | 0 | struct sset_node *node = xmalloc(length + sizeof *node); |
53 | 0 | memcpy(node->name, name, length + 1); |
54 | 0 | hmap_insert(&set->map, &node->hmap_node, hash); |
55 | 0 | return node; |
56 | 0 | } |
57 | | |
58 | | /* Initializes 'set' as an empty set of strings. */ |
59 | | void |
60 | | sset_init(struct sset *set) |
61 | 0 | { |
62 | 0 | hmap_init(&set->map); |
63 | 0 | } |
64 | | |
65 | | /* Destroys 'sets'. */ |
66 | | void |
67 | | sset_destroy(struct sset *set) |
68 | 0 | { |
69 | 0 | if (set) { |
70 | 0 | sset_clear(set); |
71 | 0 | hmap_destroy(&set->map); |
72 | 0 | } |
73 | 0 | } |
74 | | |
75 | | /* Initializes 'set' to contain the same strings as 'orig'. */ |
76 | | void |
77 | | sset_clone(struct sset *set, const struct sset *orig) |
78 | 0 | { |
79 | 0 | struct sset_node *node; |
80 | |
|
81 | 0 | sset_init(set); |
82 | 0 | hmap_reserve(&set->map, sset_count(orig)); |
83 | |
|
84 | 0 | HMAP_FOR_EACH (node, hmap_node, &orig->map) { |
85 | 0 | sset_add__(set, node->name, strlen(node->name), |
86 | 0 | node->hmap_node.hash); |
87 | 0 | } |
88 | 0 | } |
89 | | |
90 | | /* Exchanges the contents of 'a' and 'b'. */ |
91 | | void |
92 | | sset_swap(struct sset *a, struct sset *b) |
93 | 0 | { |
94 | 0 | hmap_swap(&a->map, &b->map); |
95 | 0 | } |
96 | | |
97 | | /* Adjusts 'set' so that it is still valid after it has been moved around in |
98 | | * memory (e.g. due to realloc()). */ |
99 | | void |
100 | | sset_moved(struct sset *set) |
101 | 0 | { |
102 | 0 | hmap_moved(&set->map); |
103 | 0 | } |
104 | | |
105 | | /* Initializes 'set' with substrings of 's' that are delimited by any of the |
106 | | * characters in 'delimiters'. For example, |
107 | | * sset_from_delimited_string(&set, "a b,c", " ,"); |
108 | | * initializes 'set' with three strings "a", "b", and "c". */ |
109 | | void |
110 | | sset_from_delimited_string(struct sset *set, const char *s_, |
111 | | const char *delimiters) |
112 | 0 | { |
113 | 0 | sset_init(set); |
114 | |
|
115 | 0 | char *s = xstrdup(s_); |
116 | 0 | char *token, *save_ptr = NULL; |
117 | 0 | for (token = strtok_r(s, delimiters, &save_ptr); token != NULL; |
118 | 0 | token = strtok_r(NULL, delimiters, &save_ptr)) { |
119 | 0 | sset_add(set, token); |
120 | 0 | } |
121 | 0 | free(s); |
122 | 0 | } |
123 | | |
124 | | /* Returns a malloc()'d string that consists of the concatenation of all of the |
125 | | * strings in 'sset' in lexicographic order, each separated from the next by |
126 | | * 'delimiter' and followed by 'terminator'. For example: |
127 | | * |
128 | | * sset_join(("a", "b", "c"), ", ", ".") -> "a, b, c." |
129 | | * sset_join(("xyzzy"), ", ", ".") -> "xyzzy." |
130 | | * sset_join((""), ", ", ".") -> "." |
131 | | * |
132 | | * The caller is responsible for freeing the returned string (with free()). |
133 | | */ |
134 | | char * |
135 | | sset_join(const struct sset *sset, |
136 | | const char *delimiter, const char *terminator) |
137 | 0 | { |
138 | 0 | struct ds s = DS_EMPTY_INITIALIZER; |
139 | |
|
140 | 0 | const char **names = sset_sort(sset); |
141 | 0 | for (size_t i = 0; i < sset_count(sset); i++) { |
142 | 0 | if (i) { |
143 | 0 | ds_put_cstr(&s, delimiter); |
144 | 0 | } |
145 | 0 | ds_put_cstr(&s, names[i]); |
146 | 0 | } |
147 | 0 | free(names); |
148 | |
|
149 | 0 | ds_put_cstr(&s, terminator); |
150 | |
|
151 | 0 | return ds_steal_cstr(&s); |
152 | 0 | } |
153 | | |
154 | | /* Returns true if 'set' contains no strings, false if it contains at least one |
155 | | * string. */ |
156 | | bool |
157 | | sset_is_empty(const struct sset *set) |
158 | 0 | { |
159 | 0 | return hmap_is_empty(&set->map); |
160 | 0 | } |
161 | | |
162 | | /* Returns the number of strings in 'set'. */ |
163 | | size_t |
164 | | sset_count(const struct sset *set) |
165 | 0 | { |
166 | 0 | return hmap_count(&set->map); |
167 | 0 | } |
168 | | |
169 | | /* Adds 'name' to 'set'. If 'name' is new, returns the new sset_node; |
170 | | * otherwise (if a copy of 'name' already existed in 'set'), returns NULL. */ |
171 | | struct sset_node * |
172 | | sset_add(struct sset *set, const char *name) |
173 | 0 | { |
174 | 0 | size_t length = strlen(name); |
175 | 0 | uint32_t hash = hash_name__(name, length); |
176 | |
|
177 | 0 | return (sset_find__(set, name, hash) |
178 | 0 | ? NULL |
179 | 0 | : sset_add__(set, name, length, hash)); |
180 | 0 | } |
181 | | |
182 | | /* Adds a copy of 'name' to 'set' and frees 'name'. |
183 | | * |
184 | | * If 'name' is new, returns the new sset_node; otherwise (if a copy of 'name' |
185 | | * already existed in 'set'), returns NULL. */ |
186 | | struct sset_node * |
187 | | sset_add_and_free(struct sset *set, char *name) |
188 | 0 | { |
189 | 0 | struct sset_node *node = sset_add(set, name); |
190 | 0 | free(name); |
191 | 0 | return node; |
192 | 0 | } |
193 | | |
194 | | /* Adds 'name' to 'set'. Assert-fails if a copy of 'name' was already in |
195 | | * 'set'. */ |
196 | | void |
197 | | sset_add_assert(struct sset *set, const char *name) |
198 | 0 | { |
199 | 0 | ovs_assert(sset_add(set, name)); |
200 | 0 | } |
201 | | |
202 | | /* Adds a copy of each of the 'n' names in 'names' to 'set'. */ |
203 | | void |
204 | | sset_add_array(struct sset *set, char **names, size_t n) |
205 | 0 | { |
206 | 0 | size_t i; |
207 | |
|
208 | 0 | for (i = 0; i < n; i++) { |
209 | 0 | sset_add(set, names[i]); |
210 | 0 | } |
211 | 0 | } |
212 | | |
213 | | /* Removes all of the strings from 'set'. */ |
214 | | void |
215 | | sset_clear(struct sset *set) |
216 | 0 | { |
217 | 0 | const char *name; |
218 | |
|
219 | 0 | SSET_FOR_EACH_SAFE (name, set) { |
220 | 0 | sset_delete(set, SSET_NODE_FROM_NAME(name)); |
221 | 0 | } |
222 | 0 | } |
223 | | |
224 | | /* Deletes 'node' from 'set' and frees 'node'. */ |
225 | | void |
226 | | sset_delete(struct sset *set, struct sset_node *node) |
227 | 0 | { |
228 | 0 | hmap_remove(&set->map, &node->hmap_node); |
229 | 0 | free(node); |
230 | 0 | } |
231 | | |
232 | | /* Searches for 'name' in 'set'. If found, deletes it and returns true. If |
233 | | * not found, returns false without modifying 'set'. */ |
234 | | bool |
235 | | sset_find_and_delete(struct sset *set, const char *name) |
236 | 0 | { |
237 | 0 | struct sset_node *node = sset_find(set, name); |
238 | 0 | if (node) { |
239 | 0 | sset_delete(set, node); |
240 | 0 | } |
241 | 0 | return node != NULL; |
242 | 0 | } |
243 | | |
244 | | /* Searches for 'name' in 'set' and deletes it. Assert-fails if 'name' is not |
245 | | * in 'set'. */ |
246 | | void |
247 | | sset_find_and_delete_assert(struct sset *set, const char *name) |
248 | 0 | { |
249 | 0 | ovs_assert(sset_find_and_delete(set, name)); |
250 | 0 | } |
251 | | |
252 | | /* Removes a string from 'set' and returns a copy of it. The caller must free |
253 | | * the returned string (with free()). |
254 | | * |
255 | | * 'set' must not be empty. |
256 | | * |
257 | | * This is not a very good way to iterate through an sset: it copies each name |
258 | | * and it takes O(n**2) time to remove all the names. Use SSET_FOR_EACH_SAFE |
259 | | * instead, if you can. */ |
260 | | char * |
261 | | sset_pop(struct sset *set) |
262 | 0 | { |
263 | 0 | const char *name = SSET_FIRST(set); |
264 | |
|
265 | 0 | if (!name) { |
266 | 0 | return NULL; |
267 | 0 | } |
268 | | |
269 | 0 | char *copy = xstrdup(name); |
270 | 0 | sset_delete(set, SSET_NODE_FROM_NAME(name)); |
271 | 0 | return copy; |
272 | 0 | } |
273 | | |
274 | | /* Searches for 'name' in 'set'. Returns its node, if found, otherwise a null |
275 | | * pointer. */ |
276 | | struct sset_node * |
277 | | sset_find(const struct sset *set, const char *name) |
278 | 0 | { |
279 | 0 | return sset_find__(set, name, hash_name(name)); |
280 | 0 | } |
281 | | |
282 | | /* Returns true if 'set' contains a copy of 'name', false otherwise. */ |
283 | | bool |
284 | | sset_contains(const struct sset *set, const char *name) |
285 | 0 | { |
286 | 0 | return sset_find(set, name) != NULL; |
287 | 0 | } |
288 | | |
289 | | /* Returns true if 'a' and 'b' contain the same strings, false otherwise. */ |
290 | | bool |
291 | | sset_equals(const struct sset *a, const struct sset *b) |
292 | 0 | { |
293 | 0 | struct sset_node *node; |
294 | |
|
295 | 0 | if (sset_count(a) != sset_count(b)) { |
296 | 0 | return false; |
297 | 0 | } |
298 | | |
299 | 0 | HMAP_FOR_EACH (node, hmap_node, &a->map) { |
300 | 0 | if (!sset_find__(b, node->name, node->hmap_node.hash)) { |
301 | 0 | return false; |
302 | 0 | } |
303 | 0 | } |
304 | | |
305 | 0 | return true; |
306 | 0 | } |
307 | | |
308 | | /* Returns the next node in 'set' in hash order, or NULL if no nodes remain in |
309 | | * 'set'. Uses '*pos' to determine where to begin iteration, and updates |
310 | | * '*pos' to pass on the next iteration into them before returning. |
311 | | * |
312 | | * It's better to use plain SSET_FOR_EACH and related functions, since they are |
313 | | * faster and better at dealing with ssets that change during iteration. |
314 | | * |
315 | | * Before beginning iteration, set '*pos' to all zeros. */ |
316 | | struct sset_node * |
317 | | sset_at_position(const struct sset *set, struct sset_position *pos) |
318 | 0 | { |
319 | 0 | struct hmap_node *hmap_node; |
320 | |
|
321 | 0 | hmap_node = hmap_at_position(&set->map, &pos->pos); |
322 | 0 | return hmap_node |
323 | 0 | ? SSET_NODE_FROM_HMAP_NODE(hmap_node) |
324 | 0 | : NULL; |
325 | 0 | } |
326 | | |
327 | | /* Replaces 'a' by the intersection of 'a' and 'b'. That is, removes from 'a' |
328 | | * all of the strings that are not also in 'b'. */ |
329 | | void |
330 | | sset_intersect(struct sset *a, const struct sset *b) |
331 | 0 | { |
332 | 0 | const char *name; |
333 | |
|
334 | 0 | SSET_FOR_EACH_SAFE (name, a) { |
335 | 0 | if (!sset_contains(b, name)) { |
336 | 0 | sset_delete(a, SSET_NODE_FROM_NAME(name)); |
337 | 0 | } |
338 | 0 | } |
339 | 0 | } |
340 | | |
341 | | /* Returns a null-terminated array of pointers to the strings in 'set', in no |
342 | | * particular order. The caller must free the returned array when it is no |
343 | | * longer needed, but the strings in the array belong to 'set' and thus must |
344 | | * not be modified or freed. */ |
345 | | const char ** |
346 | | sset_array(const struct sset *set) |
347 | 0 | { |
348 | 0 | size_t n = sset_count(set); |
349 | 0 | const char **array; |
350 | 0 | const char *s; |
351 | 0 | size_t i; |
352 | |
|
353 | 0 | array = xmalloc(sizeof *array * (n + 1)); |
354 | 0 | i = 0; |
355 | 0 | SSET_FOR_EACH (s, set) { |
356 | 0 | array[i++] = s; |
357 | 0 | } |
358 | 0 | ovs_assert(i == n); |
359 | 0 | array[n] = NULL; |
360 | |
|
361 | 0 | return array; |
362 | 0 | } |
363 | | |
364 | | static int |
365 | | compare_string_pointers(const void *a_, const void *b_) |
366 | 0 | { |
367 | 0 | const char *const *a = a_; |
368 | 0 | const char *const *b = b_; |
369 | |
|
370 | 0 | return strcmp(*a, *b); |
371 | 0 | } |
372 | | |
373 | | /* Returns a null-terminated array of pointers to the strings in 'set', sorted |
374 | | * alphabetically. The caller must free the returned array when it is no |
375 | | * longer needed, but the strings in the array belong to 'set' and thus must |
376 | | * not be modified or freed. */ |
377 | | const char ** |
378 | | sset_sort(const struct sset *set) |
379 | 0 | { |
380 | 0 | const char **array = sset_array(set); |
381 | 0 | qsort(array, sset_count(set), sizeof *array, compare_string_pointers); |
382 | 0 | return array; |
383 | 0 | } |