Coverage Report

Created: 2026-03-30 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/wsutil/value_string.c
Line
Count
Source
1
/* value_string.c
2
 * Routines for value_strings
3
 *
4
 * Wireshark - Network traffic analyzer
5
 * By Gerald Combs <gerald@wireshark.org>
6
 * Copyright 1998 Gerald Combs
7
 *
8
 * SPDX-License-Identifier: GPL-2.0-or-later
9
 */
10
11
#include "config.h"
12
0
#define WS_LOG_DOMAIN LOG_DOMAIN_WSUTIL
13
14
#include <stdio.h>
15
#include <string.h>
16
17
#include "to_str.h"
18
#include "value_string.h"
19
#include <wsutil/ws_assert.h>
20
21
#include <wsutil/wslog.h>
22
23
 /*
24
  * List of registered value strings for use outside of dissectors
25
  */
26
static GHashTable* registered_vs;
27
static GHashTable* registered_vs_ext;
28
29
30
/* Sort function that can be used with dynamically created value_strings */
31
int
32
value_str_value_compare(const void* a, const void* b)
33
0
{
34
0
    const value_string* vsa = (const value_string*)a;
35
0
    const value_string* vsb = (const value_string*)b;
36
37
0
    if (vsa->value > vsb->value)
38
0
        return 1;
39
0
    if (vsa->value < vsb->value)
40
0
        return -1;
41
42
0
    return 0;
43
0
}
44
45
/* REGULAR VALUE STRING */
46
47
/* Tries to match val against each element in the value_string array vs.
48
   Returns the associated string ptr on a match.
49
   Formats val with fmt, and returns the resulting string, on failure. */
50
char *
51
val_to_str(wmem_allocator_t *scope, const uint32_t val, const value_string *vs, const char *fmt)
52
12.0M
{
53
12.0M
    const char *ret;
54
55
12.0M
    ret = try_val_to_str(val, vs);
56
12.0M
    if (ret != NULL)
57
11.8M
        return wmem_strdup(scope, ret);
58
59
    //Programming error check
60
224k
    if (fmt == NULL)
61
0
        return wmem_strdup(scope, "(invalid argument: fmt cannot be NULL)");
62
63
224k
    return wmem_strdup_printf(scope, fmt, val);
64
224k
}
65
66
/* Tries to match val against each element in the value_string array vs.
67
   Returns the associated string ptr on a match.
68
   Returns 'unknown_str', on failure. */
69
const char *
70
val_to_str_const(const uint32_t val, const value_string *vs, const char *unknown_str)
71
12.2M
{
72
12.2M
    const char *ret;
73
74
12.2M
    ret = try_val_to_str(val, vs);
75
12.2M
    if (ret != NULL)
76
11.7M
        return ret;
77
78
    //Programming error check
79
498k
    if (unknown_str == NULL)
80
0
        return "(invalid argument: unknown_str cannot be NULL)";
81
82
498k
    return unknown_str;
83
498k
}
84
85
/* Tries to match val against each element in the value_string array vs.
86
   Returns the associated string ptr, and sets "*idx" to the index in
87
   that table, on a match, and returns NULL, and sets "*idx" to -1,
88
   on failure. */
89
const char *
90
try_val_to_str_idx(const uint32_t val, const value_string *vs, int *idx)
91
24.9M
{
92
24.9M
    int i = 0;
93
94
24.9M
    if(vs) {
95
144M
        while (vs[i].strptr) {
96
143M
            if (vs[i].value == val) {
97
24.1M
                if (idx != NULL)
98
17.8k
                    *idx = i;
99
24.1M
                return(vs[i].strptr);
100
24.1M
            }
101
119M
            i++;
102
119M
        }
103
24.9M
    }
104
105
791k
    if (idx != NULL)
106
17.4k
        *idx = -1;
107
791k
    return NULL;
108
24.9M
}
109
110
/* Like try_val_to_str_idx(), but doesn't return the index. */
111
const char *
112
try_val_to_str(const uint32_t val, const value_string *vs)
113
24.9M
{
114
24.9M
    return try_val_to_str_idx(val, vs, NULL);
115
24.9M
}
116
117
/* 64-BIT VALUE STRING */
118
119
const char *
120
val64_to_str_wmem(wmem_allocator_t* scope, const uint64_t val, const val64_string *vs, const char *fmt)
121
2.16k
{
122
2.16k
    const char *ret;
123
124
2.16k
    ret = try_val64_to_str(val, vs);
125
2.16k
    if (ret != NULL)
126
1.26k
        return ret;
127
128
    //Programming error check
129
900
    if (fmt == NULL)
130
0
        return wmem_strdup(scope, "(invalid argument: fmt cannot be NULL)");
131
132
900
    return wmem_strdup_printf(scope, fmt, val);
133
900
}
134
135
const char *
136
val64_to_str_const(const uint64_t val, const val64_string *vs, const char *unknown_str)
137
2.23k
{
138
2.23k
    const char *ret;
139
140
2.23k
    ret = try_val64_to_str(val, vs);
141
2.23k
    if (ret != NULL)
142
1.00k
        return ret;
143
144
    //Programming error check
145
1.23k
    if (unknown_str == NULL)
146
0
        return "(invalid argument: unknown_str cannot be NULL)";
147
148
1.23k
    return unknown_str;
149
1.23k
}
150
151
const char *
152
try_val64_to_str_idx(const uint64_t val, const val64_string *vs, int *idx)
153
4.88k
{
154
4.88k
    int i = 0;
155
156
4.88k
    if(vs) {
157
95.5k
        while (vs[i].strptr) {
158
92.8k
            if (vs[i].value == val) {
159
2.26k
                if (idx != NULL)
160
0
                    *idx = i;
161
2.26k
                return(vs[i].strptr);
162
2.26k
            }
163
90.6k
            i++;
164
90.6k
        }
165
4.88k
    }
166
167
2.61k
    if (idx != NULL)
168
0
        *idx = -1;
169
2.61k
    return NULL;
170
4.88k
}
171
172
const char *
173
try_val64_to_str(const uint64_t val, const val64_string *vs)
174
4.88k
{
175
4.88k
    return try_val64_to_str_idx(val, vs, NULL);
176
4.88k
}
177
178
/* REVERSE VALUE STRING */
179
180
/* We use the same struct as for regular value strings, but we look up strings
181
 * and return values instead */
182
183
/* Like val_to_str except backwards */
184
uint32_t
185
str_to_val(const char *val, const value_string *vs, const uint32_t err_val)
186
4.60k
{
187
4.60k
    int i;
188
189
4.60k
    i = str_to_val_idx(val, vs);
190
191
4.60k
    if (i >= 0) {
192
645
        return vs[i].value;
193
645
    }
194
195
3.96k
    return err_val;
196
4.60k
}
197
198
/* Find the index of a string in a value_string, or -1 when not present */
199
int
200
str_to_val_idx(const char *val, const value_string *vs)
201
4.60k
{
202
4.60k
    int i = 0;
203
204
4.60k
    if(vs) {
205
206
144k
        while (vs[i].strptr) {
207
208
140k
            if (strcmp(vs[i].strptr, val) == 0) {
209
645
                return i;
210
645
            }
211
212
139k
            i++;
213
139k
        }
214
215
4.60k
    }
216
217
3.96k
    return -1;
218
4.60k
}
219
220
/* EXTENDED VALUE STRING */
221
222
/* Extended value strings allow fast(er) value_string array lookups by
223
 * using (if possible) direct access or a binary search of the array.
224
 *
225
 * If the values in the value_string array are a contiguous range of values
226
 * from min to max, the value will be used as a direct index into the array.
227
 *
228
 * If the values in the array are not contiguous (ie: there are "gaps"),
229
 * but are in assending order a binary search will be used.
230
 *
231
 * If direct access or binary search cannot be used, then a linear search
232
 * is used and a warning is emitted.
233
 *
234
 * Note that the value_string array used with VALUE_STRING_EXT_INIT
235
 * *must* be terminated with {0, NULL}).
236
 *
237
 * Extended value strings are defined at compile time as follows:
238
 *   static const value_string vs[] = { {value1, "string1"},
239
 *                                      {value2, "string2"},
240
 *                                      ...,
241
 *                                      {0, NULL}};
242
 *   static value_string_ext vse = VALUE_STRING_EXT_INIT(vs);
243
 *
244
 * Extended value strings can be created at runtime by calling
245
 *   value_string_ext_new(<ptr to value_string array>,
246
 *                        <total number of entries in the value_string_array>,
247
 *                        <value_string_name>);
248
 * Note: The <total number of entries in the value_string_array> should include
249
 *       the {0, NULL} entry.
250
 */
251
252
/* Create a value_string_ext given a ptr to a value_string array and the total
253
 * number of entries. Note that the total number of entries should include the
254
 * required {0, NULL} terminating entry of the array.
255
 * Returns a pointer to an epan-scoped'd and initialized value_string_ext
256
 * struct. */
257
value_string_ext *
258
value_string_ext_new(wmem_allocator_t* scope, const value_string *vs, unsigned vs_tot_num_entries, const char *vs_name)
259
5
{
260
5
    value_string_ext *vse;
261
262
5
    ws_return_val_if((vs_name == NULL), NULL);
263
5
    ws_return_val_if((vs_tot_num_entries == 0), NULL);
264
5
    ws_return_val_if((vs[vs_tot_num_entries - 1].strptr != NULL), NULL);
265
266
5
    vse                  = wmem_new(scope, value_string_ext);
267
5
    vse->_vs_p           = vs;
268
5
    vse->_vs_num_entries = vs_tot_num_entries - 1;
269
    /* We set our 'match' function to the init function, which finishes by
270
     * setting the match function properly and then calling it. This is a
271
     * simple way to do lazy initialization of extended value strings.
272
     * The init function also sets up _vs_first_value for us. */
273
5
    vse->_vs_first_value = 0;
274
5
    vse->_vs_match2      = _try_val_to_str_ext_init;
275
5
    vse->_vs_name        = vs_name;
276
5
    vse->_scope          = scope;
277
278
5
    return vse;
279
5
}
280
281
void
282
value_string_ext_free(value_string_ext *vse)
283
0
{
284
0
    wmem_free(vse->_scope, vse);
285
0
}
286
287
/* Like try_val_to_str for extended value strings */
288
const char *
289
try_val_to_str_ext(const uint32_t val, value_string_ext *vse)
290
864k
{
291
864k
    if (vse) {
292
864k
        const value_string *vs = vse->_vs_match2(val, vse);
293
294
864k
        if (vs) {
295
506k
            return vs->strptr;
296
506k
        }
297
864k
    }
298
299
358k
    return NULL;
300
864k
}
301
302
/* Like try_val_to_str_idx for extended value strings */
303
const char *
304
try_val_to_str_idx_ext(const uint32_t val, value_string_ext *vse, int *idx)
305
1.92k
{
306
1.92k
    if (vse) {
307
1.92k
        const value_string *vs = vse->_vs_match2(val, vse);
308
1.92k
        if (vs) {
309
1.84k
            *idx = (int) (vs - vse->_vs_p);
310
1.84k
            return vs->strptr;
311
1.84k
        }
312
1.92k
    }
313
79
    *idx = -1;
314
79
    return NULL;
315
1.92k
}
316
317
/* Like val_to_str for extended value strings */
318
char *
319
val_to_str_ext(wmem_allocator_t *scope, const uint32_t val, value_string_ext *vse, const char *fmt)
320
460k
{
321
460k
    const char *ret;
322
323
460k
    ret = try_val_to_str_ext(val, vse);
324
460k
    if (ret != NULL)
325
309k
        return wmem_strdup(scope, ret);
326
327
151k
    if (fmt == NULL)
328
0
        return wmem_strdup(scope, "(invalid argument: fmt cannot be NULL)");
329
330
151k
    return wmem_strdup_printf(scope, fmt, val);
331
151k
}
332
333
/* Like val_to_str_const for extended value strings */
334
const char *
335
val_to_str_ext_const(const uint32_t val, value_string_ext *vse,
336
        const char *unknown_str)
337
350k
{
338
350k
    const char *ret;
339
340
350k
    ret = try_val_to_str_ext(val, vse);
341
350k
    if (ret != NULL)
342
168k
        return ret;
343
344
182k
    if (unknown_str == NULL)
345
0
        return "unknown_str cannot be NULL";
346
347
182k
    return unknown_str;
348
182k
}
349
350
/* Fallback linear matching algorithm for extended value strings */
351
static const value_string *
352
_try_val_to_str_linear(const uint32_t val, value_string_ext *vse)
353
0
{
354
0
    const value_string *vs_p = vse->_vs_p;
355
0
    unsigned i;
356
0
    for (i=0; i<vse->_vs_num_entries; i++) {
357
0
        if (vs_p[i].value == val)
358
0
            return &(vs_p[i]);
359
0
    }
360
0
    return NULL;
361
0
}
362
363
/* Constant-time matching algorithm for contiguous extended value strings */
364
static const value_string *
365
_try_val_to_str_index(const uint32_t val, value_string_ext *vse)
366
224k
{
367
224k
    uint32_t i;
368
369
224k
    i = val - vse->_vs_first_value;
370
224k
    if (i < vse->_vs_num_entries) {
371
173k
        ws_assert (val == vse->_vs_p[i].value);
372
173k
        return &(vse->_vs_p[i]);
373
173k
    }
374
50.2k
    return NULL;
375
224k
}
376
377
/* Value comparator for sorted extended value strings */
378
static int
379
val_to_str_compar(const void *v_needle, const void *v_item)
380
4.14M
{
381
4.14M
    uint32_t needle = *(const uint32_t *)v_needle;
382
4.14M
    uint32_t value = ((const value_string *)v_item)->value;
383
4.14M
    return needle > value ? 1 : (needle < value ? -1 : 0);
384
4.14M
}
385
386
/* log(n)-time matching for sorted extended value strings */
387
static const value_string *
388
_try_val_to_str_bsearch(const uint32_t val, value_string_ext *vse)
389
642k
{
390
642k
    return bsearch(&val, vse->_vs_p, vse->_vs_num_entries,
391
642k
                   sizeof vse->_vs_p[0], val_to_str_compar);
392
642k
}
393
394
/* Initializes an extended value string. Behaves like a match function to
395
 * permit lazy initialization of extended value strings.
396
 * - Goes through the value_string array to determine the fastest possible
397
 *   access method.
398
 * - Verifies that the value_string contains no NULL string pointers.
399
 * - Verifies that the value_string is terminated by {0, NULL}
400
 */
401
const value_string *
402
_try_val_to_str_ext_init(const uint32_t val, value_string_ext *vse)
403
664
{
404
664
    const value_string *vs_p           = vse->_vs_p;
405
664
    const unsigned      vs_num_entries = vse->_vs_num_entries;
406
407
    /* The matching algorithm used:
408
     * VS_LIN_SEARCH - slow linear search (as in a normal value string)
409
     * VS_BIN_SEARCH - log(n)-time binary search, the values must be sorted
410
     * VS_INDEX      - constant-time index lookup, the values must be contiguous
411
     */
412
664
    enum { VS_LIN_SEARCH, VS_BIN_SEARCH, VS_INDEX } type = VS_INDEX;
413
414
    /* Note: The value_string 'value' is *unsigned*, but we do a little magic
415
     * to help with value strings that have negative values.
416
     *
417
     * { -3, -2, -1, 0, 1, 2 }
418
     * will be treated as "ascending ordered" (although it isn't technically),
419
     * thus allowing constant-time index search
420
     *
421
     * { -3, -2, 0, 1, 2 } and { -3, -2, -1, 0, 2 }
422
     * will both be considered as "out-of-order with gaps", thus falling
423
     * back to the slow linear search
424
     *
425
     * { 0, 1, 2, -3, -2 } and { 0, 2, -3, -2, -1 }
426
     * will be considered "ascending ordered with gaps" thus allowing
427
     * a log(n)-time 'binary' search
428
     *
429
     * If you're confused, think of how negative values are represented, or
430
     * google two's complement.
431
     */
432
433
664
    uint32_t prev_value;
434
664
    uint32_t first_value;
435
664
    unsigned   i;
436
437
664
    if ((vs_p[vs_num_entries].value != 0) ||
438
664
        (vs_p[vs_num_entries].strptr != NULL)) {
439
0
        ws_warning("vse must end with {0, NULL}");
440
0
        return NULL;
441
0
    }
442
443
664
    vse->_vs_first_value = vs_p[0].value;
444
664
    first_value          = vs_p[0].value;
445
664
    prev_value           = first_value;
446
447
1.01M
    for (i = 0; i < vs_num_entries; i++) {
448
1.01M
        if (vs_p[i].strptr == NULL)
449
1.01M
            ws_warning("vse[%u].strptr cannot be NULL!", i);
450
1.01M
        if ((type == VS_INDEX) && (vs_p[i].value != (i + first_value))) {
451
302
            type = VS_BIN_SEARCH;
452
302
        }
453
        /* XXX: Should check for dups ?? */
454
1.01M
        if (type == VS_BIN_SEARCH) {
455
968k
            if (prev_value > vs_p[i].value) {
456
0
                ws_warning("Extended value string '%s' forced to fall back to linear search:\n"
457
0
                          "  entry %u, value %u [%#x] < previous entry, value %u [%#x]",
458
0
                          vse->_vs_name, i, vs_p[i].value, vs_p[i].value, prev_value, prev_value);
459
0
                type = VS_LIN_SEARCH;
460
0
                break;
461
0
            }
462
968k
            if (first_value > vs_p[i].value) {
463
0
                ws_warning("Extended value string '%s' forced to fall back to linear search:\n"
464
0
                          "  entry %u, value %u [%#x] < first entry, value %u [%#x]",
465
0
                          vse->_vs_name, i, vs_p[i].value, vs_p[i].value, first_value, first_value);
466
0
                type = VS_LIN_SEARCH;
467
0
                break;
468
0
            }
469
968k
        }
470
471
1.01M
        prev_value = vs_p[i].value;
472
1.01M
    }
473
474
664
    switch (type) {
475
0
        case VS_LIN_SEARCH:
476
0
            vse->_vs_match2 = _try_val_to_str_linear;
477
0
            break;
478
302
        case VS_BIN_SEARCH:
479
302
            vse->_vs_match2 = _try_val_to_str_bsearch;
480
302
            break;
481
362
        case VS_INDEX:
482
362
            vse->_vs_match2 = _try_val_to_str_index;
483
362
            break;
484
0
        default:
485
0
            ws_assert_not_reached();
486
0
            break;
487
664
    }
488
489
664
    return vse->_vs_match2(val, vse);
490
664
}
491
492
/* EXTENDED 64-BIT VALUE STRING */
493
494
/* Extended value strings allow fast(er) val64_string array lookups by
495
 * using (if possible) direct access or a binary search of the array.
496
 *
497
 * If the values in the val64_string array are a contiguous range of values
498
 * from min to max, the value will be used as a direct index into the array.
499
 *
500
 * If the values in the array are not contiguous (ie: there are "gaps"),
501
 * but are in assending order a binary search will be used.
502
 *
503
 * If direct access or binary search cannot be used, then a linear search
504
 * is used and a warning is emitted.
505
 *
506
 * Note that the val64_string array used with VAL64_STRING_EXT_INIT
507
 * *must* be terminated with {0, NULL}).
508
 *
509
 * Extended value strings are defined at compile time as follows:
510
 *   static const val64_string vs[] = { {value1, "string1"},
511
 *                                      {value2, "string2"},
512
 *                                      ...,
513
 *                                      {0, NULL}};
514
 *   static val64_string_ext vse = VAL64_STRING_EXT_INIT(vs);
515
 *
516
 * Extended value strings can be created at runtime by calling
517
 *   val64_string_ext_new(<ptr to val64_string array>,
518
 *                        <total number of entries in the val64_string_array>,
519
 *                        <val64_string_name>);
520
 * Note: The <total number of entries in the val64_string_array> should include
521
 *       the {0, NULL} entry.
522
 */
523
524
/* Create a val64_string_ext given a ptr to a val64_string array and the total
525
 * number of entries. Note that the total number of entries should include the
526
 * required {0, NULL} terminating entry of the array.
527
 * Returns a pointer to an epan-scoped'd and initialized val64_string_ext
528
 * struct. */
529
val64_string_ext *
530
val64_string_ext_new(wmem_allocator_t* scope, const val64_string *vs, unsigned vs_tot_num_entries, const char *vs_name)
531
0
{
532
0
    val64_string_ext *vse;
533
534
0
    ws_return_val_if((vs_name == NULL), NULL);
535
0
    ws_return_val_if((vs_tot_num_entries == 0), NULL);
536
0
    ws_return_val_if((vs[vs_tot_num_entries - 1].strptr != NULL), NULL);
537
538
0
    vse                  = wmem_new(NULL, val64_string_ext);
539
0
    vse->_vs_p           = vs;
540
0
    vse->_vs_num_entries = vs_tot_num_entries - 1;
541
    /* We set our 'match' function to the init function, which finishes by
542
     * setting the match function properly and then calling it. This is a
543
     * simple way to do lazy initialization of extended value strings.
544
     * The init function also sets up _vs_first_value for us. */
545
0
    vse->_vs_first_value = 0;
546
0
    vse->_vs_match2      = _try_val64_to_str_ext_init;
547
0
    vse->_vs_name        = vs_name;
548
0
    vse->_scope          = scope;
549
550
0
    return vse;
551
0
}
552
553
void
554
val64_string_ext_free(val64_string_ext *vse)
555
0
{
556
0
    wmem_free(vse->_scope, vse);
557
0
}
558
559
/* Like try_val_to_str for extended value strings */
560
const char *
561
try_val64_to_str_ext(const uint64_t val, val64_string_ext *vse)
562
4
{
563
4
    if (vse) {
564
4
        const val64_string *vs = vse->_vs_match2(val, vse);
565
566
4
        if (vs) {
567
3
            return vs->strptr;
568
3
        }
569
4
    }
570
571
1
    return NULL;
572
4
}
573
574
/* Like try_val_to_str_idx for extended value strings */
575
const char *
576
try_val64_to_str_idx_ext(const uint64_t val, val64_string_ext *vse, int *idx)
577
0
{
578
0
    if (vse) {
579
0
        const val64_string *vs = vse->_vs_match2(val, vse);
580
0
        if (vs) {
581
0
            *idx = (int) (vs - vse->_vs_p);
582
0
            return vs->strptr;
583
0
        }
584
0
    }
585
0
    *idx = -1;
586
0
    return NULL;
587
0
}
588
589
char *
590
val64_to_str_ext_wmem(wmem_allocator_t *scope, const uint64_t val, val64_string_ext *vse, const char *fmt)
591
4
{
592
4
    const char *ret;
593
594
4
    ret = try_val64_to_str_ext(val, vse);
595
4
    if (ret != NULL)
596
3
        return wmem_strdup(scope, ret);
597
598
1
    if (fmt == NULL)
599
0
        return wmem_strdup(scope, "(invalid argument: fmt cannot be NULL)");
600
601
1
    return wmem_strdup_printf(scope, fmt, val);
602
1
}
603
604
/* Like val_to_str_const for extended value strings */
605
const char *
606
val64_to_str_ext_const(const uint64_t val, val64_string_ext *vse,
607
        const char *unknown_str)
608
0
{
609
0
    const char *ret;
610
611
0
    ret = try_val64_to_str_ext(val, vse);
612
0
    if (ret != NULL)
613
0
        return ret;
614
615
0
    if (unknown_str == NULL)
616
0
        return "unknown_str cannot be NULL";
617
618
0
    return unknown_str;
619
0
}
620
621
/* Fallback linear matching algorithm for extended value strings */
622
static const val64_string *
623
_try_val64_to_str_linear(const uint64_t val, val64_string_ext *vse)
624
0
{
625
0
    const val64_string *vs_p = vse->_vs_p;
626
0
    unsigned i;
627
0
    for (i=0; i<vse->_vs_num_entries; i++) {
628
0
        if (vs_p[i].value == val)
629
0
            return &(vs_p[i]);
630
0
    }
631
0
    return NULL;
632
0
}
633
634
/* Constant-time matching algorithm for contiguous extended value strings */
635
static const val64_string *
636
_try_val64_to_str_index(const uint64_t val, val64_string_ext *vse)
637
0
{
638
0
    uint64_t i;
639
640
0
    i = val - vse->_vs_first_value;
641
0
    if (i < vse->_vs_num_entries) {
642
0
        ws_assert (val == vse->_vs_p[i].value);
643
0
        return &(vse->_vs_p[i]);
644
0
    }
645
0
    return NULL;
646
0
}
647
648
/* Value comparator for sorted extended value strings */
649
static int
650
val64_to_str_compar(const void *v_needle, const void *v_item)
651
20
{
652
20
    uint64_t needle = *(const uint64_t *)v_needle;
653
20
    uint64_t value = ((const val64_string *)v_item)->value;
654
20
    return needle > value ? 1 : (needle < value ? -1 : 0);
655
20
}
656
657
/* log(n)-time matching for sorted extended value strings */
658
static const val64_string *
659
_try_val64_to_str_bsearch(const uint64_t val, val64_string_ext *vse)
660
4
{
661
4
    return bsearch(&val, vse->_vs_p, vse->_vs_num_entries,
662
4
                   sizeof vse->_vs_p[0], val64_to_str_compar);
663
4
}
664
665
/* Initializes an extended value string. Behaves like a match function to
666
 * permit lazy initialization of extended value strings.
667
 * - Goes through the val64_string array to determine the fastest possible
668
 *   access method.
669
 * - Verifies that the val64_string contains no NULL string pointers.
670
 * - Verifies that the val64_string is terminated by {0, NULL}
671
 */
672
const val64_string *
673
_try_val64_to_str_ext_init(const uint64_t val, val64_string_ext *vse)
674
1
{
675
1
    const val64_string *vs_p           = vse->_vs_p;
676
1
    const unsigned      vs_num_entries = vse->_vs_num_entries;
677
678
    /* The matching algorithm used:
679
     * VS_LIN_SEARCH - slow linear search (as in a normal value string)
680
     * VS_BIN_SEARCH - log(n)-time binary search, the values must be sorted
681
     * VS_INDEX      - constant-time index lookup, the values must be contiguous
682
     */
683
1
    enum { VS_LIN_SEARCH, VS_BIN_SEARCH, VS_INDEX } type = VS_INDEX;
684
685
    /* Note: The val64_string 'value' is *unsigned*, but we do a little magic
686
     * to help with value strings that have negative values.
687
     *
688
     * { -3, -2, -1, 0, 1, 2 }
689
     * will be treated as "ascending ordered" (although it isn't technically),
690
     * thus allowing constant-time index search
691
     *
692
     * { -3, -2, 0, 1, 2 } and { -3, -2, -1, 0, 2 }
693
     * will both be considered as "out-of-order with gaps", thus falling
694
     * back to the slow linear search
695
     *
696
     * { 0, 1, 2, -3, -2 } and { 0, 2, -3, -2, -1 }
697
     * will be considered "ascending ordered with gaps" thus allowing
698
     * a log(n)-time 'binary' search
699
     *
700
     * If you're confused, think of how negative values are represented, or
701
     * google two's complement.
702
     */
703
704
1
    uint64_t prev_value;
705
1
    uint64_t first_value;
706
1
    unsigned   i;
707
708
1
    if ((vs_p[vs_num_entries].value != 0) ||
709
1
        (vs_p[vs_num_entries].strptr != NULL)) {
710
0
        ws_warning("vse must end with {0, NULL}");
711
0
        return NULL;
712
0
    }
713
714
1
    vse->_vs_first_value = vs_p[0].value;
715
1
    first_value          = vs_p[0].value;
716
1
    prev_value           = first_value;
717
718
31
    for (i = 0; i < vs_num_entries; i++) {
719
30
        if (vs_p[i].strptr == NULL)
720
30
            ws_warning("vse[%u].strptr cannot be NULL!", i);
721
30
        if ((type == VS_INDEX) && (vs_p[i].value != (i + first_value))) {
722
1
            type = VS_BIN_SEARCH;
723
1
        }
724
        /* XXX: Should check for dups ?? */
725
30
        if (type == VS_BIN_SEARCH) {
726
28
            if (prev_value > vs_p[i].value) {
727
0
                ws_warning("Extended value string '%s' forced to fall back to linear search:\n"
728
0
                          "  entry %u, value %" PRIu64 " [%#" PRIx64 "] < previous entry, value %" PRIu64 " [%#" PRIx64 "]",
729
0
                          vse->_vs_name, i, vs_p[i].value, vs_p[i].value, prev_value, prev_value);
730
0
                type = VS_LIN_SEARCH;
731
0
                break;
732
0
            }
733
28
            if (first_value > vs_p[i].value) {
734
0
                ws_warning("Extended value string '%s' forced to fall back to linear search:\n"
735
0
                          "  entry %u, value %" PRIu64 " [%#" PRIx64 "] < first entry, value %" PRIu64 " [%#" PRIx64 "]",
736
0
                          vse->_vs_name, i, vs_p[i].value, vs_p[i].value, first_value, first_value);
737
0
                type = VS_LIN_SEARCH;
738
0
                break;
739
0
            }
740
28
        }
741
742
30
        prev_value = vs_p[i].value;
743
30
    }
744
745
1
    switch (type) {
746
0
        case VS_LIN_SEARCH:
747
0
            vse->_vs_match2 = _try_val64_to_str_linear;
748
0
            break;
749
1
        case VS_BIN_SEARCH:
750
1
            vse->_vs_match2 = _try_val64_to_str_bsearch;
751
1
            break;
752
0
        case VS_INDEX:
753
0
            vse->_vs_match2 = _try_val64_to_str_index;
754
0
            break;
755
0
        default:
756
0
            ws_assert_not_reached();
757
0
            break;
758
1
    }
759
760
1
    return vse->_vs_match2(val, vse);
761
1
}
762
763
/* STRING TO STRING MATCHING */
764
765
/* string_string is like value_string except the values being matched are
766
 * also strings (instead of unsigned integers) */
767
768
/* Like val_to_str except for string_string */
769
const char *
770
str_to_str_wmem(wmem_allocator_t* scope, const char *val, const string_string *vs, const char *fmt)
771
772
{
772
772
    const char *ret;
773
774
772
    ret = try_str_to_str(val, vs);
775
772
    if (ret != NULL)
776
66
        return ret;
777
778
706
    if (fmt == NULL)
779
0
        return wmem_strdup(scope, "(invalid argument: fmt cannot be NULL)");
780
781
706
    return wmem_strdup_printf(scope, fmt, val);
782
706
}
783
784
/* Like try_val_to_str_idx except for string_string */
785
const char *
786
try_str_to_str_idx(const char *val, const string_string *vs, int *idx)
787
773
{
788
773
    int i = 0;
789
790
773
    if(vs) {
791
4.22k
        while (vs[i].strptr) {
792
3.51k
            if (!strcmp(vs[i].value,val)) {
793
66
                *idx = i;
794
66
                return(vs[i].strptr);
795
66
            }
796
3.45k
            i++;
797
3.45k
        }
798
773
    }
799
800
707
    *idx = -1;
801
707
    return NULL;
802
773
}
803
804
/* Like try_val_to_str except for string_string */
805
const char *
806
try_str_to_str(const char *val, const string_string *vs)
807
772
{
808
772
    int ignore_me;
809
772
    return try_str_to_str_idx(val, vs, &ignore_me);
810
772
}
811
812
/* RANGE TO STRING MATCHING */
813
814
/* range_string is like value_string except the values being matched are
815
 * integer ranges (for example, 0-10, 11-19, etc.) instead of single values. */
816
817
/* Like val_to_str except for range_string */
818
const char *
819
rval_to_str_wmem(wmem_allocator_t* scope, const uint32_t val, const range_string *rs, const char *fmt)
820
149
{
821
149
    const char *ret = NULL;
822
823
149
    ret = try_rval_to_str(val, rs);
824
149
    if(ret != NULL)
825
142
        return ret;
826
827
7
    if (fmt == NULL)
828
0
        return wmem_strdup(scope, "(invalid argument: fmt cannot be NULL)");
829
830
7
    return wmem_strdup_printf(scope, fmt, val);
831
7
}
832
833
/* Like val_to_str_const except for range_string */
834
const char *
835
rval_to_str_const(const uint32_t val, const range_string *rs,
836
        const char *unknown_str)
837
23.5k
{
838
23.5k
    const char *ret = NULL;
839
840
23.5k
    ret = try_rval_to_str(val, rs);
841
23.5k
    if(ret != NULL)
842
14.4k
        return ret;
843
844
9.06k
    if (unknown_str == NULL)
845
0
        return "unknown_str cannot be NULL";
846
847
9.06k
    return unknown_str;
848
9.06k
}
849
850
/* Like try_val_to_str_idx except for range_string */
851
const char *
852
try_rval_to_str_idx(const uint32_t val, const range_string *rs, int *idx)
853
37.9k
{
854
37.9k
    int i = 0;
855
856
37.9k
    if(rs) {
857
710k
        while(rs[i].strptr) {
858
699k
            if( (val >= rs[i].value_min) && (val <= rs[i].value_max) ) {
859
27.5k
                *idx = i;
860
27.5k
                return (rs[i].strptr);
861
27.5k
            }
862
672k
            i++;
863
672k
        }
864
37.9k
    }
865
866
10.4k
    *idx = -1;
867
10.4k
    return NULL;
868
37.9k
}
869
870
/* Like try_val_to_str except for range_string */
871
const char *
872
try_rval_to_str(const uint32_t val, const range_string *rs)
873
37.9k
{
874
37.9k
    int ignore_me = 0;
875
37.9k
    return try_rval_to_str_idx(val, rs, &ignore_me);
876
37.9k
}
877
878
/* Like try_val_to_str_idx except for range_string */
879
const char *
880
try_rval64_to_str_idx(const uint64_t val, const range_string *rs, int *idx)
881
0
{
882
0
    int i = 0;
883
884
0
    if(rs) {
885
0
        while(rs[i].strptr) {
886
0
            if( (val >= rs[i].value_min) && (val <= rs[i].value_max) ) {
887
0
                *idx = i;
888
0
                return (rs[i].strptr);
889
0
            }
890
0
            i++;
891
0
        }
892
0
    }
893
894
0
    *idx = -1;
895
0
    return NULL;
896
0
}
897
898
/* Like try_val64_to_str except for range_string */
899
const char *
900
try_rval64_to_str(const uint64_t val, const range_string *rs)
901
0
{
902
0
    int ignore_me = 0;
903
0
    return try_rval64_to_str_idx(val, rs, &ignore_me);
904
0
}
905
906
/* TIME TO STRING MATCHING */
907
908
/* Tries to match val against each element in the value_string array vs.
909
   Returns the associated string ptr, and sets "*idx" to the index in
910
   that table, on a match, and returns NULL, and sets "*idx" to -1,
911
   on failure. */
912
const char *
913
try_time_val_to_str(const nstime_t *val, const time_value_string *vs)
914
0
{
915
0
    int i = 0;
916
917
0
    if(vs) {
918
0
        while (vs[i].strptr) {
919
0
            if (nstime_cmp(&vs[i].value, val) == 0) {
920
0
                return(vs[i].strptr);
921
0
            }
922
0
            i++;
923
0
        }
924
0
    }
925
926
0
    return NULL;
927
0
}
928
929
/* BYTE BUFFER TO STRING MATCHING */
930
931
/* Like val_to_str except for bytes_string */
932
const char *
933
bytesval_to_str_wmem(wmem_allocator_t* scope, const uint8_t *val, const size_t val_len, const bytes_string *bs, const char *fmt)
934
25
{
935
25
    const char *ret;
936
937
25
    ret = try_bytesval_to_str(val, val_len, bs);
938
25
    if (ret != NULL)
939
0
        return ret;
940
941
25
    if (fmt == NULL)
942
0
        return wmem_strdup(scope, "(invalid argument: fmt cannot be NULL)");
943
944
    /*
945
     * XXX should this use bytes_to_str as format parameter for consistency?
946
     * Though for bytes I guess most of the time you want to show "Unknown"
947
     * anyway rather than "Unknown (\x13\x37...)"
948
     */
949
25
    return wmem_strdup(scope, fmt);
950
25
}
951
952
/* Like try_val_to_str except for bytes_string */
953
const char *
954
try_bytesval_to_str(const uint8_t *val, const size_t val_len, const bytes_string *bs)
955
25
{
956
25
    unsigned i = 0;
957
958
25
    if (bs) {
959
4.70k
        while (bs[i].strptr) {
960
4.67k
            if (bs[i].value_length == val_len && !memcmp(bs[i].value, val, val_len)) {
961
0
                return bs[i].strptr;
962
0
            }
963
4.67k
            i++;
964
4.67k
        }
965
25
    }
966
967
25
    return NULL;
968
25
}
969
970
/* Like val_to_str, but tries to find a prefix (instead of an exact) match
971
   of any prefix from the bytes_string array bs against the haystack. */
972
const char *
973
bytesprefix_to_str(wmem_allocator_t* scope, const uint8_t *haystack, const size_t haystack_len, const bytes_string *bs, const char *fmt)
974
0
{
975
0
    const char *ret;
976
977
0
    ret = try_bytesprefix_to_str(haystack, haystack_len, bs);
978
0
    if (ret != NULL)
979
0
        return ret;
980
981
0
    if (fmt == NULL)
982
0
        return wmem_strdup(scope, "(invalid argument: fmt cannot be NULL)");
983
984
    /* XXX See note at bytesval_to_str. */
985
0
    return wmem_strdup(scope, fmt);
986
0
}
987
988
/* Like try_val_to_str, but tries to find a prefix (instead of an exact) match
989
   of any prefix from the bytes_string array bs against the haystack. */
990
const char *
991
try_bytesprefix_to_str(const uint8_t *haystack, const size_t haystack_len, const bytes_string *bs)
992
0
{
993
0
    unsigned i = 0;
994
995
0
    if (bs) {
996
0
        while (bs[i].strptr) {
997
0
            if (haystack_len >= bs[i].value_length &&
998
0
                !memcmp(bs[i].value, haystack, bs[i].value_length)) {
999
0
                return bs[i].strptr;
1000
0
            }
1001
0
            i++;
1002
0
        }
1003
0
    }
1004
1005
0
    return NULL;
1006
0
}
1007
1008
void
1009
value_string_externals_init(void)
1010
14
{
1011
14
    registered_vs = g_hash_table_new(g_str_hash, g_str_equal);
1012
14
    registered_vs_ext = g_hash_table_new(g_str_hash, g_str_equal);
1013
14
}
1014
1015
void value_string_externals_cleanup(void)
1016
0
{
1017
0
    g_hash_table_destroy(registered_vs);
1018
0
    g_hash_table_destroy(registered_vs_ext);
1019
0
}
1020
1021
void
1022
register_external_value_string(const char* name, const value_string* vs)
1023
574
{
1024
574
    g_hash_table_insert(registered_vs, (void*)name, (void*)vs);
1025
574
}
1026
1027
value_string*
1028
get_external_value_string(const char* name)
1029
0
{
1030
0
    return (value_string*)g_hash_table_lookup(registered_vs, name);
1031
0
}
1032
1033
void
1034
register_external_value_string_ext(const char* name, const value_string_ext* vse)
1035
168
{
1036
168
    g_hash_table_insert(registered_vs_ext, (void*)name, (void*)vse);
1037
168
}
1038
1039
value_string_ext*
1040
get_external_value_string_ext(const char* name)
1041
0
{
1042
0
    return (value_string_ext*)g_hash_table_lookup(registered_vs_ext, name);
1043
0
}
1044
1045
1046
1047
/* MISC */
1048
1049
/* Functions for use by proto_registrar_dump_values(), see proto.c */
1050
1051
bool
1052
value_string_ext_validate(const value_string_ext *vse)
1053
0
{
1054
0
    if (vse == NULL)
1055
0
        return false;
1056
0
#ifndef _WIN32  /* doesn't work on Windows for refs from another DLL ?? */
1057
0
    if ((vse->_vs_match2 != _try_val_to_str_ext_init) &&
1058
0
        (vse->_vs_match2 != _try_val_to_str_linear)   &&
1059
0
        (vse->_vs_match2 != _try_val_to_str_bsearch)  &&
1060
0
        (vse->_vs_match2 != _try_val_to_str_index))
1061
0
        return false;
1062
0
#endif
1063
0
    return true;
1064
0
}
1065
1066
const char *
1067
value_string_ext_match_type_str(const value_string_ext *vse)
1068
0
{
1069
0
    if (vse->_vs_match2 == _try_val_to_str_ext_init)
1070
0
        return "[Not Initialized]";
1071
0
    if (vse->_vs_match2 == _try_val_to_str_linear)
1072
0
        return "[Linear Search]";
1073
0
    if (vse->_vs_match2 == _try_val_to_str_bsearch)
1074
0
        return "[Binary Search]";
1075
0
    if (vse->_vs_match2 == _try_val_to_str_index)
1076
0
        return "[Direct (indexed) Access]";
1077
0
    return "[Invalid]";
1078
0
}
1079
1080
bool
1081
val64_string_ext_validate(const val64_string_ext *vse)
1082
0
{
1083
0
    if (vse == NULL)
1084
0
        return false;
1085
0
#ifndef _WIN32  /* doesn't work on Windows for refs from another DLL ?? */
1086
0
    if ((vse->_vs_match2 != _try_val64_to_str_ext_init) &&
1087
0
        (vse->_vs_match2 != _try_val64_to_str_linear)   &&
1088
0
        (vse->_vs_match2 != _try_val64_to_str_bsearch)  &&
1089
0
        (vse->_vs_match2 != _try_val64_to_str_index))
1090
0
        return false;
1091
0
#endif
1092
0
    return true;
1093
0
}
1094
1095
const char *
1096
val64_string_ext_match_type_str(const val64_string_ext *vse)
1097
0
{
1098
0
    if (vse->_vs_match2 == _try_val64_to_str_ext_init)
1099
0
        return "[Not Initialized]";
1100
0
    if (vse->_vs_match2 == _try_val64_to_str_linear)
1101
0
        return "[Linear Search]";
1102
0
    if (vse->_vs_match2 == _try_val64_to_str_bsearch)
1103
0
        return "[Binary Search]";
1104
0
    if (vse->_vs_match2 == _try_val64_to_str_index)
1105
0
        return "[Direct (indexed) Access]";
1106
0
    return "[Invalid]";
1107
0
}
1108
1109
/*
1110
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
1111
 *
1112
 * Local variables:
1113
 * c-basic-offset: 4
1114
 * tab-width: 8
1115
 * indent-tabs-mode: nil
1116
 * End:
1117
 *
1118
 * vi: set shiftwidth=4 tabstop=8 expandtab:
1119
 * :indentSize=4:tabSize=8:noTabs=true:
1120
 */