Coverage Report

Created: 2026-09-28 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
 */