2 * Routines for value_strings
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
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.
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.
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.
28 #include "wmem/wmem.h"
31 #include "value_string.h"
32 #include <wsutil/ws_printf.h> /* ws_g_warning */
34 /* REGULAR VALUE STRING */
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. */
40 val_to_str(const guint32 val, const value_string *vs, const char *fmt)
44 DISSECTOR_ASSERT(fmt != NULL);
46 ret = try_val_to_str(val, vs);
50 return wmem_strdup_printf(wmem_packet_scope(), fmt, val);
54 val_to_str_wmem(wmem_allocator_t *scope, const guint32 val, const value_string *vs, const char *fmt)
58 DISSECTOR_ASSERT(fmt != NULL);
60 ret = try_val_to_str(val, vs);
62 return wmem_strdup(scope, ret);
64 return wmem_strdup_printf(scope, fmt, val);
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. */
71 val_to_str_const(const guint32 val, const value_string *vs,
72 const char *unknown_str)
76 DISSECTOR_ASSERT(unknown_str != NULL);
78 ret = try_val_to_str(val, vs);
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,
90 try_val_to_str_idx(const guint32 val, const value_string *vs, gint *idx)
94 DISSECTOR_ASSERT(idx != NULL);
97 while (vs[i].strptr) {
98 if (vs[i].value == val) {
100 return(vs[i].strptr);
110 /* Like try_val_to_str_idx(), but doesn't return the index. */
112 try_val_to_str(const guint32 val, const value_string *vs)
115 return try_val_to_str_idx(val, vs, &ignore_me);
118 /* 64-BIT VALUE STRING */
121 val64_to_str(const guint64 val, const val64_string *vs, const char *fmt)
125 DISSECTOR_ASSERT(fmt != NULL);
127 ret = try_val64_to_str(val, vs);
131 return wmem_strdup_printf(wmem_packet_scope(), fmt, val);
135 val64_to_str_const(const guint64 val, const val64_string *vs,
136 const char *unknown_str)
140 DISSECTOR_ASSERT(unknown_str != NULL);
142 ret = try_val64_to_str(val, vs);
150 try_val64_to_str_idx(const guint64 val, const val64_string *vs, gint *idx)
154 DISSECTOR_ASSERT(idx != NULL);
157 while (vs[i].strptr) {
158 if (vs[i].value == val) {
160 return(vs[i].strptr);
171 try_val64_to_str(const guint64 val, const val64_string *vs)
174 return try_val64_to_str_idx(val, vs, &ignore_me);
177 /* REVERSE VALUE STRING */
179 /* We use the same struct as for regular value strings, but we look up strings
180 * and return values instead */
182 /* Like val_to_str except backwards */
184 str_to_val(const gchar *val, const value_string *vs, const guint32 err_val)
188 i = str_to_val_idx(val, vs);
197 /* Find the index of a string in a value_string, or -1 when not present */
199 str_to_val_idx(const gchar *val, const value_string *vs)
205 while (vs[i].strptr) {
207 if (strcmp(vs[i].strptr, val) == 0) {
219 /* EXTENDED VALUE STRING */
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.
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.
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.
230 * If direct access or binary search cannot be used, then a linear search
231 * is used and a warning is emitted.
233 * Note that the value_string array used with VALUE_STRING_EXT_INIT
234 * *must* be terminated with {0, NULL}).
236 * Extended value strings are defined at compile time as follows:
237 * static const value_string vs[] = { {value1, "string1"},
238 * {value2, "string2"},
241 * static value_string_ext vse = VALUE_STRING_EXT_INIT(vs);
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.
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
257 value_string_ext_new(const value_string *vs, guint vs_tot_num_entries,
258 const gchar *vs_name)
260 value_string_ext *vse;
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);
267 vse = wmem_new(wmem_epan_scope(), value_string_ext);
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;
282 value_string_ext_free(value_string_ext *vse)
284 wmem_free(wmem_epan_scope(), vse);
287 /* Like try_val_to_str for extended value strings */
289 try_val_to_str_ext(const guint32 val, value_string_ext *vse)
292 const value_string *vs = vse->_vs_match2(val, vse);
302 /* Like try_val_to_str_idx for extended value strings */
304 try_val_to_str_idx_ext(const guint32 val, value_string_ext *vse, gint *idx)
307 const value_string *vs = vse->_vs_match2(val, vse);
309 *idx = (gint) (vs - vse->_vs_p);
317 /* Like val_to_str for extended value strings */
319 val_to_str_ext(const guint32 val, value_string_ext *vse, const char *fmt)
323 DISSECTOR_ASSERT(fmt != NULL);
325 ret = try_val_to_str_ext(val, vse);
329 return wmem_strdup_printf(wmem_packet_scope(), fmt, val);
333 val_to_str_ext_wmem(wmem_allocator_t *scope, const guint32 val, value_string_ext *vse, const char *fmt)
337 DISSECTOR_ASSERT(fmt != NULL);
339 ret = try_val_to_str_ext(val, vse);
341 return wmem_strdup(scope, ret);
343 return wmem_strdup_printf(scope, fmt, val);
346 /* Like val_to_str_const for extended value strings */
348 val_to_str_ext_const(const guint32 val, value_string_ext *vse,
349 const char *unknown_str)
353 DISSECTOR_ASSERT(unknown_str != NULL);
355 ret = try_val_to_str_ext(val, vse);
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)
366 const value_string *vs_p = vse->_vs_p;
368 for (i=0; i<vse->_vs_num_entries; i++) {
369 if (vs_p[i].value == val)
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)
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]);
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)
396 for (low = 0, max = vse->_vs_num_entries; low < max; ) {
398 item = vse->_vs_p[i].value;
405 return &(vse->_vs_p[i]);
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
414 * - Verifies that the value_string contains no NULL string pointers.
415 * - Verifies that the value_string is terminated by {0, NULL}
418 _try_val_to_str_ext_init(const guint32 val, value_string_ext *vse)
420 const value_string *vs_p = vse->_vs_p;
421 const guint vs_num_entries = vse->_vs_num_entries;
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
428 enum { VS_SEARCH, VS_BIN_TREE, VS_INDEX } type = VS_INDEX;
430 /* Note: The value_string 'value' is *unsigned*, but we do a little magic
431 * to help with value strings that have negative values.
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
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
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
445 * If you're confused, think of how negative values are represented, or
446 * google two's complement.
453 DISSECTOR_ASSERT((vs_p[vs_num_entries].value == 0) &&
454 (vs_p[vs_num_entries].strptr == NULL));
456 vse->_vs_first_value = vs_p[0].value;
457 first_value = vs_p[0].value;
458 prev_value = first_value;
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))) {
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);
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);
483 prev_value = vs_p[i].value;
488 vse->_vs_match2 = _try_val_to_str_linear;
491 vse->_vs_match2 = _try_val_to_str_bsearch;
494 vse->_vs_match2 = _try_val_to_str_index;
497 g_assert_not_reached();
501 return vse->_vs_match2(val, vse);
504 /* STRING TO STRING MATCHING */
506 /* string_string is like value_string except the values being matched are
507 * also strings (instead of unsigned integers) */
509 /* Like val_to_str except for string_string */
511 str_to_str(const gchar *val, const string_string *vs, const char *fmt)
515 DISSECTOR_ASSERT(fmt != NULL);
517 ret = try_str_to_str(val, vs);
521 return wmem_strdup_printf(wmem_packet_scope(), fmt, val);
524 /* Like try_val_to_str_idx except for string_string */
526 try_str_to_str_idx(const gchar *val, const string_string *vs, gint *idx)
531 while (vs[i].strptr) {
532 if (!strcmp(vs[i].value,val)) {
534 return(vs[i].strptr);
544 /* Like try_val_to_str except for string_string */
546 try_str_to_str(const gchar *val, const string_string *vs)
549 return try_str_to_str_idx(val, vs, &ignore_me);
552 /* RANGE TO STRING MATCHING */
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. */
557 /* Like val_to_str except for range_string */
559 rval_to_str(const guint32 val, const range_string *rs, const char *fmt)
561 const gchar *ret = NULL;
563 DISSECTOR_ASSERT(fmt != NULL);
565 ret = try_rval_to_str(val, rs);
569 return wmem_strdup_printf(wmem_packet_scope(), fmt, val);
572 /* Like val_to_str_const except for range_string */
574 rval_to_str_const(const guint32 val, const range_string *rs,
575 const char *unknown_str)
577 const gchar *ret = NULL;
579 DISSECTOR_ASSERT(unknown_str != NULL);
581 ret = try_rval_to_str(val, rs);
588 /* Like try_val_to_str_idx except for range_string */
590 try_rval_to_str_idx(const guint32 val, const range_string *rs, gint *idx)
595 while(rs[i].strptr) {
596 if( (val >= rs[i].value_min) && (val <= rs[i].value_max) ) {
598 return (rs[i].strptr);
608 /* Like try_val_to_str except for range_string */
610 try_rval_to_str(const guint32 val, const range_string *rs)
613 return try_rval_to_str_idx(val, rs, &ignore_me);
618 /* Functions for use by proto_registrar_dump_values(), see proto.c */
621 value_string_ext_validate(const value_string_ext *vse)
625 #ifndef _WIN32 /* doesn't work on Windows for refs from another DLL ?? */
626 if ((vse->_vs_match2 != _try_val_to_str_ext_init) &&
627 (vse->_vs_match2 != _try_val_to_str_linear) &&
628 (vse->_vs_match2 != _try_val_to_str_bsearch) &&
629 (vse->_vs_match2 != _try_val_to_str_index))
636 value_string_ext_match_type_str(const value_string_ext *vse)
638 if (vse->_vs_match2 == _try_val_to_str_ext_init)
639 return "[Not Initialized]";
640 if (vse->_vs_match2 == _try_val_to_str_linear)
641 return "[Linear Search]";
642 if (vse->_vs_match2 == _try_val_to_str_bsearch)
643 return "[Binary Search]";
644 if (vse->_vs_match2 == _try_val_to_str_index)
645 return "[Direct (indexed) Access]";
650 * Editor modelines - http://www.wireshark.org/tools/modelines.html
655 * indent-tabs-mode: nil
658 * vi: set shiftwidth=4 tabstop=8 expandtab:
659 * :indentSize=4:tabSize=8:noTabs=true: