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