Coverage Report

Created: 2025-07-11 06:12

/src/openvswitch/lib/smap.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (c) 2012, 2014, 2015, 2016 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 <strings.h>
19
20
#include "hash.h"
21
#include "openvswitch/json.h"
22
#include "packets.h"
23
#include "util.h"
24
#include "uuid.h"
25
26
static struct smap_node *smap_add__(struct smap *, char *, void *,
27
                                    size_t hash);
28
static struct smap_node *smap_find__(const struct smap *, const char *key,
29
                                     size_t key_len, size_t hash);
30
static int compare_nodes_by_key(const void *, const void *);
31

32
/* Public Functions. */
33
34
void
35
smap_init(struct smap *smap)
36
0
{
37
0
    hmap_init(&smap->map);
38
0
}
39
40
void
41
smap_destroy(struct smap *smap)
42
0
{
43
0
    if (smap) {
44
0
        smap_clear(smap);
45
0
        hmap_destroy(&smap->map);
46
0
    }
47
0
}
48
49
/* Adds 'key' paired with 'value' to 'smap'.  It is the caller's responsibility
50
 * to avoid duplicate keys if desirable. */
51
struct smap_node *
52
smap_add(struct smap *smap, const char *key, const char *value)
53
0
{
54
0
    size_t key_len = strlen(key);
55
0
    return smap_add__(smap, xmemdup0(key, key_len), xstrdup(value),
56
0
                      hash_bytes(key, key_len, 0));
57
0
}
58
59
/* Adds 'key' paired with 'value' to 'smap'.  Takes ownership of 'key' and
60
 * 'value' (which will eventually be freed with free()).  It is the caller's
61
 * responsibility to avoid duplicate keys if desirable. */
62
struct smap_node *
63
smap_add_nocopy(struct smap *smap, char *key, char *value)
64
0
{
65
0
    return smap_add__(smap, key, value, hash_bytes(key, strlen(key), 0));
66
0
}
67
68
/* Attempts to add 'key' to 'smap' associated with 'value'.  If 'key' already
69
 * exists in 'smap', does nothing and returns false.  Otherwise, performs the
70
 * addition and returns true. */
71
bool
72
smap_add_once(struct smap *smap, const char *key, const char *value)
73
0
{
74
0
    if (!smap_get(smap, key)) {
75
0
        smap_add(smap, key, value);
76
0
        return true;
77
0
    } else {
78
0
        return false;
79
0
    }
80
0
}
81
82
/* Adds 'key' paired with a value derived from 'format' (similar to printf).
83
 * It is the caller's responsibility to avoid duplicate keys if desirable. */
84
void
85
smap_add_format(struct smap *smap, const char *key, const char *format, ...)
86
0
{
87
0
    size_t key_len;
88
0
    va_list args;
89
0
    char *value;
90
91
0
    va_start(args, format);
92
0
    value = xvasprintf(format, args);
93
0
    va_end(args);
94
95
0
    key_len = strlen(key);
96
0
    smap_add__(smap, xmemdup0(key, key_len), value,
97
0
               hash_bytes(key, key_len, 0));
98
0
}
99
100
/* Adds 'key' paired with a string representation of 'addr'. It is the
101
 * caller's responsibility to avoid duplicate keys if desirable. */
102
void
103
smap_add_ipv6(struct smap *smap, const char *key, const struct in6_addr *addr)
104
0
{
105
0
    char buf[INET6_ADDRSTRLEN];
106
0
    ipv6_string_mapped(buf, addr);
107
0
    smap_add(smap, key, buf);
108
0
}
109
110
/* Searches for 'key' in 'smap'.  If it does not already exists, adds it.
111
 * Otherwise, changes its value to 'value'.  The caller retains ownership of
112
 * 'value'. */
113
void
114
smap_replace(struct smap *smap, const char *key, const char *value)
115
0
{
116
0
    smap_replace_nocopy(smap, key, xstrdup(value));
117
0
}
118
119
/* Searches for 'key' in 'smap'.  If it does not already exists, adds it.
120
 * Otherwise, changes its value to 'value'.  Takes ownership of 'value'. */
121
void
122
smap_replace_nocopy(struct smap *smap, const char *key, char *value)
123
0
{
124
0
    size_t  key_len = strlen(key);
125
0
    size_t hash = hash_bytes(key, key_len, 0);
126
127
0
    struct smap_node *node;
128
129
0
    node = smap_find__(smap, key, key_len, hash);
130
0
    if (node) {
131
0
        free(node->value);
132
0
        node->value = value;
133
0
    } else {
134
0
        smap_add__(smap, xmemdup0(key, key_len), value, hash);
135
0
    }
136
0
}
137
138
/* If 'key' is in 'smap', removes it.  Otherwise does nothing. */
139
void
140
smap_remove(struct smap *smap, const char *key)
141
0
{
142
0
    struct smap_node *node = smap_get_node(smap, key);
143
144
0
    if (node) {
145
0
        smap_remove_node(smap, node);
146
0
    }
147
0
}
148
149
/* Removes 'node' from 'smap'. */
150
void
151
smap_remove_node(struct smap *smap, struct smap_node *node)
152
0
{
153
0
    hmap_remove(&smap->map, &node->node);
154
0
    free(node->key);
155
0
    free(node->value);
156
0
    free(node);
157
0
}
158
159
/* Deletes 'node' from 'smap'.
160
 *
161
 * If 'keyp' is nonnull, stores the node's key in '*keyp' and transfers
162
 * ownership to the caller.  Otherwise, frees the node's key.  Similarly for
163
 * 'valuep' and the node's value. */
164
void
165
smap_steal(struct smap *smap, struct smap_node *node,
166
           char **keyp, char **valuep)
167
0
{
168
0
    if (keyp) {
169
0
        *keyp = node->key;
170
0
    } else {
171
0
        free(node->key);
172
0
    }
173
174
0
    if (valuep) {
175
0
        *valuep = node->value;
176
0
    } else {
177
0
        free(node->value);
178
0
    }
179
180
0
    hmap_remove(&smap->map, &node->node);
181
0
    free(node);
182
0
}
183
184
/* Removes all key-value pairs from 'smap'. */
185
void
186
smap_clear(struct smap *smap)
187
0
{
188
0
    struct smap_node *node;
189
190
0
    SMAP_FOR_EACH_SAFE (node, smap) {
191
0
        smap_remove_node(smap, node);
192
0
    }
193
0
}
194
195
/* Returns the value associated with 'key' in 'smap'.
196
 * If 'smap' does not contain 'key', returns NULL. */
197
const char *
198
smap_get(const struct smap *smap, const char *key)
199
0
{
200
0
    return smap_get_def(smap, key, NULL);
201
0
}
202
203
/* Returns the value associated with 'key' in 'smap'.
204
 * If 'smap' does not contain 'key', returns 'def'. */
205
const char *
206
smap_get_def(const struct smap *smap, const char *key, const char *def)
207
0
{
208
0
    struct smap_node *node = smap_get_node(smap, key);
209
0
    return node ? node->value : def;
210
0
}
211
212
/* Returns the node associated with 'key' in 'smap', or NULL. */
213
struct smap_node *
214
smap_get_node(const struct smap *smap, const char *key)
215
0
{
216
0
    size_t key_len = strlen(key);
217
0
    return smap_find__(smap, key, key_len, hash_bytes(key, key_len, 0));
218
0
}
219
220
/* Gets the value associated with 'key' in 'smap' and converts it to a boolean.
221
 * If 'key' is not in 'smap', or its value is neither "true" nor "false",
222
 * returns 'def'. */
223
bool
224
smap_get_bool(const struct smap *smap, const char *key, bool def)
225
0
{
226
0
    const char *value = smap_get_def(smap, key, "");
227
0
    if (def) {
228
0
        return strcasecmp("false", value) != 0;
229
0
    } else {
230
0
        return !strcasecmp("true", value);
231
0
    }
232
0
}
233
234
/* Gets the value associated with 'key' in 'smap' and converts it to an int.
235
 * If 'key' is not in 'smap' or a valid integer can't be parsed from it's
236
 * value, returns 'def'. */
237
int
238
smap_get_int(const struct smap *smap, const char *key, int def)
239
0
{
240
0
    const char *value = smap_get(smap, key);
241
0
    int i_value;
242
243
0
    if (!value || !str_to_int(value, 10, &i_value)) {
244
0
        return def;
245
0
    }
246
247
0
    return i_value;
248
0
}
249
250
/* Gets the value associated with 'key' in 'smap' and converts it to an
251
 * unsigned int. If 'key' is not in 'smap' or a valid unsigned integer
252
 * can't be parsed from it's value, returns 'def'. */
253
unsigned int
254
smap_get_uint(const struct smap *smap, const char *key, unsigned int def)
255
0
{
256
0
    const char *value = smap_get(smap, key);
257
0
    unsigned int u_value;
258
259
0
    if (!value || !str_to_uint(value, 10, &u_value)) {
260
0
        return def;
261
0
    }
262
263
0
    return u_value;
264
0
}
265
266
/* Gets the value associated with 'key' in 'smap' and converts it to an
267
 * unsigned long long.  If 'key' is not in 'smap' or a valid number can't be
268
 * parsed from it's value, returns 'def'. */
269
unsigned long long int
270
smap_get_ullong(const struct smap *smap, const char *key,
271
                unsigned long long def)
272
0
{
273
0
    const char *value = smap_get(smap, key);
274
0
    unsigned long long ull_value;
275
276
0
    if (!value || !str_to_ullong(value, 10, &ull_value)) {
277
0
        return def;
278
0
    }
279
280
0
    return ull_value;
281
0
}
282
283
/* Gets the value associated with 'key' in 'smap' and converts it to a UUID
284
 * using uuid_from_string().  Returns true if successful, false if 'key' is not
285
 * in 'smap' or if 'key' does not have the correct syntax for a UUID. */
286
bool
287
smap_get_uuid(const struct smap *smap, const char *key, struct uuid *uuid)
288
0
{
289
0
    return uuid_from_string(uuid, smap_get_def(smap, key, ""));
290
0
}
291
292
/* Returns true of there are no elements in 'smap'. */
293
bool
294
smap_is_empty(const struct smap *smap)
295
0
{
296
0
    return hmap_is_empty(&smap->map);
297
0
}
298
299
/* Returns the number of elements in 'smap'. */
300
size_t
301
smap_count(const struct smap *smap)
302
0
{
303
0
    ovs_assert(smap);
304
0
    return hmap_count(&smap->map);
305
0
}
306
307
/* Initializes 'dst' as a clone of 'src. */
308
void
309
smap_clone(struct smap *dst, const struct smap *src)
310
0
{
311
0
    const struct smap_node *node;
312
313
0
    smap_init(dst);
314
0
    hmap_reserve(&dst->map, smap_count(src));
315
316
0
    SMAP_FOR_EACH (node, src) {
317
0
        smap_add__(dst, xstrdup(node->key), xstrdup(node->value),
318
0
                   node->node.hash);
319
0
    }
320
0
}
321
322
/* Returns an array of nodes sorted on key or NULL if 'smap' is empty.  The
323
 * caller is responsible for freeing this array. */
324
const struct smap_node **
325
smap_sort(const struct smap *smap)
326
0
{
327
0
    if (smap_is_empty(smap)) {
328
0
        return NULL;
329
0
    } else {
330
0
        const struct smap_node **nodes;
331
0
        struct smap_node *node;
332
0
        size_t i, n;
333
334
0
        n = smap_count(smap);
335
0
        nodes = xmalloc(n * sizeof *nodes);
336
0
        i = 0;
337
0
        SMAP_FOR_EACH (node, smap) {
338
0
            nodes[i++] = node;
339
0
        }
340
0
        ovs_assert(i == n);
341
342
0
        qsort(nodes, n, sizeof *nodes, compare_nodes_by_key);
343
344
0
        return nodes;
345
0
    }
346
0
}
347
348
/* Adds each of the key-value pairs from 'json' (which must be a JSON object
349
 * whose values are strings) to 'smap'.
350
 *
351
 * The caller must have initialized 'smap'.
352
 *
353
 * The caller retains ownership of 'json' and everything in it. */
354
void
355
smap_from_json(struct smap *smap, const struct json *json)
356
0
{
357
0
    const struct shash_node *node;
358
359
0
    SHASH_FOR_EACH (node, json_object(json)) {
360
0
        const struct json *value = node->data;
361
0
        smap_add(smap, node->name, json_string(value));
362
0
    }
363
0
}
364
365
/* Returns a JSON object that maps from the keys in 'smap' to their values.
366
 *
367
 * The caller owns the returned value and must eventually json_destroy() it.
368
 *
369
 * The caller retains ownership of 'smap' and everything in it. */
370
struct json *
371
smap_to_json(const struct smap *smap)
372
0
{
373
0
    const struct smap_node *node;
374
0
    struct json *json;
375
376
0
    json = json_object_create();
377
0
    SMAP_FOR_EACH (node, smap) {
378
0
        json_object_put_string(json, node->key, node->value);
379
0
    }
380
0
    return json;
381
0
}
382
383
/* Returns true if the two maps are equal, meaning that they have the same set
384
 * of key-value pairs.
385
 */
386
bool
387
smap_equal(const struct smap *smap1, const struct smap *smap2)
388
0
{
389
0
    if (smap_count(smap1) != smap_count(smap2)) {
390
0
        return false;
391
0
    }
392
393
0
    const struct smap_node *node;
394
0
    SMAP_FOR_EACH (node, smap1) {
395
0
        const char *value2 = smap_get(smap2, node->key);
396
0
        if (!value2 || strcmp(node->value, value2)) {
397
0
            return false;
398
0
        }
399
0
    }
400
0
    return true;
401
0
}
402

403
/* Private Helpers. */
404
405
static struct smap_node *
406
smap_add__(struct smap *smap, char *key, void *value, size_t hash)
407
0
{
408
0
    struct smap_node *node = xmalloc(sizeof *node);
409
0
    node->key = key;
410
0
    node->value = value;
411
0
    hmap_insert(&smap->map, &node->node, hash);
412
0
    return node;
413
0
}
414
415
static struct smap_node *
416
smap_find__(const struct smap *smap, const char *key, size_t key_len,
417
            size_t hash)
418
0
{
419
0
    struct smap_node *node;
420
421
0
    HMAP_FOR_EACH_WITH_HASH (node, node, hash, &smap->map) {
422
0
        if (!strncmp(node->key, key, key_len) && !node->key[key_len]) {
423
0
            return node;
424
0
        }
425
0
    }
426
427
0
    return NULL;
428
0
}
429
430
static int
431
compare_nodes_by_key(const void *a_, const void *b_)
432
0
{
433
0
    const struct smap_node *const *a = a_;
434
0
    const struct smap_node *const *b = b_;
435
0
    return strcmp((*a)->key, (*b)->key);
436
0
}