summaryrefslogtreecommitdiff
path: root/lib/smap.c
blob: c1633e2a18d92712f3e31584c3bd5b7b60c67abf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
/* Copyright (c) 2012, 2014, 2015, 2016 Nicira, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. */

#include <config.h>
#include "smap.h"

#include <strings.h>

#include "hash.h"
#include "openvswitch/json.h"
#include "packets.h"
#include "util.h"
#include "uuid.h"

static struct smap_node *smap_add__(struct smap *, char *, void *,
                                    size_t hash);
static struct smap_node *smap_find__(const struct smap *, const char *key,
                                     size_t key_len, size_t hash);
static int compare_nodes_by_key(const void *, const void *);

/* Public Functions. */

void
smap_init(struct smap *smap)
{
    hmap_init(&smap->map);
}

void
smap_destroy(struct smap *smap)
{
    if (smap) {
        smap_clear(smap);
        hmap_destroy(&smap->map);
    }
}

/* Adds 'key' paired with 'value' to 'smap'.  It is the caller's responsibility
 * to avoid duplicate keys if desirable. */
struct smap_node *
smap_add(struct smap *smap, const char *key, const char *value)
{
    size_t key_len = strlen(key);
    return smap_add__(smap, xmemdup0(key, key_len), xstrdup(value),
                      hash_bytes(key, key_len, 0));
}

/* Adds 'key' paired with 'value' to 'smap'.  Takes ownership of 'key' and
 * 'value' (which will eventually be freed with free()).  It is the caller's
 * responsibility to avoid duplicate keys if desirable. */
struct smap_node *
smap_add_nocopy(struct smap *smap, char *key, char *value)
{
    return smap_add__(smap, key, value, hash_bytes(key, strlen(key), 0));
}

/* Attempts to add 'key' to 'smap' associated with 'value'.  If 'key' already
 * exists in 'smap', does nothing and returns false.  Otherwise, performs the
 * addition and returns true. */
bool
smap_add_once(struct smap *smap, const char *key, const char *value)
{
    if (!smap_get(smap, key)) {
        smap_add(smap, key, value);
        return true;
    } else {
        return false;
    }
}

/* Adds 'key' paired with a value derived from 'format' (similar to printf).
 * It is the caller's responsibility to avoid duplicate keys if desirable. */
void
smap_add_format(struct smap *smap, const char *key, const char *format, ...)
{
    size_t key_len;
    va_list args;
    char *value;

    va_start(args, format);
    value = xvasprintf(format, args);
    va_end(args);

    key_len = strlen(key);
    smap_add__(smap, xmemdup0(key, key_len), value,
               hash_bytes(key, key_len, 0));
}

/* Adds 'key' paired with a string representation of 'addr'. It is the
 * caller's responsibility to avoid duplicate keys if desirable. */
void
smap_add_ipv6(struct smap *smap, const char *key, struct in6_addr *addr)
{
    char buf[INET6_ADDRSTRLEN];
    ipv6_string_mapped(buf, addr);
    smap_add(smap, key, buf);
}

/* Searches for 'key' in 'smap'.  If it does not already exists, adds it.
 * Otherwise, changes its value to 'value'.  The caller retains ownership of
 * 'value'. */
void
smap_replace(struct smap *smap, const char *key, const char *value)
{
    smap_replace_nocopy(smap, key, xstrdup(value));
}

/* Searches for 'key' in 'smap'.  If it does not already exists, adds it.
 * Otherwise, changes its value to 'value'.  Takes ownership of 'value'. */
void
smap_replace_nocopy(struct smap *smap, const char *key, char *value)
{
    size_t  key_len = strlen(key);
    size_t hash = hash_bytes(key, key_len, 0);

    struct smap_node *node;

    node = smap_find__(smap, key, key_len, hash);
    if (node) {
        free(node->value);
        node->value = value;
    } else {
        smap_add__(smap, xmemdup0(key, key_len), value, hash);
    }
}

/* If 'key' is in 'smap', removes it.  Otherwise does nothing. */
void
smap_remove(struct smap *smap, const char *key)
{
    struct smap_node *node = smap_get_node(smap, key);

    if (node) {
        smap_remove_node(smap, node);
    }
}

/* Removes 'node' from 'smap'. */
void
smap_remove_node(struct smap *smap, struct smap_node *node)
{
    hmap_remove(&smap->map, &node->node);
    free(node->key);
    free(node->value);
    free(node);
}

/* Deletes 'node' from 'smap'.
 *
 * If 'keyp' is nonnull, stores the node's key in '*keyp' and transfers
 * ownership to the caller.  Otherwise, frees the node's key.  Similarly for
 * 'valuep' and the node's value. */
void
smap_steal(struct smap *smap, struct smap_node *node,
           char **keyp, char **valuep)
{
    if (keyp) {
        *keyp = node->key;
    } else {
        free(node->key);
    }

    if (valuep) {
        *valuep = node->value;
    } else {
        free(node->value);
    }

    hmap_remove(&smap->map, &node->node);
    free(node);
}

/* Removes all key-value pairs from 'smap'. */
void
smap_clear(struct smap *smap)
{
    struct smap_node *node;

    SMAP_FOR_EACH_SAFE (node, smap) {
        smap_remove_node(smap, node);
    }
}

/* Returns the value associated with 'key' in 'smap'.
 * If 'smap' does not contain 'key', returns NULL. */
const char *
smap_get(const struct smap *smap, const char *key)
{
    return smap_get_def(smap, key, NULL);
}

/* Returns the value associated with 'key' in 'smap'.
 * If 'smap' does not contain 'key', returns 'def'. */
const char *
smap_get_def(const struct smap *smap, const char *key, const char *def)
{
    struct smap_node *node = smap_get_node(smap, key);
    return node ? node->value : def;
}

/* Returns the node associated with 'key' in 'smap', or NULL. */
struct smap_node *
smap_get_node(const struct smap *smap, const char *key)
{
    size_t key_len = strlen(key);
    return smap_find__(smap, key, key_len, hash_bytes(key, key_len, 0));
}

/* Gets the value associated with 'key' in 'smap' and converts it to a boolean.
 * If 'key' is not in 'smap', or its value is neither "true" nor "false",
 * returns 'def'. */
bool
smap_get_bool(const struct smap *smap, const char *key, bool def)
{
    const char *value = smap_get_def(smap, key, "");
    if (def) {
        return strcasecmp("false", value) != 0;
    } else {
        return !strcasecmp("true", value);
    }
}

/* Gets the value associated with 'key' in 'smap' and converts it to an int.
 * If 'key' is not in 'smap' or a valid integer can't be parsed from it's
 * value, returns 'def'. */
int
smap_get_int(const struct smap *smap, const char *key, int def)
{
    const char *value = smap_get(smap, key);
    int i_value;

    if (!value || !str_to_int(value, 10, &i_value)) {
        return def;
    }

    return i_value;
}

/* Gets the value associated with 'key' in 'smap' and converts it to an
 * unsigned int. If 'key' is not in 'smap' or a valid unsigned integer
 * can't be parsed from it's value, returns 'def'. */
unsigned int
smap_get_uint(const struct smap *smap, const char *key, unsigned int def)
{
    const char *value = smap_get(smap, key);
    unsigned int u_value;

    if (!value || !str_to_uint(value, 10, &u_value)) {
        return def;
    }

    return u_value;
}

/* Gets the value associated with 'key' in 'smap' and converts it to an
 * unsigned long long.  If 'key' is not in 'smap' or a valid number can't be
 * parsed from it's value, returns 'def'. */
unsigned long long int
smap_get_ullong(const struct smap *smap, const char *key,
                unsigned long long def)
{
    const char *value = smap_get(smap, key);
    unsigned long long ull_value;

    if (!value || !str_to_ullong(value, 10, &ull_value)) {
        return def;
    }

    return ull_value;
}

/* Gets the value associated with 'key' in 'smap' and converts it to a UUID
 * using uuid_from_string().  Returns true if successful, false if 'key' is not
 * in 'smap' or if 'key' does not have the correct syntax for a UUID. */
bool
smap_get_uuid(const struct smap *smap, const char *key, struct uuid *uuid)
{
    return uuid_from_string(uuid, smap_get_def(smap, key, ""));
}

/* Returns true of there are no elements in 'smap'. */
bool
smap_is_empty(const struct smap *smap)
{
    return hmap_is_empty(&smap->map);
}

/* Returns the number of elements in 'smap'. */
size_t
smap_count(const struct smap *smap)
{
    return hmap_count(&smap->map);
}

/* Initializes 'dst' as a clone of 'src. */
void
smap_clone(struct smap *dst, const struct smap *src)
{
    const struct smap_node *node;

    smap_init(dst);
    hmap_reserve(&dst->map, smap_count(src));

    SMAP_FOR_EACH (node, src) {
        smap_add__(dst, xstrdup(node->key), xstrdup(node->value),
                   node->node.hash);
    }
}

/* Returns an array of nodes sorted on key or NULL if 'smap' is empty.  The
 * caller is responsible for freeing this array. */
const struct smap_node **
smap_sort(const struct smap *smap)
{
    if (smap_is_empty(smap)) {
        return NULL;
    } else {
        const struct smap_node **nodes;
        struct smap_node *node;
        size_t i, n;

        n = smap_count(smap);
        nodes = xmalloc(n * sizeof *nodes);
        i = 0;
        SMAP_FOR_EACH (node, smap) {
            nodes[i++] = node;
        }
        ovs_assert(i == n);

        qsort(nodes, n, sizeof *nodes, compare_nodes_by_key);

        return nodes;
    }
}

/* Adds each of the key-value pairs from 'json' (which must be a JSON object
 * whose values are strings) to 'smap'.
 *
 * The caller must have initialized 'smap'.
 *
 * The caller retains ownership of 'json' and everything in it. */
void
smap_from_json(struct smap *smap, const struct json *json)
{
    const struct shash_node *node;

    SHASH_FOR_EACH (node, json_object(json)) {
        const struct json *value = node->data;
        smap_add(smap, node->name, json_string(value));
    }
}

/* Returns a JSON object that maps from the keys in 'smap' to their values.
 *
 * The caller owns the returned value and must eventually json_destroy() it.
 *
 * The caller retains ownership of 'smap' and everything in it. */
struct json *
smap_to_json(const struct smap *smap)
{
    const struct smap_node *node;
    struct json *json;

    json = json_object_create();
    SMAP_FOR_EACH (node, smap) {
        json_object_put_string(json, node->key, node->value);
    }
    return json;
}

/* Returns true if the two maps are equal, meaning that they have the same set
 * of key-value pairs.
 */
bool
smap_equal(const struct smap *smap1, const struct smap *smap2)
{
    if (smap_count(smap1) != smap_count(smap2)) {
        return false;
    }

    const struct smap_node *node;
    SMAP_FOR_EACH (node, smap1) {
        const char *value2 = smap_get(smap2, node->key);
        if (!value2 || strcmp(node->value, value2)) {
            return false;
        }
    }
    return true;
}

/* Private Helpers. */

static struct smap_node *
smap_add__(struct smap *smap, char *key, void *value, size_t hash)
{
    struct smap_node *node = xmalloc(sizeof *node);
    node->key = key;
    node->value = value;
    hmap_insert(&smap->map, &node->node, hash);
    return node;
}

static struct smap_node *
smap_find__(const struct smap *smap, const char *key, size_t key_len,
            size_t hash)
{
    struct smap_node *node;

    HMAP_FOR_EACH_WITH_HASH (node, node, hash, &smap->map) {
        if (!strncmp(node->key, key, key_len) && !node->key[key_len]) {
            return node;
        }
    }

    return NULL;
}

static int
compare_nodes_by_key(const void *a_, const void *b_)
{
    const struct smap_node *const *a = a_;
    const struct smap_node *const *b = b_;
    return strcmp((*a)->key, (*b)->key);
}