Coverage Report

Created: 2025-07-01 06:50

/src/openvswitch/lib/dynamic-string.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 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
#include "openvswitch/dynamic-string.h"
19
#include <inttypes.h>
20
#include <stdlib.h>
21
#include <string.h>
22
#include <time.h>
23
#include "timeval.h"
24
#include "uuid.h"
25
#include "util.h"
26
27
/* Initializes 'ds' as an empty string buffer. */
28
void
29
ds_init(struct ds *ds)
30
0
{
31
0
    ds->string = NULL;
32
0
    ds->length = 0;
33
0
    ds->allocated = 0;
34
0
}
35
36
/* Sets 'ds''s length to 0, effectively clearing any existing content.  Does
37
 * not free any memory. */
38
void
39
ds_clear(struct ds *ds)
40
0
{
41
0
    ds->length = 0;
42
0
}
43
44
/* Reduces 'ds''s length to no more than 'new_length'.  (If its length is
45
 * already 'new_length' or less, does nothing.)  */
46
void
47
ds_truncate(struct ds *ds, size_t new_length)
48
0
{
49
0
    if (ds->length > new_length) {
50
0
        ds->length = new_length;
51
0
        ds->string[new_length] = '\0';
52
0
    }
53
0
}
54
55
/* Ensures that at least 'min_length + 1' bytes (including space for a null
56
 * terminator) are allocated for ds->string, allocating or reallocating memory
57
 * as necessary. */
58
void
59
ds_reserve(struct ds *ds, size_t min_length)
60
0
{
61
0
    if (min_length > ds->allocated || !ds->string) {
62
0
        ds->allocated += MAX(min_length, ds->allocated);
63
0
        ds->allocated = MAX(8, ds->allocated);
64
0
        ds->string = xrealloc(ds->string, ds->allocated + 1);
65
0
    }
66
0
}
67
68
/* Appends space for 'n' bytes to the end of 'ds->string', increasing
69
 * 'ds->length' by the same amount, and returns the first appended byte.  The
70
 * caller should fill in all 'n' bytes starting at the return value. */
71
char *
72
ds_put_uninit(struct ds *ds, size_t n)
73
0
{
74
0
    ds_reserve(ds, ds->length + n);
75
0
    ds->length += n;
76
0
    ds->string[ds->length] = '\0';
77
0
    return &ds->string[ds->length - n];
78
0
}
79
80
void
81
ds_put_char__(struct ds *ds, char c)
82
0
{
83
0
    *ds_put_uninit(ds, 1) = c;
84
0
}
85
86
/* Appends unicode code point 'uc' to 'ds' in UTF-8 encoding. */
87
void
88
ds_put_utf8(struct ds *ds, int uc)
89
0
{
90
0
    if (uc <= 0x7f) {
91
0
        ds_put_char(ds, uc);
92
0
    } else if (uc <= 0x7ff) {
93
0
        ds_put_char(ds, 0xc0 | (uc >> 6));
94
0
        ds_put_char(ds, 0x80 | (uc & 0x3f));
95
0
    } else if (uc <= 0xffff) {
96
0
        ds_put_char(ds, 0xe0 | (uc >> 12));
97
0
        ds_put_char(ds, 0x80 | ((uc >> 6) & 0x3f));
98
0
        ds_put_char(ds, 0x80 | (uc & 0x3f));
99
0
    } else if (uc <= 0x10ffff) {
100
0
        ds_put_char(ds, 0xf0 | (uc >> 18));
101
0
        ds_put_char(ds, 0x80 | ((uc >> 12) & 0x3f));
102
0
        ds_put_char(ds, 0x80 | ((uc >> 6) & 0x3f));
103
0
        ds_put_char(ds, 0x80 | (uc & 0x3f));
104
0
    } else {
105
        /* Invalid code point.  Insert the Unicode general substitute
106
         * REPLACEMENT CHARACTER. */
107
0
        ds_put_utf8(ds, 0xfffd);
108
0
    }
109
0
}
110
111
void
112
ds_put_char_multiple(struct ds *ds, char c, size_t n)
113
0
{
114
0
    memset(ds_put_uninit(ds, n), c, n);
115
0
}
116
117
void
118
ds_put_buffer(struct ds *ds, const char *s, size_t n)
119
0
{
120
0
    memcpy(ds_put_uninit(ds, n), s, n);
121
0
}
122
123
void
124
ds_put_cstr(struct ds *ds, const char *s)
125
0
{
126
0
    size_t s_len = strlen(s);
127
0
    memcpy(ds_put_uninit(ds, s_len), s, s_len);
128
0
}
129
130
void
131
ds_put_and_free_cstr(struct ds *ds, char *s)
132
0
{
133
0
    ds_put_cstr(ds, s);
134
0
    free(s);
135
0
}
136
137
void
138
ds_put_format(struct ds *ds, const char *format, ...)
139
0
{
140
0
    va_list args;
141
142
0
    va_start(args, format);
143
0
    ds_put_format_valist(ds, format, args);
144
0
    va_end(args);
145
0
}
146
147
void
148
ds_put_format_valist(struct ds *ds, const char *format, va_list args_)
149
0
{
150
0
    va_list args;
151
0
    size_t available;
152
0
    int needed;
153
154
0
    va_copy(args, args_);
155
0
    available = ds->string ? ds->allocated - ds->length + 1 : 0;
156
0
    needed = vsnprintf(ds->string
157
0
                       ? &ds->string[ds->length]
158
0
                       : NULL,
159
0
                       available, format, args);
160
0
    va_end(args);
161
162
0
    if (needed < available) {
163
0
        ds->length += needed;
164
0
    } else {
165
0
        ds_reserve(ds, ds->length + needed);
166
167
0
        va_copy(args, args_);
168
0
        available = ds->allocated - ds->length + 1;
169
0
        needed = vsnprintf(&ds->string[ds->length],
170
0
                           available, format, args);
171
0
        va_end(args);
172
173
0
        ovs_assert(needed < available);
174
0
        ds->length += needed;
175
0
    }
176
0
}
177
178
void
179
ds_put_printable(struct ds *ds, const char *s, size_t n)
180
0
{
181
0
    ds_reserve(ds, ds->length + n);
182
0
    while (n-- > 0) {
183
0
        unsigned char c = *s++;
184
0
        if (c < 0x20 || c > 0x7e || c == '\\' || c == '"') {
185
0
            ds_put_format(ds, "\\%03o", (int) c);
186
0
        } else {
187
0
            ds_put_char(ds, c);
188
0
        }
189
0
    }
190
0
}
191
192
void
193
ds_put_uuid(struct ds *ds, const struct uuid *uuid)
194
0
{
195
    /* We do not use dp_put_format() here to avoid two calls to
196
     * vsnprintf().
197
     *
198
     * We CAN use UUID_LEN + 1 in snprintf because ds->string always
199
     * contains one more byte for '\0'
200
     */
201
0
    snprintf(ds_put_uninit(ds, UUID_LEN), UUID_LEN + 1,
202
0
             UUID_FMT, UUID_ARGS(uuid));
203
0
}
204
205
/* Writes the current time with optional millisecond resolution to 'string'
206
 * based on 'template'.
207
 * The current time is either localtime or UTC based on 'utc'. */
208
void
209
ds_put_strftime_msec(struct ds *ds, const char *template, long long int when,
210
                     bool utc)
211
0
{
212
0
    struct tm_msec tm;
213
0
    if (utc) {
214
0
        gmtime_msec(when, &tm);
215
0
    } else {
216
0
        localtime_msec(when, &tm);
217
0
    }
218
219
0
    ds_reserve(ds, 64);
220
0
    for (;;) {
221
0
        size_t avail = ds->allocated - ds->length + 1;
222
0
        char *dest = &ds->string[ds->length];
223
0
        size_t used = strftime_msec(dest, avail, template, &tm);
224
0
        if (used) {
225
0
            ds->length += used;
226
0
            return;
227
0
        }
228
0
        ds_reserve(ds, ds->length + (avail < 32 ? 64 : 2 * avail));
229
0
    }
230
0
}
231
232
/* Returns a malloc()'d string for time 'when' based on 'template', in local
233
 * time or UTC based on 'utc'. */
234
char *
235
xastrftime_msec(const char *template, long long int when, bool utc)
236
0
{
237
0
    struct ds s;
238
239
0
    ds_init(&s);
240
0
    ds_put_strftime_msec(&s, template, when, utc);
241
0
    return s.string;
242
0
}
243
244
int
245
ds_get_line(struct ds *ds, FILE *file)
246
0
{
247
0
    ds_clear(ds);
248
0
    for (;;) {
249
0
        int c = getc(file);
250
0
        if (c == EOF) {
251
0
            return ds->length ? 0 : EOF;
252
0
        } else if (c == '\n') {
253
0
            return 0;
254
0
        } else {
255
0
            ds_put_char(ds, c);
256
0
        }
257
0
    }
258
0
}
259
260
/* Reads a line from 'file' into 'ds', clearing anything initially in 'ds'.
261
 * Deletes comments introduced by "#" and skips lines that contains only white
262
 * space (after deleting comments).
263
 *
264
 * If 'line_numberp' is nonnull, increments '*line_numberp' by the number of
265
 * lines read from 'file'.
266
 *
267
 * Returns 0 if successful, EOF if no non-blank line was found. */
268
int
269
ds_get_preprocessed_line(struct ds *ds, FILE *file, int *line_numberp)
270
0
{
271
0
    while (!ds_get_line(ds, file)) {
272
0
        char *line = ds_cstr(ds);
273
0
        char *comment;
274
275
0
        if (line_numberp) {
276
0
            ++*line_numberp;
277
0
        }
278
279
        /* Delete comments. */
280
0
        comment = strchr(line, '#');
281
0
        if (comment) {
282
0
            *comment = '\0';
283
0
        }
284
285
        /* Return successfully unless the line is all spaces. */
286
0
        if (line[strspn(line, " \t\n")] != '\0') {
287
0
            return 0;
288
0
        }
289
0
    }
290
0
    return EOF;
291
0
}
292
293
/* Reads a line from 'file' into 'ds' and does some preprocessing on it:
294
 *
295
 *    - If the line begins with #, prints it on stdout and reads the next line.
296
 *
297
 *    - Otherwise, if the line contains an # somewhere else, strips it and
298
 *      everything following it (as a comment).
299
 *
300
 *    - If (after comment removal) the line contains only white space, prints
301
 *      a blank line on stdout and reads the next line.
302
 *
303
 *    - Otherwise, returns the line to the caller.
304
 *
305
 * This is useful in some of the OVS tests, where we want to check that parsing
306
 * and then re-formatting some kind of data does not change it, but we also
307
 * want to be able to put comments in the input.
308
 *
309
 * Returns 0 if successful, EOF if no non-blank line was found. */
310
int
311
ds_get_test_line(struct ds *ds, FILE *file)
312
0
{
313
0
    for (;;) {
314
0
        char *s, *comment;
315
0
        int retval;
316
317
0
        retval = ds_get_line(ds, file);
318
0
        if (retval) {
319
0
            return retval;
320
0
        }
321
322
0
        s = ds_cstr(ds);
323
0
        if (*s == '#') {
324
0
            puts(s);
325
0
            continue;
326
0
        }
327
328
0
        comment = strchr(s, '#');
329
0
        if (comment) {
330
0
            *comment = '\0';
331
0
        }
332
0
        if (s[strspn(s, " \t\n")] == '\0') {
333
0
            putchar('\n');
334
0
            continue;
335
0
        }
336
337
0
        return 0;
338
0
    }
339
0
}
340
341
char *
342
ds_cstr(struct ds *ds)
343
0
{
344
0
    if (!ds->string) {
345
0
        ds_reserve(ds, 0);
346
0
    }
347
0
    ds->string[ds->length] = '\0';
348
0
    return ds->string;
349
0
}
350
351
const char *
352
ds_cstr_ro(const struct ds *ds)
353
0
{
354
0
    return ds_cstr(CONST_CAST(struct ds *, ds));
355
0
}
356
357
/* Returns a null-terminated string representing the current contents of 'ds',
358
 * which the caller is expected to free with free(), then clears the contents
359
 * of 'ds'. */
360
char *
361
ds_steal_cstr(struct ds *ds)
362
0
{
363
0
    char *s = ds_cstr(ds);
364
0
    ds_init(ds);
365
0
    return s;
366
0
}
367
368
void
369
ds_destroy(struct ds *ds)
370
0
{
371
0
    free(ds->string);
372
0
}
373
374
/* Swaps the content of 'a' and 'b'. */
375
void
376
ds_swap(struct ds *a, struct ds *b)
377
0
{
378
0
    struct ds temp = *a;
379
0
    *a = *b;
380
0
    *b = temp;
381
0
}
382
383
void
384
ds_put_hex(struct ds *ds, const void *buf_, size_t size)
385
0
{
386
0
    const uint8_t *buf = buf_;
387
0
    bool printed = false;
388
0
    int i;
389
390
0
    for (i = 0; i < size; i++) {
391
0
        uint8_t val = buf[i];
392
0
        if (val || printed) {
393
0
            if (!printed) {
394
0
                ds_put_format(ds, "0x%"PRIx8, val);
395
0
            } else {
396
0
                ds_put_format(ds, "%02"PRIx8, val);
397
0
            }
398
0
            printed = true;
399
0
        }
400
0
    }
401
0
    if (!printed) {
402
0
        ds_put_char(ds, '0');
403
0
    }
404
0
}
405
406
void
407
ds_put_hex_with_delimiter(struct ds *ds, const void *buf_, size_t size,
408
                          char *delimiter)
409
0
{
410
0
    const uint8_t *buf = buf_;
411
0
    size_t i;
412
413
0
    for (i = 0; i < size; i++) {
414
0
        if (i && delimiter) {
415
0
            ds_put_format(ds, "%s", delimiter);
416
0
        }
417
0
        ds_put_format(ds, "%02" PRIx8, buf[i]);
418
0
    }
419
0
}
420
421
static void
422
ds_put_hex_dump__(struct ds *ds, const void *buf_, size_t size,
423
                  uintptr_t ofs, bool ascii, bool skip_zero_lines)
424
0
{
425
0
    const uint8_t *buf = buf_;
426
0
    const size_t per_line = 16; /* Maximum bytes per line. */
427
428
0
    while (size > 0) {
429
0
        size_t start, end, n;
430
0
        size_t i;
431
432
        /* Number of bytes on this line. */
433
0
        start = ofs % per_line;
434
0
        end = per_line;
435
0
        if (end - start > size)
436
0
            end = start + size;
437
0
        n = end - start;
438
439
0
        if (skip_zero_lines && is_all_zeros(&buf[start], n)) {
440
0
            goto next;
441
0
        }
442
443
        /* Print line. */
444
0
        ds_put_format(ds, "%08"PRIxMAX"  ",
445
0
                      (uintmax_t) ROUND_DOWN(ofs, per_line));
446
0
        for (i = 0; i < start; i++) {
447
0
            ds_put_format(ds, "   ");
448
0
        }
449
0
        for (; i < end; i++) {
450
0
            ds_put_format(ds, "%02x%c",
451
0
                          buf[i - start], i == per_line / 2 - 1? '-' : ' ');
452
0
        }
453
0
        if (ascii) {
454
0
            for (; i < per_line; i++)
455
0
                ds_put_format(ds, "   ");
456
0
            ds_put_format(ds, "|");
457
0
            for (i = 0; i < start; i++)
458
0
                ds_put_format(ds, " ");
459
0
            for (; i < end; i++) {
460
0
                int c = buf[i - start];
461
0
                ds_put_char(ds, c >= 32 && c < 127 ? c : '.');
462
0
            }
463
0
            for (; i < per_line; i++)
464
0
                ds_put_format(ds, " ");
465
0
            ds_put_format(ds, "|");
466
0
        } else {
467
0
            ds_chomp(ds, ' ');
468
0
        }
469
0
        ds_put_format(ds, "\n");
470
0
next:
471
0
        ofs += n;
472
0
        buf += n;
473
0
        size -= n;
474
0
    }
475
0
}
476
477
/* Writes the 'size' bytes in 'buf' to 'string' as hex bytes arranged 16 per
478
 * line.  Numeric offsets are also included, starting at 'ofs' for the first
479
 * byte in 'buf'.  If 'ascii' is true then the corresponding ASCII characters
480
 * are also rendered alongside. */
481
void
482
ds_put_hex_dump(struct ds *ds, const void *buf_, size_t size,
483
                uintptr_t ofs, bool ascii)
484
0
{
485
0
    ds_put_hex_dump__(ds, buf_, size, ofs, ascii, false);
486
0
}
487
488
/* Same as 'ds_put_hex_dump', but doesn't print lines that only contains
489
 * zero bytes. */
490
void
491
ds_put_sparse_hex_dump(struct ds *ds, const void *buf_, size_t size,
492
                       uintptr_t ofs, bool ascii)
493
0
{
494
0
    ds_put_hex_dump__(ds, buf_, size, ofs, ascii, true);
495
0
}
496
497
int
498
ds_last(const struct ds *ds)
499
0
{
500
0
    return ds->length > 0 ? (unsigned char) ds->string[ds->length - 1] : EOF;
501
0
}
502
503
bool
504
ds_chomp(struct ds *ds, int c)
505
0
{
506
0
    if (ds->length > 0 && ds->string[ds->length - 1] == (char) c) {
507
0
        ds->string[--ds->length] = '\0';
508
0
        return true;
509
0
    } else {
510
0
        return false;
511
0
    }
512
0
}
513
514
void
515
ds_clone(struct ds *dst, struct ds *source)
516
0
{
517
0
    if (!source->allocated) {
518
0
        ds_init(dst);
519
0
        return;
520
0
    }
521
0
    dst->length = source->length;
522
0
    dst->allocated = dst->length;
523
0
    dst->string = xmalloc(dst->allocated + 1);
524
0
    memcpy(dst->string, source->string, dst->allocated + 1);
525
0
}