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