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