Fix CID 1111814: segment_item is set but not used (in one conditional).
[metze/wireshark/wip.git] / epan / value_string.c
1 /* value_string.c
2  * Routines for value_strings
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24
25 #include "config.h"
26
27 #include <stdio.h>
28 #include <string.h>
29
30 #include "emem.h"
31 #include "wmem/wmem.h"
32 #include "proto.h"
33 #include "to_str.h"
34 #include "value_string.h"
35
36 /* REGULAR VALUE STRING */
37
38 /* Tries to match val against each element in the value_string array vs.
39    Returns the associated string ptr on a match.
40    Formats val with fmt, and returns the resulting string, on failure. */
41 const gchar*
42 val_to_str(const guint32 val, const value_string *vs, const char *fmt)
43 {
44     const gchar *ret;
45
46     DISSECTOR_ASSERT(fmt != NULL);
47
48     ret = try_val_to_str(val, vs);
49     if (ret != NULL)
50         return ret;
51
52     return ep_strdup_printf(fmt, val);
53 }
54
55 /* Tries to match val against each element in the value_string array vs.
56    Returns the associated string ptr on a match.
57    Returns 'unknown_str', on failure. */
58 const gchar*
59 val_to_str_const(const guint32 val, const value_string *vs,
60         const char *unknown_str)
61 {
62     const gchar *ret;
63
64     DISSECTOR_ASSERT(unknown_str != NULL);
65
66     ret = try_val_to_str(val, vs);
67     if (ret != NULL)
68         return ret;
69
70     return unknown_str;
71 }
72
73 /* Tries to match val against each element in the value_string array vs.
74    Returns the associated string ptr, and sets "*idx" to the index in
75    that table, on a match, and returns NULL, and sets "*idx" to -1,
76    on failure. */
77 const gchar*
78 try_val_to_str_idx(const guint32 val, const value_string *vs, gint *idx)
79 {
80     gint i = 0;
81
82     DISSECTOR_ASSERT(idx != NULL);
83
84     if(vs) {
85         while (vs[i].strptr) {
86             if (vs[i].value == val) {
87                 *idx = i;
88                 return(vs[i].strptr);
89             }
90             i++;
91         }
92     }
93
94     *idx = -1;
95     return NULL;
96 }
97
98 /* Like try_val_to_str_idx(), but doesn't return the index. */
99 const gchar*
100 try_val_to_str(const guint32 val, const value_string *vs)
101 {
102     gint ignore_me;
103     return try_val_to_str_idx(val, vs, &ignore_me);
104 }
105
106 /* 64-BIT VALUE STRING */
107
108 const gchar*
109 val64_to_str(const guint64 val, const val64_string *vs, const char *fmt)
110 {
111     const gchar *ret;
112
113     DISSECTOR_ASSERT(fmt != NULL);
114
115     ret = try_val64_to_str(val, vs);
116     if (ret != NULL)
117         return ret;
118
119     return ep_strdup_printf(fmt, val);
120 }
121
122 const gchar*
123 val64_to_str_const(const guint64 val, const val64_string *vs,
124         const char *unknown_str)
125 {
126     const gchar *ret;
127
128     DISSECTOR_ASSERT(unknown_str != NULL);
129
130     ret = try_val64_to_str(val, vs);
131     if (ret != NULL)
132         return ret;
133
134     return unknown_str;
135 }
136
137 const gchar*
138 try_val64_to_str_idx(const guint64 val, const val64_string *vs, gint *idx)
139 {
140     gint i = 0;
141
142     DISSECTOR_ASSERT(idx != NULL);
143
144     if(vs) {
145         while (vs[i].strptr) {
146             if (vs[i].value == val) {
147                 *idx = i;
148                 return(vs[i].strptr);
149             }
150             i++;
151         }
152     }
153
154     *idx = -1;
155     return NULL;
156 }
157
158 const gchar*
159 try_val64_to_str(const guint64 val, const val64_string *vs)
160 {
161     gint ignore_me;
162     return try_val64_to_str_idx(val, vs, &ignore_me);
163 }
164
165 /* REVERSE VALUE STRING */
166
167 /* We use the same struct as for regular value strings, but we look up strings
168  * and return values instead */
169
170 /* Like val_to_str except backwards */
171 guint32
172 str_to_val(const gchar *val, const value_string *vs, const guint32 err_val)
173 {
174     gint i;
175
176     i = str_to_val_idx(val, vs);
177
178     if (i >= 0) {
179         return vs[i].value;
180     }
181
182     return err_val;
183 }
184
185 /* Find the index of a string in a value_string, or -1 when not present */
186 gint
187 str_to_val_idx(const gchar *val, const value_string *vs)
188 {
189     gint i = 0;
190
191     if(vs) {
192
193         while (vs[i].strptr) {
194
195             if (strcmp(vs[i].strptr, val) == 0) {
196                 return i;
197             }
198
199             i++;
200         }
201
202     }
203
204     return -1;
205 }
206
207 /* EXTENDED VALUE STRING */
208
209 /* Extended value strings allow fast(er) value_string array lookups by
210  * using (if possible) direct access or a binary search of the array.
211  *
212  * If the values in the value_string array are a contiguous range of values
213  * from min to max, the value will be used as as a direct index into the array.
214  *
215  * If the values in the array are not contiguous (ie: there are "gaps"),
216  * but are in assending order a binary search will be used.
217  *
218  * If direct access or binary search cannot be used, then a linear search
219  * is used and a warning is emitted.
220  *
221  * Note that the value_string array used with VALUE_STRING_EXT_INIT
222  * *must* be terminated with {0, NULL}).
223  *
224  * Extended value strings are defined at compile time as follows:
225  *   static const value_string vs[] = { {value1, "string1"},
226  *                                      {value2, "string2"},
227  *                                      ...,
228  *                                      {0, NULL}};
229  *   static value_string_ext vse = VALUE_STRING_EXT_INIT(vs);
230  *
231  * Extended value strings can be created at runtime by calling
232  *   value_string_ext_new(<ptr to value_string array>,
233  *                        <total number of entries in the value_string_array>,
234  *                        <value_string_name>);
235  * Note: The <total number of entries in the value_string_array> should include
236  *       the {0, NULL} entry.
237  */
238
239 /* Create a value_string_ext given a ptr to a value_string array and the total
240  * number of entries. Note that the total number of entries should include the
241  * required {0, NULL} terminating entry of the array.
242  * Returns a pointer to an epan-scoped'd and initialized value_string_ext
243  * struct. */
244 value_string_ext *
245 value_string_ext_new(value_string *vs, guint vs_tot_num_entries,
246         const gchar *vs_name)
247 {
248     value_string_ext *vse;
249
250     DISSECTOR_ASSERT (vs_name != NULL);
251     DISSECTOR_ASSERT (vs_tot_num_entries > 0);
252     /* Null-terminated value-string ? */
253     DISSECTOR_ASSERT (vs[vs_tot_num_entries-1].strptr == NULL);
254
255     vse                  = wmem_new(wmem_epan_scope(), value_string_ext);
256     vse->_vs_p           = vs;
257     vse->_vs_num_entries = vs_tot_num_entries - 1;
258     /* We set our 'match' function to the init function, which finishes by
259      * setting the match function properly and then calling it. This is a
260      * simple way to do lazy initialization of extended value strings.
261      * The init function also sets up _vs_first_value for us. */
262     vse->_vs_first_value = 0;
263     vse->_vs_match2      = _try_val_to_str_ext_init;
264     vse->_vs_name        = vs_name;
265
266     return vse;
267 }
268
269 /* Like try_val_to_str for extended value strings */
270 const gchar*
271 try_val_to_str_ext(const guint32 val, const value_string_ext *vse)
272 {
273     if (vse) {
274         const value_string *vs = vse->_vs_match2(val, vse);
275
276         if (vs) {
277             return vs->strptr;
278         }
279     }
280
281     return NULL;
282 }
283
284 /* Like try_val_to_str_idx for extended value strings */
285 const gchar*
286 try_val_to_str_idx_ext(const guint32 val, value_string_ext *vse, gint *idx)
287 {
288     if (vse) {
289         const value_string *vs = vse->_vs_match2(val, vse);
290         if (vs) {
291             *idx = (gint) (vs - vse->_vs_p);
292             return vs->strptr;
293         }
294     }
295     *idx = -1;
296     return NULL;
297 }
298
299 /* Like val_to_str for extended value strings */
300 const gchar*
301 val_to_str_ext(const guint32 val, const value_string_ext *vse, const char *fmt)
302 {
303     const gchar *ret;
304
305     DISSECTOR_ASSERT(fmt != NULL);
306
307     ret = try_val_to_str_ext(val, vse);
308     if (ret != NULL)
309         return ret;
310
311     return ep_strdup_printf(fmt, val);
312 }
313
314 /* Like val_to_str_const for extended value strings */
315 const gchar*
316 val_to_str_ext_const(const guint32 val, const value_string_ext *vse,
317         const char *unknown_str)
318 {
319     const gchar *ret;
320
321     DISSECTOR_ASSERT(unknown_str != NULL);
322
323     ret = try_val_to_str_ext(val, vse);
324     if (ret != NULL)
325         return ret;
326
327     return unknown_str;
328 }
329
330 /* Fallback linear matching algorithm for extended value strings */
331 static const value_string *
332 _try_val_to_str_linear(const guint32 val, const value_string_ext *vse)
333 {
334     const value_string *vs_p = vse->_vs_p;
335     guint i;
336     for (i=0; i<vse->_vs_num_entries; i++) {
337         if (vs_p[i].value == val)
338             return &(vs_p[i]);
339     }
340     return NULL;
341 }
342
343 /* Constant-time matching algorithm for contiguous extended value strings */
344 static const value_string *
345 _try_val_to_str_index(const guint32 val, const value_string_ext *vse)
346 {
347     guint i;
348
349     i = val - vse->_vs_first_value;
350     if (i < vse->_vs_num_entries) {
351         g_assert (val == vse->_vs_p[i].value);
352         return &(vse->_vs_p[i]);
353     }
354     return NULL;
355 }
356
357 /* log(n)-time matching algorithm for sorted extended value strings */
358 static const value_string *
359 _try_val_to_str_bsearch(const guint32 val, const value_string_ext *vse)
360 {
361     guint low, i, max;
362     guint32 item;
363
364     for (low = 0, max = vse->_vs_num_entries; low < max; ) {
365         i = (low + max) / 2;
366         item = vse->_vs_p[i].value;
367
368         if (val < item)
369             max = i;
370         else if (val > item)
371             low = i + 1;
372         else
373             return &(vse->_vs_p[i]);
374     }
375     return NULL;
376 }
377
378 /* Initializes an extended value string. Behaves like a match function to
379  * permit lazy initialization of extended value strings.
380  * - Goes through the value_string array to determine the fastest possible
381  *   access method.
382  * - Verifies that the value_string contains no NULL string pointers.
383  * - Verifies that the value_string is terminated by {0, NULL}
384  */
385 const value_string *
386 _try_val_to_str_ext_init(const guint32 val, const value_string_ext *a_vse)
387 {
388     /* Cast away the constness!
389      * It's better if the prototype for this function matches the other
390      * _try_val_to_str_* functions (so we don't have to cast it when storing it
391      * in _try_val_to_str so the compiler will notice if the prototypes get out
392      * of sync), but the init function is unique in that it does actually
393      * modify the vse.
394      */
395     value_string_ext    *vse            = (value_string_ext *)a_vse;
396
397     const value_string *vs_p           = vse->_vs_p;
398     const guint         vs_num_entries = vse->_vs_num_entries;
399
400     /* The matching algorithm used:
401      * VS_SEARCH   - slow sequential search (as in a normal value string)
402      * VS_BIN_TREE - log(n)-time binary search, the values must be sorted
403      * VS_INDEX    - constant-time index lookup, the values must be contiguous
404      */
405     enum { VS_SEARCH, VS_BIN_TREE, VS_INDEX } type = VS_INDEX;
406
407     /* Note: The value_string 'value' is *unsigned*, but we do a little magic
408      * to help with value strings that have negative values.
409      *
410      * { -3, -2, -1, 0, 1, 2 }
411      * will be treated as "ascending ordered" (although it isn't technically),
412      * thus allowing constant-time index search
413      *
414      * { -3, -2, 0, 1, 2 } and { -3, -2, -1, 0, 2 }
415      * will both be considered as "out-of-order with gaps", thus falling
416      * back to the slow linear search
417      *
418      * { 0, 1, 2, -3, -2 } and { 0, 2, -3, -2, -1 }
419      * will be considered "ascending ordered with gaps" thus allowing
420      * a log(n)-time 'binary' search
421      *
422      * If you're confused, think of how negative values are represented, or
423      * google two's complement.
424      */
425
426     guint32 prev_value;
427     guint   first_value;
428     guint   i;
429
430     DISSECTOR_ASSERT((vs_p[vs_num_entries].value  == 0) &&
431                      (vs_p[vs_num_entries].strptr == NULL));
432
433     vse->_vs_first_value = vs_p[0].value;
434     first_value          = vs_p[0].value;
435     prev_value           = first_value;
436
437     for (i = 0; i < vs_num_entries; i++) {
438         DISSECTOR_ASSERT(vs_p[i].strptr != NULL);
439         if ((type == VS_INDEX) && (vs_p[i].value != (i + first_value))) {
440             type = VS_BIN_TREE;
441         }
442         /* XXX: Should check for dups ?? */
443         if (type == VS_BIN_TREE) {
444             if (prev_value > vs_p[i].value) {
445                 g_warning("Extended value string %s forced to fall back to linear search: entry %u, value %u < previous entry, value %u",
446                         vse->_vs_name, i, vs_p[i].value, prev_value);
447                 type = VS_SEARCH;
448                 break;
449             }
450             if (first_value > vs_p[i].value) {
451                 g_warning("Extended value string %s forced to fall back to linear search: entry %u, value %u < first entry, value %u",
452                         vse->_vs_name, i, vs_p[i].value, first_value);
453                 type = VS_SEARCH;
454                 break;
455             }
456         }
457
458         prev_value = vs_p[i].value;
459     }
460
461     switch (type) {
462         case VS_SEARCH:
463             vse->_vs_match2 = _try_val_to_str_linear;
464             break;
465         case VS_BIN_TREE:
466             vse->_vs_match2 = _try_val_to_str_bsearch;
467             break;
468         case VS_INDEX:
469             vse->_vs_match2 = _try_val_to_str_index;
470             break;
471         default:
472             g_assert_not_reached();
473             break;
474     }
475
476     return vse->_vs_match2(val, vse);
477 }
478
479 /* STRING TO STRING MATCHING */
480
481 /* string_string is like value_string except the values being matched are
482  * also strings (instead of unsigned integers) */
483
484 /* Like val_to_str except for string_string */
485 const gchar*
486 str_to_str(const gchar *val, const string_string *vs, const char *fmt)
487 {
488     const gchar *ret;
489
490     DISSECTOR_ASSERT(fmt != NULL);
491
492     ret = try_str_to_str(val, vs);
493     if (ret != NULL)
494         return ret;
495
496     return ep_strdup_printf(fmt, val);
497 }
498
499 /* Like try_val_to_str_idx except for string_string */
500 const gchar*
501 try_str_to_str_idx(const gchar *val, const string_string *vs, gint *idx)
502 {
503     gint i = 0;
504
505     if(vs) {
506         while (vs[i].strptr) {
507             if (!strcmp(vs[i].value,val)) {
508                 *idx = i;
509                 return(vs[i].strptr);
510             }
511             i++;
512         }
513     }
514
515     *idx = -1;
516     return NULL;
517 }
518
519 /* Like try_val_to_str except for string_string */
520 const gchar*
521 try_str_to_str(const gchar *val, const string_string *vs)
522 {
523     gint ignore_me;
524     return try_str_to_str_idx(val, vs, &ignore_me);
525 }
526
527 /* RANGE TO STRING MATCHING */
528
529 /* range_string is like value_string except the values being matched are
530  * integer ranges (for example, 0-10, 11-19, etc.) instead of single values. */
531
532 /* Like val_to_str except for range_string */
533 const gchar *
534 rval_to_str(const guint32 val, const range_string *rs, const char *fmt)
535 {
536     const gchar *ret = NULL;
537
538     DISSECTOR_ASSERT(fmt != NULL);
539
540     ret = try_rval_to_str(val, rs);
541     if(ret != NULL)
542         return ret;
543
544     return ep_strdup_printf(fmt, val);
545 }
546
547 /* Like val_to_str_const except for range_string */
548 const gchar *
549 rval_to_str_const(const guint32 val, const range_string *rs,
550         const char *unknown_str)
551 {
552     const gchar *ret = NULL;
553
554     DISSECTOR_ASSERT(unknown_str != NULL);
555
556     ret = try_rval_to_str(val, rs);
557     if(ret != NULL)
558         return ret;
559
560     return unknown_str;
561 }
562
563 /* Like try_val_to_str_idx except for range_string */
564 const gchar *
565 try_rval_to_str_idx(const guint32 val, const range_string *rs, gint *idx)
566 {
567     gint i = 0;
568
569     if(rs) {
570         while(rs[i].strptr) {
571             if( (val >= rs[i].value_min) && (val <= rs[i].value_max) ) {
572                 *idx = i;
573                 return (rs[i].strptr);
574             }
575             i++;
576         }
577     }
578
579     *idx = -1;
580     return NULL;
581 }
582
583 /* Like try_val_to_str except for range_string */
584 const gchar *
585 try_rval_to_str(const guint32 val, const range_string *rs)
586 {
587     gint ignore_me = 0;
588     return try_rval_to_str_idx(val, rs, &ignore_me);
589 }
590
591 /* MISC */
592
593 /* Functions for use by proto_registrar_dump_values(), see proto.c */
594
595 gboolean
596 value_string_ext_validate(const value_string_ext *vse)
597 {
598     if (vse == NULL)
599         return FALSE;
600     if ((vse->_vs_match2 == _try_val_to_str_ext_init) ||
601             (vse->_vs_match2 == _try_val_to_str_linear)   ||
602             (vse->_vs_match2 == _try_val_to_str_bsearch)  ||
603             (vse->_vs_match2 == _try_val_to_str_index))
604         return TRUE;
605     return FALSE;
606 }
607
608 const gchar *
609 value_string_ext_match_type_str(const value_string_ext *vse)
610 {
611     if (vse->_vs_match2 == _try_val_to_str_linear)
612         return "[Linear Search]";
613     if (vse->_vs_match2 == _try_val_to_str_bsearch)
614         return "[Binary Search]";
615     if (vse->_vs_match2 == _try_val_to_str_index)
616         return "[Direct (indexed) Access]";
617     return "[Match Type not initialized or invalid]";
618 }
619
620 /*
621  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
622  *
623  * Local variables:
624  * c-basic-offset: 4
625  * tab-width: 8
626  * indent-tabs-mode: nil
627  * End:
628  *
629  * vi: set shiftwidth=4 tabstop=8 expandtab:
630  * :indentSize=4:tabSize=8:noTabs=true:
631  */