checkAPIs.pl: support for new-style dissectors in check_hf_entries
[metze/wireshark/wip.git] / epan / value_string.c
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
13 #include <stdio.h>
14 #include <string.h>
15
16 #include "wmem/wmem.h"
17 #include "proto.h"
18 #include "to_str.h"
19 #include "value_string.h"
20
21 /* REGULAR VALUE STRING */
22
23 /* Tries to match val against each element in the value_string array vs.
24    Returns the associated string ptr on a match.
25    Formats val with fmt, and returns the resulting string, on failure. */
26 const gchar *
27 val_to_str(const guint32 val, const value_string *vs, const char *fmt)
28 {
29     const gchar *ret;
30
31     DISSECTOR_ASSERT(fmt != NULL);
32
33     ret = try_val_to_str(val, vs);
34     if (ret != NULL)
35         return ret;
36
37     return wmem_strdup_printf(wmem_packet_scope(), fmt, val);
38 }
39
40 gchar *
41 val_to_str_wmem(wmem_allocator_t *scope, const guint32 val, const value_string *vs, const char *fmt)
42 {
43     const gchar *ret;
44
45     DISSECTOR_ASSERT(fmt != NULL);
46
47     ret = try_val_to_str(val, vs);
48     if (ret != NULL)
49         return wmem_strdup(scope, ret);
50
51     return wmem_strdup_printf(scope, fmt, val);
52 }
53
54 /* Tries to match val against each element in the value_string array vs.
55    Returns the associated string ptr on a match.
56    Returns 'unknown_str', on failure. */
57 const gchar *
58 val_to_str_const(const guint32 val, const value_string *vs,
59         const char *unknown_str)
60 {
61     const gchar *ret;
62
63     DISSECTOR_ASSERT(unknown_str != NULL);
64
65     ret = try_val_to_str(val, vs);
66     if (ret != NULL)
67         return ret;
68
69     return unknown_str;
70 }
71
72 /* Tries to match val against each element in the value_string array vs.
73    Returns the associated string ptr, and sets "*idx" to the index in
74    that table, on a match, and returns NULL, and sets "*idx" to -1,
75    on failure. */
76 const gchar *
77 try_val_to_str_idx(const guint32 val, const value_string *vs, gint *idx)
78 {
79     gint i = 0;
80
81     DISSECTOR_ASSERT(idx != NULL);
82
83     if(vs) {
84         while (vs[i].strptr) {
85             if (vs[i].value == val) {
86                 *idx = i;
87                 return(vs[i].strptr);
88             }
89             i++;
90         }
91     }
92
93     *idx = -1;
94     return NULL;
95 }
96
97 /* Like try_val_to_str_idx(), but doesn't return the index. */
98 const gchar *
99 try_val_to_str(const guint32 val, const value_string *vs)
100 {
101     gint ignore_me;
102     return try_val_to_str_idx(val, vs, &ignore_me);
103 }
104
105 /* 64-BIT VALUE STRING */
106
107 const gchar *
108 val64_to_str(const guint64 val, const val64_string *vs, const char *fmt)
109 {
110     const gchar *ret;
111
112     DISSECTOR_ASSERT(fmt != NULL);
113
114     ret = try_val64_to_str(val, vs);
115     if (ret != NULL)
116         return ret;
117
118     return wmem_strdup_printf(wmem_packet_scope(), fmt, val);
119 }
120
121 const gchar *
122 val64_to_str_const(const guint64 val, const val64_string *vs,
123         const char *unknown_str)
124 {
125     const gchar *ret;
126
127     DISSECTOR_ASSERT(unknown_str != NULL);
128
129     ret = try_val64_to_str(val, vs);
130     if (ret != NULL)
131         return ret;
132
133     return unknown_str;
134 }
135
136 const gchar *
137 try_val64_to_str_idx(const guint64 val, const val64_string *vs, gint *idx)
138 {
139     gint i = 0;
140
141     DISSECTOR_ASSERT(idx != NULL);
142
143     if(vs) {
144         while (vs[i].strptr) {
145             if (vs[i].value == val) {
146                 *idx = i;
147                 return(vs[i].strptr);
148             }
149             i++;
150         }
151     }
152
153     *idx = -1;
154     return NULL;
155 }
156
157 const gchar *
158 try_val64_to_str(const guint64 val, const val64_string *vs)
159 {
160     gint ignore_me;
161     return try_val64_to_str_idx(val, vs, &ignore_me);
162 }
163
164 /* REVERSE VALUE STRING */
165
166 /* We use the same struct as for regular value strings, but we look up strings
167  * and return values instead */
168
169 /* Like val_to_str except backwards */
170 guint32
171 str_to_val(const gchar *val, const value_string *vs, const guint32 err_val)
172 {
173     gint i;
174
175     i = str_to_val_idx(val, vs);
176
177     if (i >= 0) {
178         return vs[i].value;
179     }
180
181     return err_val;
182 }
183
184 /* Find the index of a string in a value_string, or -1 when not present */
185 gint
186 str_to_val_idx(const gchar *val, const value_string *vs)
187 {
188     gint i = 0;
189
190     if(vs) {
191
192         while (vs[i].strptr) {
193
194             if (strcmp(vs[i].strptr, val) == 0) {
195                 return i;
196             }
197
198             i++;
199         }
200
201     }
202
203     return -1;
204 }
205
206 /* EXTENDED VALUE STRING */
207
208 /* Extended value strings allow fast(er) value_string array lookups by
209  * using (if possible) direct access or a binary search of the array.
210  *
211  * If the values in the value_string array are a contiguous range of values
212  * from min to max, the value will be used as as a direct index into the array.
213  *
214  * If the values in the array are not contiguous (ie: there are "gaps"),
215  * but are in assending order a binary search will be used.
216  *
217  * If direct access or binary search cannot be used, then a linear search
218  * is used and a warning is emitted.
219  *
220  * Note that the value_string array used with VALUE_STRING_EXT_INIT
221  * *must* be terminated with {0, NULL}).
222  *
223  * Extended value strings are defined at compile time as follows:
224  *   static const value_string vs[] = { {value1, "string1"},
225  *                                      {value2, "string2"},
226  *                                      ...,
227  *                                      {0, NULL}};
228  *   static value_string_ext vse = VALUE_STRING_EXT_INIT(vs);
229  *
230  * Extended value strings can be created at runtime by calling
231  *   value_string_ext_new(<ptr to value_string array>,
232  *                        <total number of entries in the value_string_array>,
233  *                        <value_string_name>);
234  * Note: The <total number of entries in the value_string_array> should include
235  *       the {0, NULL} entry.
236  */
237
238 /* Create a value_string_ext given a ptr to a value_string array and the total
239  * number of entries. Note that the total number of entries should include the
240  * required {0, NULL} terminating entry of the array.
241  * Returns a pointer to an epan-scoped'd and initialized value_string_ext
242  * struct. */
243 value_string_ext *
244 value_string_ext_new(const value_string *vs, guint vs_tot_num_entries,
245         const gchar *vs_name)
246 {
247     value_string_ext *vse;
248
249     DISSECTOR_ASSERT (vs_name != NULL);
250     DISSECTOR_ASSERT (vs_tot_num_entries > 0);
251     /* Null-terminated value-string ? */
252     DISSECTOR_ASSERT (vs[vs_tot_num_entries-1].strptr == NULL);
253
254     vse                  = wmem_new(wmem_epan_scope(), value_string_ext);
255     vse->_vs_p           = vs;
256     vse->_vs_num_entries = vs_tot_num_entries - 1;
257     /* We set our 'match' function to the init function, which finishes by
258      * setting the match function properly and then calling it. This is a
259      * simple way to do lazy initialization of extended value strings.
260      * The init function also sets up _vs_first_value for us. */
261     vse->_vs_first_value = 0;
262     vse->_vs_match2      = _try_val_to_str_ext_init;
263     vse->_vs_name        = vs_name;
264
265     return vse;
266 }
267
268 void
269 value_string_ext_free(value_string_ext *vse)
270 {
271     wmem_free(wmem_epan_scope(), vse);
272 }
273
274 /* Like try_val_to_str for extended value strings */
275 const gchar *
276 try_val_to_str_ext(const guint32 val, value_string_ext *vse)
277 {
278     if (vse) {
279         const value_string *vs = vse->_vs_match2(val, vse);
280
281         if (vs) {
282             return vs->strptr;
283         }
284     }
285
286     return NULL;
287 }
288
289 /* Like try_val_to_str_idx for extended value strings */
290 const gchar *
291 try_val_to_str_idx_ext(const guint32 val, value_string_ext *vse, gint *idx)
292 {
293     if (vse) {
294         const value_string *vs = vse->_vs_match2(val, vse);
295         if (vs) {
296             *idx = (gint) (vs - vse->_vs_p);
297             return vs->strptr;
298         }
299     }
300     *idx = -1;
301     return NULL;
302 }
303
304 /* Like val_to_str for extended value strings */
305 const gchar *
306 val_to_str_ext(const guint32 val, value_string_ext *vse, const char *fmt)
307 {
308     const gchar *ret;
309
310     DISSECTOR_ASSERT(fmt != NULL);
311
312     ret = try_val_to_str_ext(val, vse);
313     if (ret != NULL)
314         return ret;
315
316     return wmem_strdup_printf(wmem_packet_scope(), fmt, val);
317 }
318
319 gchar *
320 val_to_str_ext_wmem(wmem_allocator_t *scope, const guint32 val, value_string_ext *vse, const char *fmt)
321 {
322     const gchar *ret;
323
324     DISSECTOR_ASSERT(fmt != NULL);
325
326     ret = try_val_to_str_ext(val, vse);
327     if (ret != NULL)
328         return wmem_strdup(scope, ret);
329
330     return wmem_strdup_printf(scope, fmt, val);
331 }
332
333 /* Like val_to_str_const for extended value strings */
334 const gchar *
335 val_to_str_ext_const(const guint32 val, value_string_ext *vse,
336         const char *unknown_str)
337 {
338     const gchar *ret;
339
340     DISSECTOR_ASSERT(unknown_str != NULL);
341
342     ret = try_val_to_str_ext(val, vse);
343     if (ret != NULL)
344         return ret;
345
346     return unknown_str;
347 }
348
349 /* Fallback linear matching algorithm for extended value strings */
350 static const value_string *
351 _try_val_to_str_linear(const guint32 val, value_string_ext *vse)
352 {
353     const value_string *vs_p = vse->_vs_p;
354     guint i;
355     for (i=0; i<vse->_vs_num_entries; i++) {
356         if (vs_p[i].value == val)
357             return &(vs_p[i]);
358     }
359     return NULL;
360 }
361
362 /* Constant-time matching algorithm for contiguous extended value strings */
363 static const value_string *
364 _try_val_to_str_index(const guint32 val, value_string_ext *vse)
365 {
366     guint i;
367
368     i = val - vse->_vs_first_value;
369     if (i < vse->_vs_num_entries) {
370         g_assert (val == vse->_vs_p[i].value);
371         return &(vse->_vs_p[i]);
372     }
373     return NULL;
374 }
375
376 /* log(n)-time matching algorithm for sorted extended value strings */
377 static const value_string *
378 _try_val_to_str_bsearch(const guint32 val, value_string_ext *vse)
379 {
380     guint low, i, max;
381     guint32 item;
382
383     for (low = 0, max = vse->_vs_num_entries; low < max; ) {
384         i = (low + max) / 2;
385         item = vse->_vs_p[i].value;
386
387         if (val < item)
388             max = i;
389         else if (val > item)
390             low = i + 1;
391         else
392             return &(vse->_vs_p[i]);
393     }
394     return NULL;
395 }
396
397 /* Initializes an extended value string. Behaves like a match function to
398  * permit lazy initialization of extended value strings.
399  * - Goes through the value_string array to determine the fastest possible
400  *   access method.
401  * - Verifies that the value_string contains no NULL string pointers.
402  * - Verifies that the value_string is terminated by {0, NULL}
403  */
404 const value_string *
405 _try_val_to_str_ext_init(const guint32 val, value_string_ext *vse)
406 {
407     const value_string *vs_p           = vse->_vs_p;
408     const guint         vs_num_entries = vse->_vs_num_entries;
409
410     /* The matching algorithm used:
411      * VS_SEARCH   - slow sequential search (as in a normal value string)
412      * VS_BIN_TREE - log(n)-time binary search, the values must be sorted
413      * VS_INDEX    - constant-time index lookup, the values must be contiguous
414      */
415     enum { VS_SEARCH, VS_BIN_TREE, VS_INDEX } type = VS_INDEX;
416
417     /* Note: The value_string 'value' is *unsigned*, but we do a little magic
418      * to help with value strings that have negative values.
419      *
420      * { -3, -2, -1, 0, 1, 2 }
421      * will be treated as "ascending ordered" (although it isn't technically),
422      * thus allowing constant-time index search
423      *
424      * { -3, -2, 0, 1, 2 } and { -3, -2, -1, 0, 2 }
425      * will both be considered as "out-of-order with gaps", thus falling
426      * back to the slow linear search
427      *
428      * { 0, 1, 2, -3, -2 } and { 0, 2, -3, -2, -1 }
429      * will be considered "ascending ordered with gaps" thus allowing
430      * a log(n)-time 'binary' search
431      *
432      * If you're confused, think of how negative values are represented, or
433      * google two's complement.
434      */
435
436     guint32 prev_value;
437     guint   first_value;
438     guint   i;
439
440     DISSECTOR_ASSERT((vs_p[vs_num_entries].value  == 0) &&
441                      (vs_p[vs_num_entries].strptr == NULL));
442
443     vse->_vs_first_value = vs_p[0].value;
444     first_value          = vs_p[0].value;
445     prev_value           = first_value;
446
447     for (i = 0; i < vs_num_entries; i++) {
448         DISSECTOR_ASSERT(vs_p[i].strptr != NULL);
449         if ((type == VS_INDEX) && (vs_p[i].value != (i + first_value))) {
450             type = VS_BIN_TREE;
451         }
452         /* XXX: Should check for dups ?? */
453         if (type == VS_BIN_TREE) {
454             if (prev_value > vs_p[i].value) {
455                 g_warning("Extended value string '%s' forced to fall back to linear search:\n"
456                           "  entry %u, value %u [%#x] < previous entry, value %u [%#x]",
457                           vse->_vs_name, i, vs_p[i].value, vs_p[i].value, prev_value, prev_value);
458                 type = VS_SEARCH;
459                 break;
460             }
461             if (first_value > vs_p[i].value) {
462                 g_warning("Extended value string '%s' forced to fall back to linear search:\n"
463                           "  entry %u, value %u [%#x] < first entry, value %u [%#x]",
464                           vse->_vs_name, i, vs_p[i].value, vs_p[i].value, first_value, first_value);
465                 type = VS_SEARCH;
466                 break;
467             }
468         }
469
470         prev_value = vs_p[i].value;
471     }
472
473     switch (type) {
474         case VS_SEARCH:
475             vse->_vs_match2 = _try_val_to_str_linear;
476             break;
477         case VS_BIN_TREE:
478             vse->_vs_match2 = _try_val_to_str_bsearch;
479             break;
480         case VS_INDEX:
481             vse->_vs_match2 = _try_val_to_str_index;
482             break;
483         default:
484             g_assert_not_reached();
485             break;
486     }
487
488     return vse->_vs_match2(val, vse);
489 }
490
491 /* STRING TO STRING MATCHING */
492
493 /* string_string is like value_string except the values being matched are
494  * also strings (instead of unsigned integers) */
495
496 /* Like val_to_str except for string_string */
497 const gchar *
498 str_to_str(const gchar *val, const string_string *vs, const char *fmt)
499 {
500     const gchar *ret;
501
502     DISSECTOR_ASSERT(fmt != NULL);
503
504     ret = try_str_to_str(val, vs);
505     if (ret != NULL)
506         return ret;
507
508     return wmem_strdup_printf(wmem_packet_scope(), fmt, val);
509 }
510
511 /* Like try_val_to_str_idx except for string_string */
512 const gchar *
513 try_str_to_str_idx(const gchar *val, const string_string *vs, gint *idx)
514 {
515     gint i = 0;
516
517     if(vs) {
518         while (vs[i].strptr) {
519             if (!strcmp(vs[i].value,val)) {
520                 *idx = i;
521                 return(vs[i].strptr);
522             }
523             i++;
524         }
525     }
526
527     *idx = -1;
528     return NULL;
529 }
530
531 /* Like try_val_to_str except for string_string */
532 const gchar *
533 try_str_to_str(const gchar *val, const string_string *vs)
534 {
535     gint ignore_me;
536     return try_str_to_str_idx(val, vs, &ignore_me);
537 }
538
539 /* RANGE TO STRING MATCHING */
540
541 /* range_string is like value_string except the values being matched are
542  * integer ranges (for example, 0-10, 11-19, etc.) instead of single values. */
543
544 /* Like val_to_str except for range_string */
545 const gchar *
546 rval_to_str(const guint32 val, const range_string *rs, const char *fmt)
547 {
548     const gchar *ret = NULL;
549
550     DISSECTOR_ASSERT(fmt != NULL);
551
552     ret = try_rval_to_str(val, rs);
553     if(ret != NULL)
554         return ret;
555
556     return wmem_strdup_printf(wmem_packet_scope(), fmt, val);
557 }
558
559 /* Like val_to_str_const except for range_string */
560 const gchar *
561 rval_to_str_const(const guint32 val, const range_string *rs,
562         const char *unknown_str)
563 {
564     const gchar *ret = NULL;
565
566     DISSECTOR_ASSERT(unknown_str != NULL);
567
568     ret = try_rval_to_str(val, rs);
569     if(ret != NULL)
570         return ret;
571
572     return unknown_str;
573 }
574
575 /* Like try_val_to_str_idx except for range_string */
576 const gchar *
577 try_rval_to_str_idx(const guint32 val, const range_string *rs, gint *idx)
578 {
579     gint i = 0;
580
581     if(rs) {
582         while(rs[i].strptr) {
583             if( (val >= rs[i].value_min) && (val <= rs[i].value_max) ) {
584                 *idx = i;
585                 return (rs[i].strptr);
586             }
587             i++;
588         }
589     }
590
591     *idx = -1;
592     return NULL;
593 }
594
595 /* Like try_val_to_str except for range_string */
596 const gchar *
597 try_rval_to_str(const guint32 val, const range_string *rs)
598 {
599     gint ignore_me = 0;
600     return try_rval_to_str_idx(val, rs, &ignore_me);
601 }
602
603 /* Like try_val_to_str_idx except for range_string */
604 const gchar *
605 try_rval64_to_str_idx(const guint64 val, const range_string *rs, gint *idx)
606 {
607     gint i = 0;
608
609     if(rs) {
610         while(rs[i].strptr) {
611             if( (val >= rs[i].value_min) && (val <= rs[i].value_max) ) {
612                 *idx = i;
613                 return (rs[i].strptr);
614             }
615             i++;
616         }
617     }
618
619     *idx = -1;
620     return NULL;
621 }
622
623 /* Like try_val64_to_str except for range_string */
624 const gchar *
625 try_rval64_to_str(const guint64 val, const range_string *rs)
626 {
627     gint ignore_me = 0;
628     return try_rval64_to_str_idx(val, rs, &ignore_me);
629 }
630
631
632 /* BYTE BUFFER TO STRING MATCHING */
633
634 /* Like val_to_str except for bytes_string */
635 const gchar *
636 bytesval_to_str(const guint8 *val, const size_t val_len, const bytes_string *bs, const char *fmt)
637 {
638     const gchar *ret;
639
640     DISSECTOR_ASSERT(fmt != NULL);
641
642     ret = try_bytesval_to_str(val, val_len, bs);
643     if (ret != NULL)
644         return ret;
645
646     /*
647      * XXX should this use bytes_to_str as format parameter for consistency?
648      * Though for bytes I guess most of the time you want to show "Unknown"
649      * anyway rather than "Unknown (\x13\x37...)"
650      */
651     return wmem_strdup(wmem_packet_scope(), fmt);
652 }
653
654 /* Like try_val_to_str except for bytes_string */
655 const gchar *
656 try_bytesval_to_str(const guint8 *val, const size_t val_len, const bytes_string *bs)
657 {
658     guint i = 0;
659
660     if (bs) {
661         while (bs[i].strptr) {
662             if (bs[i].value_length == val_len && !memcmp(bs[i].value, val, val_len)) {
663                 return bs[i].strptr;
664             }
665             i++;
666         }
667     }
668
669     return NULL;
670 }
671
672 /* Like val_to_str, but tries to find a prefix (instead of an exact) match
673    of any prefix from the bytes_string array bs against the haystack. */
674 const gchar *
675 bytesprefix_to_str(const guint8 *haystack, const size_t haystack_len, const bytes_string *bs, const char *fmt)
676 {
677     const gchar *ret;
678
679     DISSECTOR_ASSERT(fmt != NULL);
680
681     ret = try_bytesprefix_to_str(haystack, haystack_len, bs);
682     if (ret != NULL)
683         return ret;
684
685     /* XXX See note at bytesval_to_str. */
686     return wmem_strdup(wmem_packet_scope(), fmt);
687 }
688
689 /* Like try_val_to_str, but tries to find a prefix (instead of an exact) match
690    of any prefix from the bytes_string array bs against the haystack. */
691 const gchar *
692 try_bytesprefix_to_str(const guint8 *haystack, const size_t haystack_len, const bytes_string *bs)
693 {
694     guint i = 0;
695
696     if (bs) {
697         while (bs[i].strptr) {
698             if (haystack_len >= bs[i].value_length &&
699                 !memcmp(bs[i].value, haystack, bs[i].value_length)) {
700                 return bs[i].strptr;
701             }
702             i++;
703         }
704     }
705
706     return NULL;
707 }
708
709 /* MISC */
710
711 /* Functions for use by proto_registrar_dump_values(), see proto.c */
712
713 gboolean
714 value_string_ext_validate(const value_string_ext *vse)
715 {
716     if (vse == NULL)
717         return FALSE;
718 #ifndef _WIN32  /* doesn't work on Windows for refs from another DLL ?? */
719     if ((vse->_vs_match2 != _try_val_to_str_ext_init) &&
720         (vse->_vs_match2 != _try_val_to_str_linear)   &&
721         (vse->_vs_match2 != _try_val_to_str_bsearch)  &&
722         (vse->_vs_match2 != _try_val_to_str_index))
723         return FALSE;
724 #endif
725     return TRUE;
726 }
727
728 const gchar *
729 value_string_ext_match_type_str(const value_string_ext *vse)
730 {
731     if (vse->_vs_match2 == _try_val_to_str_ext_init)
732         return "[Not Initialized]";
733     if (vse->_vs_match2 == _try_val_to_str_linear)
734         return "[Linear Search]";
735     if (vse->_vs_match2 == _try_val_to_str_bsearch)
736         return "[Binary Search]";
737     if (vse->_vs_match2 == _try_val_to_str_index)
738         return "[Direct (indexed) Access]";
739     return "[Invalid]";
740 }
741
742 /*
743  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
744  *
745  * Local variables:
746  * c-basic-offset: 4
747  * tab-width: 8
748  * indent-tabs-mode: nil
749  * End:
750  *
751  * vi: set shiftwidth=4 tabstop=8 expandtab:
752  * :indentSize=4:tabSize=8:noTabs=true:
753  */