2 * Routines for utilities to convert various other types to strings.
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
18 #include <wsutil/utf8_entities.h>
19 #include <wsutil/wslog.h>
20 #include <wsutil/inet_addr.h>
21 #include <wsutil/pint.h>
22 #include <wsutil/time_util.h>
25 * If a user _does_ pass in a too-small buffer, this is probably
26 * going to be too long to fit. However, even a partial string
27 * starting with "[Buf" should provide enough of a clue to be
30 #define _return_if_nospace(str_len, buf, buf_len) \
32 if ((str_len) > (buf_len)) { \
33 (void)g_strlcpy(buf, "[Buffer too small]", buf_len); \
38 static const char fast_strings
[][4] = {
39 "0", "1", "2", "3", "4", "5", "6", "7",
40 "8", "9", "10", "11", "12", "13", "14", "15",
41 "16", "17", "18", "19", "20", "21", "22", "23",
42 "24", "25", "26", "27", "28", "29", "30", "31",
43 "32", "33", "34", "35", "36", "37", "38", "39",
44 "40", "41", "42", "43", "44", "45", "46", "47",
45 "48", "49", "50", "51", "52", "53", "54", "55",
46 "56", "57", "58", "59", "60", "61", "62", "63",
47 "64", "65", "66", "67", "68", "69", "70", "71",
48 "72", "73", "74", "75", "76", "77", "78", "79",
49 "80", "81", "82", "83", "84", "85", "86", "87",
50 "88", "89", "90", "91", "92", "93", "94", "95",
51 "96", "97", "98", "99", "100", "101", "102", "103",
52 "104", "105", "106", "107", "108", "109", "110", "111",
53 "112", "113", "114", "115", "116", "117", "118", "119",
54 "120", "121", "122", "123", "124", "125", "126", "127",
55 "128", "129", "130", "131", "132", "133", "134", "135",
56 "136", "137", "138", "139", "140", "141", "142", "143",
57 "144", "145", "146", "147", "148", "149", "150", "151",
58 "152", "153", "154", "155", "156", "157", "158", "159",
59 "160", "161", "162", "163", "164", "165", "166", "167",
60 "168", "169", "170", "171", "172", "173", "174", "175",
61 "176", "177", "178", "179", "180", "181", "182", "183",
62 "184", "185", "186", "187", "188", "189", "190", "191",
63 "192", "193", "194", "195", "196", "197", "198", "199",
64 "200", "201", "202", "203", "204", "205", "206", "207",
65 "208", "209", "210", "211", "212", "213", "214", "215",
66 "216", "217", "218", "219", "220", "221", "222", "223",
67 "224", "225", "226", "227", "228", "229", "230", "231",
68 "232", "233", "234", "235", "236", "237", "238", "239",
69 "240", "241", "242", "243", "244", "245", "246", "247",
70 "248", "249", "250", "251", "252", "253", "254", "255"
74 low_nibble_of_octet_to_hex(uint8_t oct
)
76 /* At least one version of Apple's C compiler/linker is buggy, causing
77 a complaint from the linker about the "literal C string section"
78 not ending with '\0' if we initialize a 16-element "char" array with
79 a 16-character string, the fact that initializing such an array with
80 such a string is perfectly legitimate ANSI C nonwithstanding, the 17th
81 '\0' byte in the string nonwithstanding. */
82 static const char hex_digits
[16] =
83 { '0', '1', '2', '3', '4', '5', '6', '7',
84 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
86 return hex_digits
[oct
& 0xF];
90 byte_to_hex(char *out
, uint32_t dword
)
92 *out
++ = low_nibble_of_octet_to_hex(dword
>> 4);
93 *out
++ = low_nibble_of_octet_to_hex(dword
);
98 uint8_to_hex(char *out
, uint8_t val
)
100 return byte_to_hex(out
, val
);
104 word_to_hex(char *out
, uint16_t word
)
106 out
= byte_to_hex(out
, word
>> 8);
107 out
= byte_to_hex(out
, word
);
112 word_to_hex_punct(char *out
, uint16_t word
, char punct
)
114 out
= byte_to_hex(out
, word
>> 8);
116 out
= byte_to_hex(out
, word
);
121 word_to_hex_npad(char *out
, uint16_t word
)
124 *out
++ = low_nibble_of_octet_to_hex((uint8_t)(word
>> 12));
126 *out
++ = low_nibble_of_octet_to_hex((uint8_t)(word
>> 8));
128 *out
++ = low_nibble_of_octet_to_hex((uint8_t)(word
>> 4));
129 *out
++ = low_nibble_of_octet_to_hex((uint8_t)(word
>> 0));
134 dword_to_hex(char *out
, uint32_t dword
)
136 out
= word_to_hex(out
, dword
>> 16);
137 out
= word_to_hex(out
, dword
);
142 dword_to_hex_punct(char *out
, uint32_t dword
, char punct
)
144 out
= word_to_hex_punct(out
, dword
>> 16, punct
);
146 out
= word_to_hex_punct(out
, dword
, punct
);
151 qword_to_hex(char *out
, uint64_t qword
)
153 out
= dword_to_hex(out
, (uint32_t)(qword
>> 32));
154 out
= dword_to_hex(out
, (uint32_t)(qword
& 0xffffffff));
159 qword_to_hex_punct(char *out
, uint64_t qword
, char punct
)
161 out
= dword_to_hex_punct(out
, (uint32_t)(qword
>> 32), punct
);
163 out
= dword_to_hex_punct(out
, (uint32_t)(qword
& 0xffffffff), punct
);
168 * This does *not* null-terminate the string. It returns a pointer
169 * to the position in the string following the last character it
170 * puts there, so that the caller can either put the null terminator
171 * in or can append more stuff to the buffer.
173 * There needs to be at least len * 2 bytes left in the buffer.
176 bytes_to_hexstr(char *out
, const uint8_t *ad
, size_t len
)
180 ws_return_val_if(!ad
, NULL
);
182 for (i
= 0; i
< len
; i
++)
183 out
= byte_to_hex(out
, ad
[i
]);
188 * This does *not* null-terminate the string. It returns a pointer
189 * to the position in the string following the last character it
190 * puts there, so that the caller can either put the null terminator
191 * in or can append more stuff to the buffer.
193 * There needs to be at least len * 3 - 1 bytes left in the buffer.
196 bytes_to_hexstr_punct(char *out
, const uint8_t *ad
, size_t len
, char punct
)
200 ws_return_val_if(!ad
, NULL
);
202 out
= byte_to_hex(out
, ad
[0]);
203 for (i
= 1; i
< len
; i
++) {
205 out
= byte_to_hex(out
, ad
[i
]);
210 /* Routine to convert a sequence of bytes to a hex string, one byte/two hex
211 * digits at a time, with a specified punctuation character between
214 * If punct is '\0', no punctuation is applied (and thus
215 * the resulting string is (len-1) bytes shorter)
218 bytes_to_str_punct_maxlen(wmem_allocator_t
*scope
,
219 const uint8_t *src
, size_t src_size
,
220 char punct
, size_t max_bytes_len
)
223 size_t max_char_size
;
227 ws_return_str_if(!src
, scope
);
230 return wmem_strdup(scope
, "");
234 return bytes_to_str_maxlen(scope
, src
, src_size
, max_bytes_len
);
236 if (max_bytes_len
== 0 || max_bytes_len
> src_size
) {
237 max_bytes_len
= src_size
;
239 else if (max_bytes_len
< src_size
) {
243 /* Include space for ellipsis and '\0'. Optional extra punct
244 * at the end is already accounted for. */
245 max_char_size
= max_bytes_len
* 3 + strlen(UTF8_HORIZONTAL_ELLIPSIS
) + 1;
247 buf
= wmem_alloc(scope
, max_char_size
);
248 buf_ptr
= bytes_to_hexstr_punct(buf
, src
, max_bytes_len
, punct
);
252 buf_ptr
= g_stpcpy(buf_ptr
, UTF8_HORIZONTAL_ELLIPSIS
);
260 bytes_to_str_maxlen(wmem_allocator_t
*scope
,
261 const uint8_t *src
, size_t src_size
,
262 size_t max_bytes_len
)
265 size_t max_char_size
;
269 ws_return_str_if(!src
, scope
);
272 return wmem_strdup(scope
, "");
275 if (max_bytes_len
== 0 || max_bytes_len
> src_size
) {
276 max_bytes_len
= src_size
;
278 else if (max_bytes_len
< src_size
) {
282 max_char_size
= max_bytes_len
* 2 + strlen(UTF8_HORIZONTAL_ELLIPSIS
) + 1;
284 buf
= wmem_alloc(scope
, max_char_size
);
285 buf_ptr
= bytes_to_hexstr(buf
, src
, max_bytes_len
);
288 buf_ptr
= g_stpcpy(buf_ptr
, UTF8_HORIZONTAL_ELLIPSIS
);
295 * The *_to_str_back() functions measured approx. a x7.5 speed-up versus
296 * snprintf() on my Linux system with GNU libc.
300 oct_to_str_back(char *ptr
, uint32_t value
)
303 *(--ptr
) = '0' + (value
& 0x7);
312 oct64_to_str_back(char *ptr
, uint64_t value
)
315 *(--ptr
) = '0' + (value
& 0x7);
324 hex_to_str_back_len(char *ptr
, uint32_t value
, int len
)
327 *(--ptr
) = low_nibble_of_octet_to_hex(value
);
345 hex64_to_str_back_len(char *ptr
, uint64_t value
, int len
)
348 *(--ptr
) = low_nibble_of_octet_to_hex(value
& 0xF);
366 uint_to_str_back(char *ptr
, uint32_t value
)
374 while (value
>= 10) {
375 p
= fast_strings
[100 + (value
% 100)];
384 *(--ptr
) = (value
) | '0';
390 uint64_to_str_back(char *ptr
, uint64_t value
)
398 while (value
>= 10) {
399 p
= fast_strings
[100 + (value
% 100)];
407 /* value will be 0..9, so using '& 0xF' is safe, and faster than '% 10' */
409 *(--ptr
) = (value
& 0xF) | '0';
415 uint_to_str_back_len(char *ptr
, uint32_t value
, int len
)
419 new_ptr
= uint_to_str_back(ptr
, value
);
421 /* subtract from len number of generated characters */
422 len
-= (int)(ptr
- new_ptr
);
424 /* pad remaining with '0' */
435 uint64_to_str_back_len(char *ptr
, uint64_t value
, int len
)
439 new_ptr
= uint64_to_str_back(ptr
, value
);
441 /* subtract from len number of generated characters */
442 len
-= (int)(ptr
- new_ptr
);
444 /* pad remaining with '0' */
455 int_to_str_back(char *ptr
, int32_t value
)
458 ptr
= uint_to_str_back(ptr
, -value
);
461 ptr
= uint_to_str_back(ptr
, value
);
467 int64_to_str_back(char *ptr
, int64_t value
)
470 ptr
= uint64_to_str_back(ptr
, -value
);
473 ptr
= uint64_to_str_back(ptr
, value
);
479 uint32_to_str_buf_len(const uint32_t u
)
481 /* ((2^32)-1) == 2147483647 */
482 if (u
>= 1000000000)return 10;
483 if (u
>= 100000000) return 9;
484 if (u
>= 10000000) return 8;
485 if (u
>= 1000000) return 7;
486 if (u
>= 100000) return 6;
487 if (u
>= 10000) return 5;
488 if (u
>= 1000) return 4;
489 if (u
>= 100) return 3;
490 if (u
>= 10) return 2;
496 uint32_to_str_buf(uint32_t u
, char *buf
, size_t buf_len
)
498 size_t str_len
= uint32_to_str_buf_len(u
)+1;
500 char *bp
= &buf
[str_len
];
502 _return_if_nospace(str_len
, buf
, buf_len
);
506 uint_to_str_back(bp
, u
);
510 uint64_to_str_buf_len(const uint64_t u
)
512 /* ((2^64)-1) == 18446744073709551615 */
514 if (u
>= UINT64_C(10000000000000000000)) return 20;
515 if (u
>= UINT64_C(1000000000000000000)) return 19;
516 if (u
>= UINT64_C(100000000000000000)) return 18;
517 if (u
>= UINT64_C(10000000000000000)) return 17;
518 if (u
>= UINT64_C(1000000000000000)) return 16;
519 if (u
>= UINT64_C(100000000000000)) return 15;
520 if (u
>= UINT64_C(10000000000000)) return 14;
521 if (u
>= UINT64_C(1000000000000)) return 13;
522 if (u
>= UINT64_C(100000000000)) return 12;
523 if (u
>= UINT64_C(10000000000)) return 11;
524 if (u
>= UINT64_C(1000000000)) return 10;
525 if (u
>= UINT64_C(100000000)) return 9;
526 if (u
>= UINT64_C(10000000)) return 8;
527 if (u
>= UINT64_C(1000000)) return 7;
528 if (u
>= UINT64_C(100000)) return 6;
529 if (u
>= UINT64_C(10000)) return 5;
530 if (u
>= UINT64_C(1000)) return 4;
531 if (u
>= UINT64_C(100)) return 3;
532 if (u
>= UINT64_C(10)) return 2;
538 uint64_to_str_buf(uint64_t u
, char *buf
, size_t buf_len
)
540 size_t str_len
= uint64_to_str_buf_len(u
)+1;
542 char *bp
= &buf
[str_len
];
544 _return_if_nospace(str_len
, buf
, buf_len
);
548 uint64_to_str_back(bp
, u
);
552 This function is very fast and this function is called a lot.
553 XXX update the address_to_str stuff to use this function.
556 ip_addr_to_str_buf(const ws_in4_addr
*_ad
, char *buf
, const int buf_len
)
558 uint8_t *ad
= (uint8_t *)_ad
;
559 register char const *p
;
560 register char *b
=buf
;
562 _return_if_nospace(WS_INET_ADDRSTRLEN
, buf
, buf_len
);
564 p
=fast_strings
[*ad
++];
571 p
=fast_strings
[*ad
++];
578 p
=fast_strings
[*ad
++];
594 ip_addr_to_str(wmem_allocator_t
*scope
, const ws_in4_addr
*ad
)
596 char *buf
= wmem_alloc(scope
, WS_INET_ADDRSTRLEN
* sizeof(char));
598 ip_addr_to_str_buf(ad
, buf
, WS_INET_ADDRSTRLEN
);
604 ip_num_to_str_buf(uint32_t ad
, char *buf
, const int buf_len
)
606 ws_in4_addr addr
= g_htonl(ad
);
607 ip_addr_to_str_buf(&addr
, buf
, buf_len
);
610 /* Host byte order */
612 ip_num_to_str(wmem_allocator_t
*scope
, uint32_t ad
)
614 ws_in4_addr addr
= g_htonl(ad
);
615 return ip_addr_to_str(scope
, &addr
);
619 ip_to_str_buf(const uint8_t *ad
, char *buf
, const int buf_len
)
621 ip_addr_to_str_buf((const ws_in4_addr
*)ad
, buf
, buf_len
);
625 ip_to_str(wmem_allocator_t
*scope
, const uint8_t *ad
)
627 return ip_addr_to_str(scope
, (const ws_in4_addr
*)ad
);
631 ip6_to_str_buf(const ws_in6_addr
*addr
, char *buf
, size_t buf_size
)
634 * If there is not enough space then ws_inet_ntop6() will leave
635 * an error message in the buffer, we don't need
636 * to use _return_if_nospace().
638 ws_inet_ntop6(addr
, buf
, (unsigned)buf_size
);
641 char *ip6_to_str(wmem_allocator_t
*scope
, const ws_in6_addr
*ad
)
643 char *buf
= wmem_alloc(scope
, WS_INET6_ADDRSTRLEN
* sizeof(char));
645 ws_inet_ntop6(ad
, buf
, WS_INET6_ADDRSTRLEN
);
651 ipxnet_to_str_punct(wmem_allocator_t
*allocator
, const uint32_t ad
, const char punct
)
653 char *buf
= (char *)wmem_alloc(allocator
, 12);
655 *dword_to_hex_punct(buf
, ad
, punct
) = '\0';
659 #define WS_EUI64_STRLEN 24
662 eui64_to_str(wmem_allocator_t
*scope
, const uint64_t ad
) {
666 p_eui64
=(uint8_t *)wmem_alloc(NULL
, 8);
667 buf
=(char *)wmem_alloc(scope
, WS_EUI64_STRLEN
);
669 /* Copy and convert the address to network byte order. */
670 *(uint64_t *)(void *)(p_eui64
) = pntoh64(&(ad
));
672 tmp
= bytes_to_hexstr_punct(buf
, p_eui64
, 8, ':');
673 *tmp
= '\0'; /* NULL terminate */
674 wmem_free(NULL
, p_eui64
);
679 * Number of characters required by a 64-bit signed number.
681 #define CHARS_64_BIT_SIGNED 20 /* sign plus 19 digits */
684 * Number of characters required by a fractional part, in nanoseconds,
685 * not counting the decimal point.
687 #define CHARS_NANOSECONDS 9 /* 000000001 */
690 * Format the fractional part of a time, with the specified precision.
691 * Returns the number of bytes formatted.
694 format_fractional_part_nsecs(char *buf
, size_t buflen
, uint32_t nsecs
, const char *decimal_point
, int precision
)
699 size_t decimal_point_len
;
701 int8_t num_buf
[CHARS_NANOSECONDS
];
702 int8_t *num_end
= &num_buf
[CHARS_NANOSECONDS
];
706 ws_assert(precision
!= WS_TSPREC_SEC
);
710 * No room in the buffer for anything, including
711 * a terminating '\0'.
717 * If the fractional part is >= 1, don't show it as a
720 if (nsecs
>= 1000000000U) {
721 num_bytes
= snprintf(buf
, buflen
, "%s(%u nanoseconds)",
722 decimal_point
, nsecs
);
723 if ((unsigned int)num_bytes
>= buflen
) {
725 * That filled up or would have overflowed
726 * the buffer. Nothing more to do; return
727 * the remaining space in the buffer, minus
728 * one byte for the terminating '\0',* as
729 * that's the number of bytes we copied.
731 return (int)(buflen
- 1);
741 * Copy the decimal point.
742 * (We assume here that the locale's decimal point does
743 * not contain so many characters that its size doesn't
744 * fit in an int. :-))
746 decimal_point_len
= g_strlcpy(buf
, decimal_point
, buflen
);
747 if (decimal_point_len
>= buflen
) {
749 * The decimal point didn't fit in the buffer
750 * and was truncated. Nothing more to do;
751 * return the remaining space in the buffer,
752 * minus one byte for the terminating '\0',
753 * as that's the number of bytes we copied.
755 return (int)(buflen
- 1);
757 ptr
+= decimal_point_len
;
758 remaining
-= decimal_point_len
;
759 num_bytes
+= (int)decimal_point_len
;
762 * Fill in num_buf with the nanoseconds value, padded with
763 * leading zeroes, to the specified precision.
765 * We scale the fractional part in advance, as that just
766 * takes one division by a constant (which may be
767 * optimized to a faster multiplication by a constant)
768 * and gets rid of some divisions and remainders by 100
769 * done to generate the digits.
771 * We pass preciions as the last argument to
772 * uint_to_str_back_len(), as that might mean that
773 * all of the cases end up using common code to
774 * do part of the call to uint_to_str_back_len().
778 case WS_TSPREC_100_MSEC
:
780 * Scale down to units of 1/10 second.
782 frac_part
= nsecs
/ 100000000U;
785 case WS_TSPREC_10_MSEC
:
787 * Scale down to units of 1/100 second.
789 frac_part
= nsecs
/ 10000000U;
794 * Scale down to units of 1/1000 second.
796 frac_part
= nsecs
/ 1000000U;
799 case WS_TSPREC_100_USEC
:
801 * Scale down to units of 1/10000 second.
803 frac_part
= nsecs
/ 100000U;
806 case WS_TSPREC_10_USEC
:
808 * Scale down to units of 1/100000 second.
810 frac_part
= nsecs
/ 10000U;
815 * Scale down to units of 1/1000000 second.
817 frac_part
= nsecs
/ 1000U;
820 case WS_TSPREC_100_NSEC
:
822 * Scale down to units of 1/10000000 second.
824 frac_part
= nsecs
/ 100U;
827 case WS_TSPREC_10_NSEC
:
829 * Scale down to units of 1/100000000 second.
831 frac_part
= nsecs
/ 10U;
836 * We're already in units of 1/1000000000 second.
842 ws_assert_not_reached();
846 num_ptr
= uint_to_str_back_len(num_end
, frac_part
, precision
);
849 * The length of the string that we want to copy to the buffer
852 * the length of the digit string;
853 * the remaining space in the buffer, minus 1 for the
856 num_len
= MIN((size_t)(num_end
- num_ptr
), remaining
- 1);
859 * Not enough room to copy anything.
860 * Return the number of bytes we've generated.
866 * Copy over the fractional part.
867 * (We assume here that the fractional part does not contain
868 * so many characters that its size doesn't fit in an int. :-))
870 memcpy(ptr
, num_ptr
, num_len
);
872 num_bytes
+= (int)num_len
;
882 display_epoch_time(char *buf
, size_t buflen
, const nstime_t
*ns
, int precision
)
884 display_signed_time(buf
, buflen
, ns
, precision
);
888 display_signed_time(char *buf
, size_t buflen
, const nstime_t
*ns
, int precision
)
891 /* this buffer is not NUL terminated */
892 int8_t num_buf
[CHARS_64_BIT_SIGNED
];
893 int8_t *num_end
= &num_buf
[CHARS_64_BIT_SIGNED
];
900 /* If the fractional part of the time stamp is negative,
901 print its absolute value and, if the seconds part isn't
902 (the seconds part should be zero in that case), stick
903 a "-" in front of the entire time stamp. */
915 * Fill in num_buf with the seconds value.
917 num_ptr
= int64_to_str_back(num_end
, ns
->secs
);
920 * The length of the string that we want to copy to the buffer
923 * the length of the digit string;
924 * the size of the buffer, minus 1 for the terminating
927 num_len
= MIN((size_t)(num_end
- num_ptr
), buflen
- 1);
930 * Not enough room to copy anything.
936 * Copy over the seconds value.
938 memcpy(buf
, num_ptr
, num_len
);
942 if (precision
== WS_TSPREC_SEC
) {
944 * Seconds precision, so no nanosecond.
945 * Nothing more to do other than to
946 * '\0'-terminate the string.
953 * Append the fractional part.
955 format_fractional_part_nsecs(buf
, buflen
, (uint32_t)nsecs
, ".", precision
);
959 format_nstime_as_iso8601(char *buf
, size_t buflen
, const nstime_t
*ns
,
960 char *decimal_point
, bool local
, int precision
)
968 tmp
= ws_localtime_r(&ns
->secs
, &tm
);
970 tmp
= ws_gmtime_r(&ns
->secs
, &tm
);
972 snprintf(buf
, buflen
, "Not representable");
977 num_bytes
= snprintf(ptr
, remaining
,
978 "%04d-%02d-%02d %02d:%02d:%02d",
988 * Not much else we can do.
990 snprintf(buf
, buflen
, "snprintf() failed");
993 if ((unsigned int)num_bytes
>= remaining
) {
995 * That filled up or would have overflowed the buffer.
996 * Nothing more we can do.
1001 remaining
-= num_bytes
;
1003 if (precision
!= 0) {
1005 * Append the fractional part.
1006 * Get the nsecs as a 32-bit unsigned value, as it should
1007 * never be negative, so we treat it as unsigned.
1009 format_fractional_part_nsecs(ptr
, remaining
, (uint32_t)ns
->nsecs
, decimal_point
, precision
);
1014 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1019 * indent-tabs-mode: t
1022 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1023 * :indentSize=8:tabSize=8:noTabs=false: