/src/wireshark/wsutil/to_str.c
Line | Count | Source |
1 | | /* wsutil/to_str.c |
2 | | * Routines for utilities to convert various other types to strings. |
3 | | * |
4 | | * Wireshark - Network traffic analyzer |
5 | | * By Gerald Combs <gerald@wireshark.org> |
6 | | * Copyright 1998 Gerald Combs |
7 | | * |
8 | | * SPDX-License-Identifier: GPL-2.0-or-later |
9 | | */ |
10 | | |
11 | | #include "config.h" |
12 | | #include "to_str.h" |
13 | | |
14 | | #include <stdio.h> |
15 | | #include <string.h> |
16 | | #include <time.h> |
17 | | |
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> |
23 | | |
24 | | /* |
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 |
28 | | * useful. |
29 | | */ |
30 | | #define _return_if_nospace(str_len, buf, buf_len) \ |
31 | 545k | do { \ |
32 | 545k | if ((str_len) > (buf_len)) { \ |
33 | 0 | (void)g_strlcpy(buf, "[Buffer too small]", buf_len); \ |
34 | 0 | return; \ |
35 | 0 | } \ |
36 | 545k | } while (0) |
37 | | |
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" |
71 | | }; |
72 | | |
73 | | static inline char |
74 | | low_nibble_of_octet_to_hex(uint8_t oct) |
75 | 5.65M | { |
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 notwithstanding, the 17th |
81 | | '\0' byte in the string notwithstanding. */ |
82 | 5.65M | static const char hex_digits[16] = |
83 | 5.65M | { '0', '1', '2', '3', '4', '5', '6', '7', |
84 | 5.65M | '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; |
85 | | |
86 | 5.65M | return hex_digits[oct & 0xF]; |
87 | 5.65M | } |
88 | | |
89 | | static inline char * |
90 | | byte_to_hex(char *out, uint32_t dword) |
91 | 2.05M | { |
92 | 2.05M | *out++ = low_nibble_of_octet_to_hex(dword >> 4); |
93 | 2.05M | *out++ = low_nibble_of_octet_to_hex(dword); |
94 | 2.05M | return out; |
95 | 2.05M | } |
96 | | |
97 | | char * |
98 | | uint8_to_hex(char *out, uint8_t val) |
99 | 0 | { |
100 | 0 | return byte_to_hex(out, val); |
101 | 0 | } |
102 | | |
103 | | char * |
104 | | word_to_hex(char *out, uint16_t word) |
105 | 26.5k | { |
106 | 26.5k | out = byte_to_hex(out, word >> 8); |
107 | 26.5k | out = byte_to_hex(out, word); |
108 | 26.5k | return out; |
109 | 26.5k | } |
110 | | |
111 | | char * |
112 | | word_to_hex_punct(char *out, uint16_t word, char punct) |
113 | 8 | { |
114 | 8 | out = byte_to_hex(out, word >> 8); |
115 | 8 | *out++ = punct; |
116 | 8 | out = byte_to_hex(out, word); |
117 | 8 | return out; |
118 | 8 | } |
119 | | |
120 | | char * |
121 | | word_to_hex_npad(char *out, uint16_t word) |
122 | 0 | { |
123 | 0 | if (word >= 0x1000) |
124 | 0 | *out++ = low_nibble_of_octet_to_hex((uint8_t)(word >> 12)); |
125 | 0 | if (word >= 0x0100) |
126 | 0 | *out++ = low_nibble_of_octet_to_hex((uint8_t)(word >> 8)); |
127 | 0 | if (word >= 0x0010) |
128 | 0 | *out++ = low_nibble_of_octet_to_hex((uint8_t)(word >> 4)); |
129 | 0 | *out++ = low_nibble_of_octet_to_hex((uint8_t)(word >> 0)); |
130 | 0 | return out; |
131 | 0 | } |
132 | | |
133 | | char * |
134 | | dword_to_hex(char *out, uint32_t dword) |
135 | 1.33k | { |
136 | 1.33k | out = word_to_hex(out, dword >> 16); |
137 | 1.33k | out = word_to_hex(out, dword); |
138 | 1.33k | return out; |
139 | 1.33k | } |
140 | | |
141 | | char * |
142 | | dword_to_hex_punct(char *out, uint32_t dword, char punct) |
143 | 4 | { |
144 | 4 | out = word_to_hex_punct(out, dword >> 16, punct); |
145 | 4 | *out++ = punct; |
146 | 4 | out = word_to_hex_punct(out, dword, punct); |
147 | 4 | return out; |
148 | 4 | } |
149 | | |
150 | | char * |
151 | | qword_to_hex(char *out, uint64_t qword) |
152 | 0 | { |
153 | 0 | out = dword_to_hex(out, (uint32_t)(qword >> 32)); |
154 | 0 | out = dword_to_hex(out, (uint32_t)(qword & 0xffffffff)); |
155 | 0 | return out; |
156 | 0 | } |
157 | | |
158 | | char * |
159 | | qword_to_hex_punct(char *out, uint64_t qword, char punct) |
160 | 0 | { |
161 | 0 | out = dword_to_hex_punct(out, (uint32_t)(qword >> 32), punct); |
162 | 0 | *out++ = punct; |
163 | 0 | out = dword_to_hex_punct(out, (uint32_t)(qword & 0xffffffff), punct); |
164 | 0 | return out; |
165 | 0 | } |
166 | | |
167 | | /* |
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. |
172 | | * |
173 | | * There needs to be at least len * 2 bytes left in the buffer. |
174 | | */ |
175 | | char * |
176 | | bytes_to_hexstr(char *out, const uint8_t *ad, size_t len) |
177 | 314k | { |
178 | 314k | size_t i; |
179 | | |
180 | 314k | ws_return_val_if(!ad, NULL); |
181 | | |
182 | 1.85M | for (i = 0; i < len; i++) |
183 | 1.54M | out = byte_to_hex(out, ad[i]); |
184 | 314k | return out; |
185 | 314k | } |
186 | | |
187 | | /* |
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. |
192 | | * |
193 | | * There needs to be at least len * 3 - 1 bytes left in the buffer. |
194 | | */ |
195 | | char * |
196 | | bytes_to_hexstr_punct(char *out, const uint8_t *ad, size_t len, char punct) |
197 | 83.5k | { |
198 | 83.5k | size_t i; |
199 | | |
200 | 83.5k | ws_return_val_if(!ad, NULL); |
201 | | |
202 | 83.5k | out = byte_to_hex(out, ad[0]); |
203 | 461k | for (i = 1; i < len; i++) { |
204 | 378k | *out++ = punct; |
205 | 378k | out = byte_to_hex(out, ad[i]); |
206 | 378k | } |
207 | 83.5k | return out; |
208 | 83.5k | } |
209 | | |
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 |
212 | | * the bytes. |
213 | | * |
214 | | * If punct is '\0', no punctuation is applied (and thus |
215 | | * the resulting string is (len-1) bytes shorter) |
216 | | */ |
217 | | char * |
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) |
221 | 8.33k | { |
222 | 8.33k | char *buf; |
223 | 8.33k | size_t max_char_size; |
224 | 8.33k | char *buf_ptr; |
225 | 8.33k | int truncated = 0; |
226 | | |
227 | | /* src is an array of bytes, not necessarily null-terminated, |
228 | | * so check for a zero length first and allow it even with a |
229 | | * NULL src. */ |
230 | 8.33k | if (!src_size) { |
231 | 0 | return wmem_strdup(scope, ""); |
232 | 0 | } |
233 | | |
234 | 8.33k | ws_return_str_if(!src, scope); |
235 | | |
236 | 8.33k | if (!punct) |
237 | 0 | return bytes_to_str_maxlen(scope, src, src_size, max_bytes_len); |
238 | | |
239 | 8.33k | if (max_bytes_len == 0 || max_bytes_len > src_size) { |
240 | 8.18k | max_bytes_len = src_size; |
241 | 8.18k | } |
242 | 147 | else if (max_bytes_len < src_size) { |
243 | 136 | truncated = 1; |
244 | 136 | } |
245 | | |
246 | | /* Include space for ellipsis and '\0'. Optional extra punct |
247 | | * at the end is already accounted for. */ |
248 | 8.33k | max_char_size = max_bytes_len * 3 + strlen(UTF8_HORIZONTAL_ELLIPSIS) + 1; |
249 | | |
250 | 8.33k | buf = wmem_alloc(scope, max_char_size); |
251 | 8.33k | buf_ptr = bytes_to_hexstr_punct(buf, src, max_bytes_len, punct); |
252 | | |
253 | 8.33k | if (truncated) { |
254 | 136 | *buf_ptr++ = punct; |
255 | 136 | buf_ptr = g_stpcpy(buf_ptr, UTF8_HORIZONTAL_ELLIPSIS); |
256 | 136 | } |
257 | | |
258 | 8.33k | *buf_ptr = '\0'; |
259 | 8.33k | return buf; |
260 | 8.33k | } |
261 | | |
262 | | char * |
263 | | bytes_to_str_maxlen(wmem_allocator_t *scope, |
264 | | const uint8_t *src, size_t src_size, |
265 | | size_t max_bytes_len) |
266 | 291k | { |
267 | 291k | char *buf; |
268 | 291k | size_t max_char_size; |
269 | 291k | char *buf_ptr; |
270 | 291k | int truncated = 0; |
271 | | |
272 | | /* src is an array of bytes, not necessarily null-terminated, |
273 | | * so check for a zero length first and allow it even with a |
274 | | * NULL src. */ |
275 | 291k | if (!src_size) { |
276 | 165 | return wmem_strdup(scope, ""); |
277 | 165 | } |
278 | | |
279 | 290k | ws_return_str_if(!src, scope); |
280 | | |
281 | 290k | if (max_bytes_len == 0 || max_bytes_len > src_size) { |
282 | 288k | max_bytes_len = src_size; |
283 | 288k | } |
284 | 2.37k | else if (max_bytes_len < src_size) { |
285 | 2.19k | truncated = 1; |
286 | 2.19k | } |
287 | | |
288 | 290k | max_char_size = max_bytes_len * 2 + strlen(UTF8_HORIZONTAL_ELLIPSIS) + 1; |
289 | | |
290 | 290k | buf = wmem_alloc(scope, max_char_size); |
291 | 290k | buf_ptr = bytes_to_hexstr(buf, src, max_bytes_len); |
292 | | |
293 | 290k | if (truncated) |
294 | 2.19k | buf_ptr = g_stpcpy(buf_ptr, UTF8_HORIZONTAL_ELLIPSIS); |
295 | | |
296 | 290k | *buf_ptr = '\0'; |
297 | 290k | return buf; |
298 | 290k | } |
299 | | |
300 | | /* |
301 | | * The *_to_str_back() functions measured approx. a x7.5 speed-up versus |
302 | | * snprintf() on my Linux system with GNU libc. |
303 | | */ |
304 | | |
305 | | char * |
306 | | oct_to_str_back(char *ptr, uint32_t value) |
307 | 4 | { |
308 | 26 | while (value) { |
309 | 22 | *(--ptr) = '0' + (value & 0x7); |
310 | 22 | value >>= 3; |
311 | 22 | } |
312 | | |
313 | 4 | *(--ptr) = '0'; |
314 | 4 | return ptr; |
315 | 4 | } |
316 | | |
317 | | char * |
318 | | oct64_to_str_back(char *ptr, uint64_t value) |
319 | 0 | { |
320 | 0 | while (value) { |
321 | 0 | *(--ptr) = '0' + (value & 0x7); |
322 | 0 | value >>= 3; |
323 | 0 | } |
324 | |
|
325 | 0 | *(--ptr) = '0'; |
326 | 0 | return ptr; |
327 | 0 | } |
328 | | |
329 | | char * |
330 | | hex_to_str_back_len(char *ptr, uint32_t value, int len) |
331 | 608k | { |
332 | 1.42M | do { |
333 | 1.42M | *(--ptr) = low_nibble_of_octet_to_hex(value); |
334 | 1.42M | value >>= 4; |
335 | 1.42M | len--; |
336 | 1.42M | } while (value); |
337 | | |
338 | | /* pad */ |
339 | 970k | while (len > 0) { |
340 | 361k | *(--ptr) = '0'; |
341 | 361k | len--; |
342 | 361k | } |
343 | | |
344 | 608k | *(--ptr) = 'x'; |
345 | 608k | *(--ptr) = '0'; |
346 | | |
347 | 608k | return ptr; |
348 | 608k | } |
349 | | |
350 | | char * |
351 | | hex64_to_str_back_len(char *ptr, uint64_t value, int len) |
352 | 14.1k | { |
353 | 117k | do { |
354 | 117k | *(--ptr) = low_nibble_of_octet_to_hex(value & 0xF); |
355 | 117k | value >>= 4; |
356 | 117k | len--; |
357 | 117k | } while (value); |
358 | | |
359 | | /* pad */ |
360 | 40.5k | while (len > 0) { |
361 | 26.3k | *(--ptr) = '0'; |
362 | 26.3k | len--; |
363 | 26.3k | } |
364 | | |
365 | 14.1k | *(--ptr) = 'x'; |
366 | 14.1k | *(--ptr) = '0'; |
367 | | |
368 | 14.1k | return ptr; |
369 | 14.1k | } |
370 | | |
371 | | char * |
372 | | uint_to_str_back(char *ptr, uint32_t value) |
373 | 1.86M | { |
374 | 1.86M | char const *p; |
375 | | |
376 | | /* special case */ |
377 | 1.86M | if (value == 0) |
378 | 624k | *(--ptr) = '0'; |
379 | | |
380 | 2.52M | while (value >= 10) { |
381 | 655k | p = fast_strings[100 + (value % 100)]; |
382 | | |
383 | 655k | value /= 100; |
384 | | |
385 | 655k | *(--ptr) = p[2]; |
386 | 655k | *(--ptr) = p[1]; |
387 | 655k | } |
388 | | |
389 | 1.86M | if (value) |
390 | 979k | *(--ptr) = (value) | '0'; |
391 | | |
392 | 1.86M | return ptr; |
393 | 1.86M | } |
394 | | |
395 | | char * |
396 | | uint64_to_str_back(char *ptr, uint64_t value) |
397 | 260 | { |
398 | 260 | char const *p; |
399 | | |
400 | | /* special case */ |
401 | 260 | if (value == 0) |
402 | 12 | *(--ptr) = '0'; |
403 | | |
404 | 1.30k | while (value >= 10) { |
405 | 1.04k | p = fast_strings[100 + (value % 100)]; |
406 | | |
407 | 1.04k | value /= 100; |
408 | | |
409 | 1.04k | *(--ptr) = p[2]; |
410 | 1.04k | *(--ptr) = p[1]; |
411 | 1.04k | } |
412 | | |
413 | | /* value will be 0..9, so using '& 0xF' is safe, and faster than '% 10' */ |
414 | 260 | if (value) |
415 | 167 | *(--ptr) = (value & 0xF) | '0'; |
416 | | |
417 | 260 | return ptr; |
418 | 260 | } |
419 | | |
420 | | char * |
421 | | uint_to_str_back_len(char *ptr, uint32_t value, int len) |
422 | 16 | { |
423 | 16 | char *new_ptr; |
424 | | |
425 | 16 | new_ptr = uint_to_str_back(ptr, value); |
426 | | |
427 | | /* subtract from len number of generated characters */ |
428 | 16 | len -= (int)(ptr - new_ptr); |
429 | | |
430 | | /* pad remaining with '0' */ |
431 | 42 | while (len > 0) |
432 | 26 | { |
433 | 26 | *(--new_ptr) = '0'; |
434 | 26 | len--; |
435 | 26 | } |
436 | | |
437 | 16 | return new_ptr; |
438 | 16 | } |
439 | | |
440 | | char * |
441 | | uint64_to_str_back_len(char *ptr, uint64_t value, int len) |
442 | 0 | { |
443 | 0 | char *new_ptr; |
444 | |
|
445 | 0 | new_ptr = uint64_to_str_back(ptr, value); |
446 | | |
447 | | /* subtract from len number of generated characters */ |
448 | 0 | len -= (int)(ptr - new_ptr); |
449 | | |
450 | | /* pad remaining with '0' */ |
451 | 0 | while (len > 0) |
452 | 0 | { |
453 | 0 | *(--new_ptr) = '0'; |
454 | 0 | len--; |
455 | 0 | } |
456 | |
|
457 | 0 | return new_ptr; |
458 | 0 | } |
459 | | |
460 | | char * |
461 | | int_to_str_back(char *ptr, int32_t value) |
462 | 1.24k | { |
463 | 1.24k | if (value < 0) { |
464 | 395 | ptr = uint_to_str_back(ptr, -value); |
465 | 395 | *(--ptr) = '-'; |
466 | 395 | } else |
467 | 850 | ptr = uint_to_str_back(ptr, value); |
468 | | |
469 | 1.24k | return ptr; |
470 | 1.24k | } |
471 | | |
472 | | char * |
473 | | int64_to_str_back(char *ptr, int64_t value) |
474 | 75 | { |
475 | 75 | if (value < 0) { |
476 | 59 | ptr = uint64_to_str_back(ptr, -value); |
477 | 59 | *(--ptr) = '-'; |
478 | 59 | } else |
479 | 16 | ptr = uint64_to_str_back(ptr, value); |
480 | | |
481 | 75 | return ptr; |
482 | 75 | } |
483 | | |
484 | | static size_t |
485 | | uint32_to_str_buf_len(const uint32_t u) |
486 | 107k | { |
487 | | /* ((2^32)-1) == 2147483647 */ |
488 | 107k | if (u >= 1000000000)return 10; |
489 | 107k | if (u >= 100000000) return 9; |
490 | 107k | if (u >= 10000000) return 8; |
491 | 107k | if (u >= 1000000) return 7; |
492 | 107k | if (u >= 100000) return 6; |
493 | 107k | if (u >= 10000) return 5; |
494 | 77.1k | if (u >= 1000) return 4; |
495 | 44.1k | if (u >= 100) return 3; |
496 | 15.2k | if (u >= 10) return 2; |
497 | | |
498 | 9.57k | return 1; |
499 | 15.2k | } |
500 | | |
501 | | void |
502 | | uint32_to_str_buf(uint32_t u, char *buf, size_t buf_len) |
503 | 107k | { |
504 | 107k | size_t str_len = uint32_to_str_buf_len(u)+1; |
505 | | |
506 | 107k | char *bp = &buf[str_len]; |
507 | | |
508 | 107k | _return_if_nospace(str_len, buf, buf_len); |
509 | | |
510 | 107k | *--bp = '\0'; |
511 | | |
512 | 107k | uint_to_str_back(bp, u); |
513 | 107k | } |
514 | | |
515 | | static size_t |
516 | | uint64_to_str_buf_len(const uint64_t u) |
517 | 7 | { |
518 | | /* ((2^64)-1) == 18446744073709551615 */ |
519 | | |
520 | 7 | if (u >= UINT64_C(10000000000000000000)) return 20; |
521 | 5 | if (u >= UINT64_C(1000000000000000000)) return 19; |
522 | 3 | if (u >= UINT64_C(100000000000000000)) return 18; |
523 | 3 | if (u >= UINT64_C(10000000000000000)) return 17; |
524 | 3 | if (u >= UINT64_C(1000000000000000)) return 16; |
525 | 3 | if (u >= UINT64_C(100000000000000)) return 15; |
526 | 3 | if (u >= UINT64_C(10000000000000)) return 14; |
527 | 3 | if (u >= UINT64_C(1000000000000)) return 13; |
528 | 3 | if (u >= UINT64_C(100000000000)) return 12; |
529 | 3 | if (u >= UINT64_C(10000000000)) return 11; |
530 | 3 | if (u >= UINT64_C(1000000000)) return 10; |
531 | 2 | if (u >= UINT64_C(100000000)) return 9; |
532 | 2 | if (u >= UINT64_C(10000000)) return 8; |
533 | 1 | if (u >= UINT64_C(1000000)) return 7; |
534 | 1 | if (u >= UINT64_C(100000)) return 6; |
535 | 1 | if (u >= UINT64_C(10000)) return 5; |
536 | 1 | if (u >= UINT64_C(1000)) return 4; |
537 | 1 | if (u >= UINT64_C(100)) return 3; |
538 | 0 | if (u >= UINT64_C(10)) return 2; |
539 | | |
540 | 0 | return 1; |
541 | 0 | } |
542 | | |
543 | | void |
544 | | uint64_to_str_buf(uint64_t u, char *buf, size_t buf_len) |
545 | 7 | { |
546 | 7 | size_t str_len = uint64_to_str_buf_len(u)+1; |
547 | | |
548 | 7 | char *bp = &buf[str_len]; |
549 | | |
550 | 7 | _return_if_nospace(str_len, buf, buf_len); |
551 | | |
552 | 7 | *--bp = '\0'; |
553 | | |
554 | 7 | uint64_to_str_back(bp, u); |
555 | 7 | } |
556 | | |
557 | | /* |
558 | | This function is very fast and this function is called a lot. |
559 | | XXX update the address_to_str stuff to use this function. |
560 | | */ |
561 | | void |
562 | | ip_addr_to_str_buf(const ws_in4_addr *_ad, char *buf, const int buf_len) |
563 | 438k | { |
564 | 438k | uint8_t *ad = (uint8_t *)_ad; |
565 | 438k | register char const *p; |
566 | 438k | register char *b=buf; |
567 | | |
568 | 438k | _return_if_nospace(WS_INET_ADDRSTRLEN, buf, buf_len); |
569 | | |
570 | 438k | p=fast_strings[*ad++]; |
571 | 691k | do { |
572 | 691k | *b++=*p; |
573 | 691k | p++; |
574 | 691k | } while(*p); |
575 | 438k | *b++='.'; |
576 | | |
577 | 438k | p=fast_strings[*ad++]; |
578 | 689k | do { |
579 | 689k | *b++=*p; |
580 | 689k | p++; |
581 | 689k | } while(*p); |
582 | 438k | *b++='.'; |
583 | | |
584 | 438k | p=fast_strings[*ad++]; |
585 | 680k | do { |
586 | 680k | *b++=*p; |
587 | 680k | p++; |
588 | 680k | } while(*p); |
589 | 438k | *b++='.'; |
590 | | |
591 | 438k | p=fast_strings[*ad]; |
592 | 701k | do { |
593 | 701k | *b++=*p; |
594 | 701k | p++; |
595 | 701k | } while(*p); |
596 | 438k | *b=0; |
597 | 438k | } |
598 | | |
599 | | char * |
600 | | ip_addr_to_str(wmem_allocator_t *scope, const ws_in4_addr *ad) |
601 | 110k | { |
602 | 110k | char *buf = wmem_alloc(scope, WS_INET_ADDRSTRLEN * sizeof(char)); |
603 | | |
604 | 110k | ip_addr_to_str_buf(ad, buf, WS_INET_ADDRSTRLEN); |
605 | | |
606 | 110k | return buf; |
607 | 110k | } |
608 | | |
609 | | void |
610 | | ip_num_to_str_buf(uint32_t ad, char *buf, const int buf_len) |
611 | 0 | { |
612 | 0 | ws_in4_addr addr = g_htonl(ad); |
613 | 0 | ip_addr_to_str_buf(&addr, buf, buf_len); |
614 | 0 | } |
615 | | |
616 | | /* Host byte order */ |
617 | | char * |
618 | | ip_num_to_str(wmem_allocator_t *scope, uint32_t ad) |
619 | 0 | { |
620 | 0 | ws_in4_addr addr = g_htonl(ad); |
621 | 0 | return ip_addr_to_str(scope, &addr); |
622 | 0 | } |
623 | | |
624 | | void |
625 | | ip6_to_str_buf(const ws_in6_addr *addr, char *buf, size_t buf_size) |
626 | 124k | { |
627 | | /* |
628 | | * If there is not enough space then ws_inet_ntop6() will leave |
629 | | * an error message in the buffer, we don't need |
630 | | * to use _return_if_nospace(). |
631 | | */ |
632 | 124k | ws_inet_ntop6(addr, buf, (unsigned)buf_size); |
633 | 124k | } |
634 | | |
635 | | char *ip6_to_str(wmem_allocator_t *scope, const ws_in6_addr *ad) |
636 | 39.7k | { |
637 | 39.7k | char *buf = wmem_alloc(scope, WS_INET6_ADDRSTRLEN * sizeof(char)); |
638 | | |
639 | 39.7k | ws_inet_ntop6(ad, buf, WS_INET6_ADDRSTRLEN); |
640 | | |
641 | 39.7k | return buf; |
642 | 39.7k | } |
643 | | |
644 | | char * |
645 | | ipxnet_to_str_punct(wmem_allocator_t *allocator, const uint32_t ad, const char punct) |
646 | 4 | { |
647 | 4 | char *buf = (char *)wmem_alloc(allocator, 12); |
648 | | |
649 | 4 | *dword_to_hex_punct(buf, ad, punct) = '\0'; |
650 | 4 | return buf; |
651 | 4 | } |
652 | | |
653 | 0 | #define WS_EUI64_STRLEN 24 |
654 | | |
655 | | char * |
656 | 0 | eui64_to_str(wmem_allocator_t *scope, const uint64_t ad) { |
657 | 0 | char *buf, *tmp; |
658 | 0 | uint8_t *p_eui64; |
659 | |
|
660 | 0 | p_eui64=(uint8_t *)wmem_alloc(NULL, 8); |
661 | 0 | buf=(char *)wmem_alloc(scope, WS_EUI64_STRLEN); |
662 | | |
663 | | /* Copy and convert the address to network byte order. */ |
664 | 0 | *(uint64_t *)(void *)(p_eui64) = pntohu64(&(ad)); |
665 | |
|
666 | 0 | tmp = bytes_to_hexstr_punct(buf, p_eui64, 8, ':'); |
667 | 0 | *tmp = '\0'; /* NULL terminate */ |
668 | 0 | wmem_free(NULL, p_eui64); |
669 | 0 | return buf; |
670 | 0 | } |
671 | | |
672 | | /* |
673 | | * Number of characters required by a 64-bit signed number. |
674 | | */ |
675 | 16 | #define CHARS_64_BIT_SIGNED 20 /* sign plus 19 digits */ |
676 | | |
677 | | /* |
678 | | * Number of characters required by a fractional part, in nanoseconds, |
679 | | * not counting the decimal point. |
680 | | */ |
681 | 16 | #define CHARS_NANOSECONDS 9 /* 000000001 */ |
682 | | |
683 | | /* |
684 | | * Format the fractional part of a time, with the specified precision. |
685 | | * Returns the number of bytes formatted. |
686 | | */ |
687 | | int |
688 | | format_fractional_part_nsecs(char *buf, size_t buflen, uint32_t nsecs, const char *decimal_point, int precision) |
689 | 16 | { |
690 | 16 | char *ptr; |
691 | 16 | size_t remaining; |
692 | 16 | int num_bytes; |
693 | 16 | size_t decimal_point_len; |
694 | 16 | uint32_t frac_part; |
695 | 16 | char num_buf[CHARS_NANOSECONDS]; |
696 | 16 | char *num_end = &num_buf[CHARS_NANOSECONDS]; |
697 | 16 | char *num_ptr; |
698 | 16 | size_t num_len; |
699 | | |
700 | 16 | ws_assert(precision != WS_TSPREC_SEC); |
701 | | |
702 | 16 | if (buflen == 0) { |
703 | | /* |
704 | | * No room in the buffer for anything, including |
705 | | * a terminating '\0'. |
706 | | */ |
707 | 0 | return 0; |
708 | 0 | } |
709 | | |
710 | | /* |
711 | | * If the fractional part is >= 1, don't show it as a |
712 | | * fractional part. |
713 | | */ |
714 | 16 | if (nsecs >= 1000000000U) { |
715 | 0 | num_bytes = snprintf(buf, buflen, "%s(%u nanoseconds)", |
716 | 0 | decimal_point, nsecs); |
717 | 0 | if ((unsigned int)num_bytes >= buflen) { |
718 | | /* |
719 | | * That filled up or would have overflowed |
720 | | * the buffer. Nothing more to do; return |
721 | | * the remaining space in the buffer, minus |
722 | | * one byte for the terminating '\0',* as |
723 | | * that's the number of bytes we copied. |
724 | | */ |
725 | 0 | return (int)(buflen - 1); |
726 | 0 | } |
727 | 0 | return num_bytes; |
728 | 0 | } |
729 | | |
730 | 16 | ptr = buf; |
731 | 16 | remaining = buflen; |
732 | 16 | num_bytes = 0; |
733 | | |
734 | | /* |
735 | | * Copy the decimal point. |
736 | | * (We assume here that the locale's decimal point does |
737 | | * not contain so many characters that its size doesn't |
738 | | * fit in an int. :-)) |
739 | | */ |
740 | 16 | decimal_point_len = g_strlcpy(buf, decimal_point, buflen); |
741 | 16 | if (decimal_point_len >= buflen) { |
742 | | /* |
743 | | * The decimal point didn't fit in the buffer |
744 | | * and was truncated. Nothing more to do; |
745 | | * return the remaining space in the buffer, |
746 | | * minus one byte for the terminating '\0', |
747 | | * as that's the number of bytes we copied. |
748 | | */ |
749 | 0 | return (int)(buflen - 1); |
750 | 0 | } |
751 | 16 | ptr += decimal_point_len; |
752 | 16 | remaining -= decimal_point_len; |
753 | 16 | num_bytes += (int)decimal_point_len; |
754 | | |
755 | | /* |
756 | | * Fill in num_buf with the nanoseconds value, padded with |
757 | | * leading zeroes, to the specified precision. |
758 | | * |
759 | | * We scale the fractional part in advance, as that just |
760 | | * takes one division by a constant (which may be |
761 | | * optimized to a faster multiplication by a constant) |
762 | | * and gets rid of some divisions and remainders by 100 |
763 | | * done to generate the digits. |
764 | | * |
765 | | * We pass precision as the last argument to |
766 | | * uint_to_str_back_len(), as that might mean that |
767 | | * all of the cases end up using common code to |
768 | | * do part of the call to uint_to_str_back_len(). |
769 | | */ |
770 | 16 | switch (precision) { |
771 | | |
772 | 0 | case WS_TSPREC_100_MSEC: |
773 | | /* |
774 | | * Scale down to units of 1/10 second. |
775 | | */ |
776 | 0 | frac_part = nsecs / 100000000U; |
777 | 0 | break; |
778 | | |
779 | 0 | case WS_TSPREC_10_MSEC: |
780 | | /* |
781 | | * Scale down to units of 1/100 second. |
782 | | */ |
783 | 0 | frac_part = nsecs / 10000000U; |
784 | 0 | break; |
785 | | |
786 | 0 | case WS_TSPREC_MSEC: |
787 | | /* |
788 | | * Scale down to units of 1/1000 second. |
789 | | */ |
790 | 0 | frac_part = nsecs / 1000000U; |
791 | 0 | break; |
792 | | |
793 | 0 | case WS_TSPREC_100_USEC: |
794 | | /* |
795 | | * Scale down to units of 1/10000 second. |
796 | | */ |
797 | 0 | frac_part = nsecs / 100000U; |
798 | 0 | break; |
799 | | |
800 | 0 | case WS_TSPREC_10_USEC: |
801 | | /* |
802 | | * Scale down to units of 1/100000 second. |
803 | | */ |
804 | 0 | frac_part = nsecs / 10000U; |
805 | 0 | break; |
806 | | |
807 | 0 | case WS_TSPREC_USEC: |
808 | | /* |
809 | | * Scale down to units of 1/1000000 second. |
810 | | */ |
811 | 0 | frac_part = nsecs / 1000U; |
812 | 0 | break; |
813 | | |
814 | 0 | case WS_TSPREC_100_NSEC: |
815 | | /* |
816 | | * Scale down to units of 1/10000000 second. |
817 | | */ |
818 | 0 | frac_part = nsecs / 100U; |
819 | 0 | break; |
820 | | |
821 | 0 | case WS_TSPREC_10_NSEC: |
822 | | /* |
823 | | * Scale down to units of 1/100000000 second. |
824 | | */ |
825 | 0 | frac_part = nsecs / 10U; |
826 | 0 | break; |
827 | | |
828 | 16 | case WS_TSPREC_NSEC: |
829 | | /* |
830 | | * We're already in units of 1/1000000000 second. |
831 | | */ |
832 | 16 | frac_part = nsecs; |
833 | 16 | break; |
834 | | |
835 | 0 | default: |
836 | 0 | ws_assert_not_reached(); |
837 | 0 | break; |
838 | 16 | } |
839 | | |
840 | 16 | num_ptr = uint_to_str_back_len(num_end, frac_part, precision); |
841 | | |
842 | | /* |
843 | | * The length of the string that we want to copy to the buffer |
844 | | * is the minimum of: |
845 | | * |
846 | | * the length of the digit string; |
847 | | * the remaining space in the buffer, minus 1 for the |
848 | | * terminating '\0'. |
849 | | */ |
850 | 16 | num_len = MIN((size_t)(num_end - num_ptr), remaining - 1); |
851 | 16 | if (num_len == 0) { |
852 | | /* |
853 | | * Not enough room to copy anything. |
854 | | * Return the number of bytes we've generated. |
855 | | */ |
856 | 0 | return num_bytes; |
857 | 0 | } |
858 | | |
859 | | /* |
860 | | * Copy over the fractional part. |
861 | | * (We assume here that the fractional part does not contain |
862 | | * so many characters that its size doesn't fit in an int. :-)) |
863 | | */ |
864 | 16 | memcpy(ptr, num_ptr, num_len); |
865 | 16 | ptr += num_len; |
866 | 16 | num_bytes += (int)num_len; |
867 | | |
868 | | /* |
869 | | * '\0'-terminate it. |
870 | | */ |
871 | 16 | *ptr = '\0'; |
872 | 16 | return num_bytes; |
873 | 16 | } |
874 | | |
875 | | void |
876 | | display_epoch_time(char *buf, size_t buflen, const nstime_t *ns, int precision) |
877 | 0 | { |
878 | 0 | display_signed_time(buf, buflen, ns, precision); |
879 | 0 | } |
880 | | |
881 | | void |
882 | | display_signed_time(char *buf, size_t buflen, const nstime_t *ns, int precision) |
883 | 16 | { |
884 | 16 | int nsecs; |
885 | | /* this buffer is not NUL terminated */ |
886 | 16 | char num_buf[CHARS_64_BIT_SIGNED]; |
887 | 16 | char *num_end = &num_buf[CHARS_64_BIT_SIGNED]; |
888 | 16 | char *num_ptr; |
889 | 16 | size_t num_len; |
890 | | |
891 | 16 | if (buflen < 1) |
892 | 0 | return; |
893 | | |
894 | | /* If the fractional part of the time stamp is negative, |
895 | | print its absolute value and, if the seconds part isn't |
896 | | (the seconds part should be zero in that case), stick |
897 | | a "-" in front of the entire time stamp. */ |
898 | 16 | nsecs = ns->nsecs; |
899 | 16 | if (nsecs < 0) { |
900 | 0 | nsecs = -nsecs; |
901 | 0 | if (ns->secs >= 0) { |
902 | 0 | buf[0] = '-'; |
903 | 0 | buf++; |
904 | 0 | buflen--; |
905 | 0 | } |
906 | 0 | } |
907 | | |
908 | | /* |
909 | | * Fill in num_buf with the seconds value. |
910 | | */ |
911 | 16 | num_ptr = int64_to_str_back(num_end, ns->secs); |
912 | | |
913 | | /* |
914 | | * The length of the string that we want to copy to the buffer |
915 | | * is the minimum of: |
916 | | * |
917 | | * the length of the digit string; |
918 | | * the size of the buffer, minus 1 for the terminating |
919 | | * '\0'. |
920 | | */ |
921 | 16 | num_len = MIN((size_t)(num_end - num_ptr), buflen - 1); |
922 | 16 | if (num_len == 0) { |
923 | | /* |
924 | | * Not enough room to copy anything. |
925 | | */ |
926 | 0 | return; |
927 | 0 | } |
928 | | |
929 | | /* |
930 | | * Copy over the seconds value. |
931 | | */ |
932 | 16 | memcpy(buf, num_ptr, num_len); |
933 | 16 | buf += num_len; |
934 | 16 | buflen -= num_len; |
935 | | |
936 | 16 | if (precision == WS_TSPREC_SEC) { |
937 | | /* |
938 | | * Seconds precision, so no nanosecond. |
939 | | * Nothing more to do other than to |
940 | | * '\0'-terminate the string. |
941 | | */ |
942 | 0 | *buf = '\0'; |
943 | 0 | return; |
944 | 0 | } |
945 | | |
946 | | /* |
947 | | * Append the fractional part. |
948 | | */ |
949 | 16 | format_fractional_part_nsecs(buf, buflen, (uint32_t)nsecs, ".", precision); |
950 | 16 | } |
951 | | |
952 | | void |
953 | | format_nstime_as_iso8601(char *buf, size_t buflen, const nstime_t *ns, |
954 | | char *decimal_point, bool local, int precision) |
955 | 0 | { |
956 | 0 | struct tm tm, *tmp; |
957 | 0 | char *ptr; |
958 | 0 | size_t remaining; |
959 | 0 | int num_bytes; |
960 | |
|
961 | 0 | if (local) |
962 | 0 | tmp = ws_localtime_r(&ns->secs, &tm); |
963 | 0 | else |
964 | 0 | tmp = ws_gmtime_r(&ns->secs, &tm); |
965 | 0 | if (tmp == NULL) { |
966 | 0 | snprintf(buf, buflen, "Not representable"); |
967 | 0 | return; |
968 | 0 | } |
969 | 0 | ptr = buf; |
970 | 0 | remaining = buflen; |
971 | 0 | num_bytes = snprintf(ptr, remaining, |
972 | 0 | "%04d-%02d-%02d %02d:%02d:%02d", |
973 | 0 | tmp->tm_year + 1900, |
974 | 0 | tmp->tm_mon + 1, |
975 | 0 | tmp->tm_mday, |
976 | 0 | tmp->tm_hour, |
977 | 0 | tmp->tm_min, |
978 | 0 | tmp->tm_sec); |
979 | 0 | if (num_bytes < 0) { |
980 | | /* |
981 | | * That got an error. |
982 | | * Not much else we can do. |
983 | | */ |
984 | 0 | snprintf(buf, buflen, "snprintf() failed"); |
985 | 0 | return; |
986 | 0 | } |
987 | 0 | if ((unsigned int)num_bytes >= remaining) { |
988 | | /* |
989 | | * That filled up or would have overflowed the buffer. |
990 | | * Nothing more we can do. |
991 | | */ |
992 | 0 | return; |
993 | 0 | } |
994 | 0 | ptr += num_bytes; |
995 | 0 | remaining -= num_bytes; |
996 | |
|
997 | 0 | num_bytes = 0; |
998 | 0 | if (precision != 0) { |
999 | | /* |
1000 | | * Append the fractional part. |
1001 | | * Get the nsecs as a 32-bit unsigned value, as it should |
1002 | | * never be negative, so we treat it as unsigned. |
1003 | | */ |
1004 | 0 | num_bytes = format_fractional_part_nsecs(ptr, remaining, (uint32_t)ns->nsecs, decimal_point, precision); |
1005 | 0 | } |
1006 | |
|
1007 | 0 | if (!local) { |
1008 | | /* |
1009 | | * format_fractional_part_nsecs, unlike snprintf, returns the |
1010 | | * number of bytes copied (not "would have copied"), so we |
1011 | | * don't check for overflow here. |
1012 | | */ |
1013 | 0 | ptr += num_bytes; |
1014 | 0 | remaining -= num_bytes; |
1015 | |
|
1016 | 0 | if (remaining == 1 && num_bytes > 0) { |
1017 | | /* |
1018 | | * If we copied a fractional part but there's only room |
1019 | | * for the terminating '\0', replace the last digit of |
1020 | | * the fractional part with the "Z". (Remaining is at |
1021 | | * least 1, otherwise we would have returned above.) |
1022 | | */ |
1023 | 0 | ptr--; |
1024 | 0 | remaining++; |
1025 | 0 | } |
1026 | 0 | (void)g_strlcpy(ptr, "Z", remaining); |
1027 | 0 | } |
1028 | 0 | } |
1029 | | |
1030 | | /* |
1031 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
1032 | | * |
1033 | | * Local variables: |
1034 | | * c-basic-offset: 8 |
1035 | | * tab-width: 8 |
1036 | | * indent-tabs-mode: t |
1037 | | * End: |
1038 | | * |
1039 | | * vi: set shiftwidth=8 tabstop=8 noexpandtab: |
1040 | | * :indentSize=8:tabSize=8:noTabs=false: |
1041 | | */ |