Coverage Report

Created: 2026-10-28 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavutil/dict.c
Line
Count
Source
1
/*
2
 * copyright (c) 2009 Michael Niedermayer
3
 *
4
 * This file is part of FFmpeg.
5
 *
6
 * FFmpeg is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2.1 of the License, or (at your option) any later version.
10
 *
11
 * FFmpeg is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with FFmpeg; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
 */
20
21
#include <inttypes.h>
22
#include <stdio.h>
23
#include <string.h>
24
25
#include "avassert.h"
26
#include "avstring.h"
27
#include "dict.h"
28
#include "error.h"
29
#include "mem.h"
30
#include "bprint.h"
31
32
struct AVDictionary {
33
    int count;
34
    AVDictionaryEntry *elems;
35
    int duplicate_keys;
36
};
37
38
int av_dict_count(const AVDictionary *m)
39
0
{
40
0
    return m ? m->count : 0;
41
0
}
42
43
const AVDictionaryEntry *av_dict_iterate(const AVDictionary *m,
44
                                         const AVDictionaryEntry *prev)
45
1.43k
{
46
1.43k
    int i = 0;
47
48
1.43k
    if (!m)
49
1.43k
        return NULL;
50
51
0
    if (prev)
52
0
        i = prev - m->elems + 1;
53
54
0
    av_assert2(i >= 0);
55
0
    if (i >= m->count)
56
0
        return NULL;
57
58
0
    return &m->elems[i];
59
0
}
60
61
AVDictionaryEntry *av_dict_get(const AVDictionary *m, const char *key,
62
                               const AVDictionaryEntry *prev, int flags)
63
0
{
64
0
    const AVDictionaryEntry *entry = prev;
65
0
    unsigned int j;
66
67
0
    if (!key)
68
0
        return NULL;
69
70
0
    while ((entry = av_dict_iterate(m, entry))) {
71
0
        const char *s = entry->key;
72
0
        if (flags & AV_DICT_MATCH_CASE)
73
0
            for (j = 0; s[j] == key[j] && key[j]; j++)
74
0
                ;
75
0
        else
76
0
            for (j = 0; av_toupper(s[j]) == av_toupper(key[j]) && key[j]; j++)
77
0
                ;
78
0
        if (key[j])
79
0
            continue;
80
0
        if (s[j] && !(flags & AV_DICT_IGNORE_SUFFIX))
81
0
            continue;
82
0
        return (AVDictionaryEntry *)entry;
83
0
    }
84
0
    return NULL;
85
0
}
86
87
int av_dict_set(AVDictionary **pm, const char *key, const char *value,
88
                int flags)
89
0
{
90
0
    AVDictionary *m = *pm;
91
0
    AVDictionaryEntry *tag = NULL;
92
0
    char *copy_key = NULL, *copy_value = NULL;
93
0
    int err;
94
95
0
    if (flags & AV_DICT_DONT_STRDUP_VAL)
96
0
        copy_value = (void *)value;
97
0
    else if (value)
98
0
        copy_value = av_strdup(value);
99
0
    if (!key) {
100
0
        err = AVERROR(EINVAL);
101
0
        goto err_out;
102
0
    }
103
0
    if (flags & AV_DICT_DONT_STRDUP_KEY)
104
0
        copy_key = (void *)key;
105
0
    else
106
0
        copy_key = av_strdup(key);
107
0
    if (!copy_key || (value && !copy_value))
108
0
        goto enomem;
109
110
0
    if (!(flags & AV_DICT_MULTIKEY)) {
111
0
        tag = av_dict_get(m, key, NULL, flags);
112
0
    } else if (flags & AV_DICT_DEDUP) {
113
0
        while ((tag = av_dict_get(m, key, tag, flags))) {
114
0
            if ((!value && !tag->value) ||
115
0
                (value && tag->value && !strcmp(value, tag->value))) {
116
0
                av_free(copy_key);
117
0
                av_free(copy_value);
118
0
                return 0;
119
0
            }
120
0
        }
121
0
    }
122
0
    if (!m)
123
0
        m = *pm = av_mallocz(sizeof(*m));
124
0
    if (!m)
125
0
        goto enomem;
126
127
0
    if (tag) {
128
0
        if (flags & AV_DICT_DONT_OVERWRITE) {
129
0
            av_free(copy_key);
130
0
            av_free(copy_value);
131
0
            return 0;
132
0
        }
133
0
        if (copy_value && flags & AV_DICT_APPEND) {
134
0
            size_t oldlen = strlen(tag->value);
135
0
            size_t new_part_len = strlen(copy_value);
136
0
            size_t len = oldlen + new_part_len + 1;
137
0
            char *newval = av_realloc(tag->value, len);
138
0
            if (!newval)
139
0
                goto enomem;
140
0
            memcpy(newval + oldlen, copy_value, new_part_len + 1);
141
0
            av_freep(&copy_value);
142
0
            copy_value = newval;
143
0
        } else
144
0
            av_free(tag->value);
145
0
        av_free(tag->key);
146
0
        *tag = m->elems[--m->count];
147
0
    } else if (copy_value) {
148
0
        AVDictionaryEntry *tmp = av_realloc_array(m->elems,
149
0
                                                  m->count + 1, sizeof(*m->elems));
150
0
        if (!tmp)
151
0
            goto enomem;
152
0
        m->elems = tmp;
153
0
    }
154
0
    if (copy_value) {
155
0
        m->elems[m->count].key = copy_key;
156
0
        m->elems[m->count].value = copy_value;
157
0
        m->count++;
158
0
        if (flags & (AV_DICT_MATCH_CASE | AV_DICT_IGNORE_SUFFIX | AV_DICT_MULTIKEY))
159
0
            m->duplicate_keys = 1;
160
0
    } else {
161
0
        err = 0;
162
0
        goto end;
163
0
    }
164
165
0
    return 0;
166
167
0
enomem:
168
0
    err = AVERROR(ENOMEM);
169
0
err_out:
170
0
    av_free(copy_value);
171
0
end:
172
0
    if (m && !m->count) {
173
0
        av_freep(&m->elems);
174
0
        av_freep(pm);
175
0
    }
176
0
    av_free(copy_key);
177
0
    return err;
178
0
}
179
180
int av_dict_set_int(AVDictionary **pm, const char *key, int64_t value,
181
                int flags)
182
0
{
183
0
    char valuestr[22];
184
0
    snprintf(valuestr, sizeof(valuestr), "%"PRId64, value);
185
0
    flags &= ~AV_DICT_DONT_STRDUP_VAL;
186
0
    return av_dict_set(pm, key, valuestr, flags);
187
0
}
188
189
static int parse_key_value_pair(AVDictionary **pm, const char **buf,
190
                                const char *key_val_sep, const char *pairs_sep,
191
                                int flags)
192
0
{
193
0
    char *key = av_get_token(buf, key_val_sep);
194
0
    char *val = NULL;
195
0
    int ret;
196
197
0
    if (key && *key && strspn(*buf, key_val_sep)) {
198
0
        (*buf)++;
199
0
        val = av_get_token(buf, pairs_sep);
200
0
    }
201
202
0
    if (key && *key && val && *val)
203
0
        ret = av_dict_set(pm, key, val, flags);
204
0
    else
205
0
        ret = AVERROR(EINVAL);
206
207
0
    av_freep(&key);
208
0
    av_freep(&val);
209
210
0
    return ret;
211
0
}
212
213
int av_dict_parse_string(AVDictionary **pm, const char *str,
214
                         const char *key_val_sep, const char *pairs_sep,
215
                         int flags)
216
0
{
217
0
    int ret;
218
219
0
    if (!str)
220
0
        return 0;
221
222
    /* ignore STRDUP flags */
223
0
    flags &= ~(AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL);
224
225
0
    while (*str) {
226
0
        if ((ret = parse_key_value_pair(pm, &str, key_val_sep, pairs_sep, flags)) < 0)
227
0
            return ret;
228
229
0
        if (*str)
230
0
            str++;
231
0
    }
232
233
0
    return 0;
234
0
}
235
236
void av_dict_free(AVDictionary **pm)
237
5.74k
{
238
5.74k
    AVDictionary *m = *pm;
239
240
5.74k
    if (m) {
241
0
        while (m->count--) {
242
0
            av_freep(&m->elems[m->count].key);
243
0
            av_freep(&m->elems[m->count].value);
244
0
        }
245
0
        av_freep(&m->elems);
246
0
    }
247
5.74k
    av_freep(pm);
248
5.74k
}
249
250
int av_dict_copy(AVDictionary **dst, const AVDictionary *src, int flags)
251
0
{
252
0
    const AVDictionaryEntry *t = NULL;
253
254
0
    if (!*dst && src && src->count && !src->duplicate_keys &&
255
0
        !(flags & AV_DICT_IGNORE_SUFFIX)) {
256
0
        AVDictionary *m = av_mallocz(sizeof(*m));
257
0
        if (!m)
258
0
            return AVERROR(ENOMEM);
259
0
        m->elems = av_calloc(src->count, sizeof(*m->elems));
260
0
        if (!m->elems) {
261
0
            av_free(m);
262
0
            return AVERROR(ENOMEM);
263
0
        }
264
0
        *dst = m;
265
0
        for (int i = 0; i < src->count; i++) {
266
0
            m->elems[i].key   = av_strdup(src->elems[i].key);
267
0
            m->elems[i].value = av_strdup(src->elems[i].value);
268
0
            m->count++;
269
0
            if (!m->elems[i].key || !m->elems[i].value) {
270
0
                av_dict_free(dst);
271
0
                return AVERROR(ENOMEM);
272
0
            }
273
0
        }
274
0
        return 0;
275
0
    }
276
277
0
    while ((t = av_dict_iterate(src, t))) {
278
0
        int ret = av_dict_set(dst, t->key, t->value, flags);
279
0
        if (ret < 0)
280
0
            return ret;
281
0
    }
282
283
0
    return 0;
284
0
}
285
286
int av_dict_get_string(const AVDictionary *m, char **buffer,
287
                       const char key_val_sep, const char pairs_sep)
288
0
{
289
0
    const AVDictionaryEntry *t = NULL;
290
0
    AVBPrint bprint;
291
0
    int cnt = 0;
292
0
    char special_chars[] = {pairs_sep, key_val_sep, '\0'};
293
294
0
    if (!buffer || pairs_sep == '\0' || key_val_sep == '\0' || pairs_sep == key_val_sep ||
295
0
        pairs_sep == '\\' || key_val_sep == '\\')
296
0
        return AVERROR(EINVAL);
297
298
0
    if (!av_dict_count(m)) {
299
0
        *buffer = av_strdup("");
300
0
        return *buffer ? 0 : AVERROR(ENOMEM);
301
0
    }
302
303
0
    av_bprint_init(&bprint, 64, AV_BPRINT_SIZE_UNLIMITED);
304
0
    while ((t = av_dict_iterate(m, t))) {
305
0
        if (cnt++)
306
0
            av_bprint_append_data(&bprint, &pairs_sep, 1);
307
0
        av_bprint_escape(&bprint, t->key, special_chars, AV_ESCAPE_MODE_BACKSLASH, 0);
308
0
        av_bprint_append_data(&bprint, &key_val_sep, 1);
309
0
        av_bprint_escape(&bprint, t->value, special_chars, AV_ESCAPE_MODE_BACKSLASH, 0);
310
0
    }
311
0
    return av_bprint_finalize(&bprint, buffer);
312
0
}