summaryrefslogtreecommitdiff
path: root/lib/smap.c
blob: 7fe3ce41cd7527b6fd12ae2cb479e650232a8146 (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
/* Copyright (c) 2012, 2014 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 "json.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));
}

/* Searches for 'key' in 'smap'.  If it does not already exists, adds it.
 * Otherwise, changes its value to 'value'. */
void
smap_replace(struct smap *smap, const char *key, const 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 = xstrdup(value);
    } else {
        smap_add__(smap, xmemdup0(key, key_len), xstrdup(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, *next;

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

/* Returns the value associated with 'key' in 'smap', or NULL. */
const char *
smap_get(const struct smap *smap, const char *key)
{
    struct smap_node *node = smap_get_node(smap, key);
    return node ? node->value : NULL;
}

/* 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(smap, key);

    if (!value) {
        return def;
    }

    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
 * using atoi().  If 'key' is not in 'smap', returns 'def'. */
int
smap_get_int(const struct smap *smap, const char *key, int def)
{
    const char *value = smap_get(smap, key);

    return value ? atoi(value) : def;
}

/* 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);
    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;
}

/* 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);
}