Coverage Report

Created: 2023-03-26 07:41

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