2 * Routines for value_strings
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
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.
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.
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.
31 #include "wmem/wmem.h"
34 #include "value_string.h"
36 /* REGULAR VALUE STRING */
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. */
42 val_to_str(const guint32 val
, const value_string
*vs
, const char *fmt
)
46 DISSECTOR_ASSERT(fmt
!= NULL
);
48 ret
= try_val_to_str(val
, vs
);
52 return ep_strdup_printf(fmt
, val
);
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. */
59 val_to_str_const(const guint32 val
, const value_string
*vs
,
60 const char *unknown_str
)
64 DISSECTOR_ASSERT(unknown_str
!= NULL
);
66 ret
= try_val_to_str(val
, vs
);
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,
78 try_val_to_str_idx(const guint32 val
, const value_string
*vs
, gint
*idx
)
82 DISSECTOR_ASSERT(idx
!= NULL
);
85 while (vs
[i
].strptr
) {
86 if (vs
[i
].value
== val
) {
98 /* Like try_val_to_str_idx(), but doesn't return the index. */
100 try_val_to_str(const guint32 val
, const value_string
*vs
)
103 return try_val_to_str_idx(val
, vs
, &ignore_me
);
106 /* 64-BIT VALUE STRING */
109 val64_to_str(const guint64 val
, const val64_string
*vs
, const char *fmt
)
113 DISSECTOR_ASSERT(fmt
!= NULL
);
115 ret
= try_val64_to_str(val
, vs
);
119 return ep_strdup_printf(fmt
, val
);
123 val64_to_str_const(const guint64 val
, const val64_string
*vs
,
124 const char *unknown_str
)
128 DISSECTOR_ASSERT(unknown_str
!= NULL
);
130 ret
= try_val64_to_str(val
, vs
);
138 try_val64_to_str_idx(const guint64 val
, const val64_string
*vs
, gint
*idx
)
142 DISSECTOR_ASSERT(idx
!= NULL
);
145 while (vs
[i
].strptr
) {
146 if (vs
[i
].value
== val
) {
148 return(vs
[i
].strptr
);
159 try_val64_to_str(const guint64 val
, const val64_string
*vs
)
162 return try_val64_to_str_idx(val
, vs
, &ignore_me
);
165 /* REVERSE VALUE STRING */
167 /* We use the same struct as for regular value strings, but we look up strings
168 * and return values instead */
170 /* Like val_to_str except backwards */
172 str_to_val(const gchar
*val
, const value_string
*vs
, const guint32 err_val
)
176 i
= str_to_val_idx(val
, vs
);
185 /* Find the index of a string in a value_string, or -1 when not present */
187 str_to_val_idx(const gchar
*val
, const value_string
*vs
)
193 while (vs
[i
].strptr
) {
195 if (strcmp(vs
[i
].strptr
, val
) == 0) {
207 /* EXTENDED VALUE STRING */
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.
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.
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.
218 * If direct access or binary search cannot be used, then a linear search
219 * is used and a warning is emitted.
221 * Note that the value_string array used with VALUE_STRING_EXT_INIT
222 * *must* be terminated with {0, NULL}).
224 * Extended value strings are defined at compile time as follows:
225 * static const value_string vs[] = { {value1, "string1"},
226 * {value2, "string2"},
229 * static value_string_ext vse = VALUE_STRING_EXT_INIT(vs);
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.
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
245 value_string_ext_new(value_string
*vs
, guint vs_tot_num_entries
,
246 const gchar
*vs_name
)
248 value_string_ext
*vse
;
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
);
255 vse
= wmem_new(wmem_epan_scope(), value_string_ext
);
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
;
269 /* Like try_val_to_str for extended value strings */
271 try_val_to_str_ext(const guint32 val
, const value_string_ext
*vse
)
274 const value_string
*vs
= vse
->_vs_match2(val
, vse
);
284 /* Like try_val_to_str_idx for extended value strings */
286 try_val_to_str_idx_ext(const guint32 val
, value_string_ext
*vse
, gint
*idx
)
289 const value_string
*vs
= vse
->_vs_match2(val
, vse
);
291 *idx
= (gint
) (vs
- vse
->_vs_p
);
299 /* Like val_to_str for extended value strings */
301 val_to_str_ext(const guint32 val
, const value_string_ext
*vse
, const char *fmt
)
305 DISSECTOR_ASSERT(fmt
!= NULL
);
307 ret
= try_val_to_str_ext(val
, vse
);
311 return ep_strdup_printf(fmt
, val
);
314 /* Like val_to_str_const for extended value strings */
316 val_to_str_ext_const(const guint32 val
, const value_string_ext
*vse
,
317 const char *unknown_str
)
321 DISSECTOR_ASSERT(unknown_str
!= NULL
);
323 ret
= try_val_to_str_ext(val
, vse
);
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
)
334 const value_string
*vs_p
= vse
->_vs_p
;
336 for (i
=0; i
<vse
->_vs_num_entries
; i
++) {
337 if (vs_p
[i
].value
== val
)
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
)
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
]);
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
)
364 for (low
= 0, max
= vse
->_vs_num_entries
; low
< max
; ) {
366 item
= vse
->_vs_p
[i
].value
;
373 return &(vse
->_vs_p
[i
]);
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
382 * - Verifies that the value_string contains no NULL string pointers.
383 * - Verifies that the value_string is terminated by {0, NULL}
386 _try_val_to_str_ext_init(const guint32 val
, const value_string_ext
*a_vse
)
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
395 value_string_ext
*vse
= (value_string_ext
*)a_vse
;
397 const value_string
*vs_p
= vse
->_vs_p
;
398 const guint vs_num_entries
= vse
->_vs_num_entries
;
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
405 enum { VS_SEARCH
, VS_BIN_TREE
, VS_INDEX
} type
= VS_INDEX
;
407 /* Note: The value_string 'value' is *unsigned*, but we do a little magic
408 * to help with value strings that have negative values.
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
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
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
422 * If you're confused, think of how negative values are represented, or
423 * google two's complement.
430 DISSECTOR_ASSERT((vs_p
[vs_num_entries
].value
== 0) &&
431 (vs_p
[vs_num_entries
].strptr
== NULL
));
433 vse
->_vs_first_value
= vs_p
[0].value
;
434 first_value
= vs_p
[0].value
;
435 prev_value
= first_value
;
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
))) {
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
);
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
);
458 prev_value
= vs_p
[i
].value
;
463 vse
->_vs_match2
= _try_val_to_str_linear
;
466 vse
->_vs_match2
= _try_val_to_str_bsearch
;
469 vse
->_vs_match2
= _try_val_to_str_index
;
472 g_assert_not_reached();
476 return vse
->_vs_match2(val
, vse
);
479 /* STRING TO STRING MATCHING */
481 /* string_string is like value_string except the values being matched are
482 * also strings (instead of unsigned integers) */
484 /* Like val_to_str except for string_string */
486 str_to_str(const gchar
*val
, const string_string
*vs
, const char *fmt
)
490 DISSECTOR_ASSERT(fmt
!= NULL
);
492 ret
= try_str_to_str(val
, vs
);
496 return ep_strdup_printf(fmt
, val
);
499 /* Like try_val_to_str_idx except for string_string */
501 try_str_to_str_idx(const gchar
*val
, const string_string
*vs
, gint
*idx
)
506 while (vs
[i
].strptr
) {
507 if (!strcmp(vs
[i
].value
,val
)) {
509 return(vs
[i
].strptr
);
519 /* Like try_val_to_str except for string_string */
521 try_str_to_str(const gchar
*val
, const string_string
*vs
)
524 return try_str_to_str_idx(val
, vs
, &ignore_me
);
527 /* RANGE TO STRING MATCHING */
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. */
532 /* Like val_to_str except for range_string */
534 rval_to_str(const guint32 val
, const range_string
*rs
, const char *fmt
)
536 const gchar
*ret
= NULL
;
538 DISSECTOR_ASSERT(fmt
!= NULL
);
540 ret
= try_rval_to_str(val
, rs
);
544 return ep_strdup_printf(fmt
, val
);
547 /* Like val_to_str_const except for range_string */
549 rval_to_str_const(const guint32 val
, const range_string
*rs
,
550 const char *unknown_str
)
552 const gchar
*ret
= NULL
;
554 DISSECTOR_ASSERT(unknown_str
!= NULL
);
556 ret
= try_rval_to_str(val
, rs
);
563 /* Like try_val_to_str_idx except for range_string */
565 try_rval_to_str_idx(const guint32 val
, const range_string
*rs
, gint
*idx
)
570 while(rs
[i
].strptr
) {
571 if( (val
>= rs
[i
].value_min
) && (val
<= rs
[i
].value_max
) ) {
573 return (rs
[i
].strptr
);
583 /* Like try_val_to_str except for range_string */
585 try_rval_to_str(const guint32 val
, const range_string
*rs
)
588 return try_rval_to_str_idx(val
, rs
, &ignore_me
);
593 /* Functions for use by proto_registrar_dump_values(), see proto.c */
596 value_string_ext_validate(const value_string_ext
*vse
)
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
))
609 value_string_ext_match_type_str(const value_string_ext
*vse
)
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]";
621 * Editor modelines - http://www.wireshark.org/tools/modelines.html
626 * indent-tabs-mode: nil
629 * vi: set shiftwidth=4 tabstop=8 expandtab:
630 * :indentSize=4:tabSize=8:noTabs=true: