Coverage Report

Created: 2025-07-01 06:50

/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
}