dte test coverage


Directory: ./
File: src/util/numtostr.c
Date: 2025-05-08 15:05:54
Exec Total Coverage
Lines: 99 99 100.0%
Functions: 11 11 100.0%
Branches: 36 36 100.0%

Line Branch Exec Source
1 // Locale-independent integer to string conversion.
2 // Copyright © Craig Barnes.
3 // SPDX-License-Identifier: GPL-2.0-only
4
5 #include <string.h>
6 #include <sys/stat.h>
7 #include "numtostr.h"
8 #include "arith.h"
9 #include "bit.h"
10 #include "debug.h"
11
12 const char hextab_lower[16] = "0123456789abcdef";
13 const char hextab_upper[16] = "0123456789ABCDEF";
14
15 1078 static size_t umax_count_base10_digits(uintmax_t x)
16 {
17 1078 size_t digits = 0;
18 2746 do {
19 2746 x /= 10;
20 2746 digits++;
21
2/2
✓ Branch 0 (3→4) taken 1668 times.
✓ Branch 1 (3→5) taken 1078 times.
2746 } while (x);
22 1078 return digits;
23 }
24
25 // Writes the decimal string representation of `x` into `buf`,
26 // which must have enough space available for a known/constant
27 // value of `x` or `DECIMAL_STR_MAX(x)` bytes for arbitrary values.
28 // Returns the number of bytes (digits) written.
29 1078 size_t buf_umax_to_str(uintmax_t x, char *buf)
30 {
31 1078 const size_t ndigits = umax_count_base10_digits(x);
32 1078 size_t i = ndigits;
33 1078 buf[i--] = '\0';
34 2746 do {
35 2746 buf[i--] = (x % 10) + '0';
36
2/2
✓ Branch 0 (4→4) taken 1668 times.
✓ Branch 1 (4→5) taken 1078 times.
2746 } while (x /= 10);
37 1078 return ndigits;
38 }
39
40 // Like buf_umax_to_str(), but writing a hexadecimal string and
41 // needing `HEX_STR_MAX(x)` bytes of buffer space for arbitrary
42 // values. A minimum number of digits can also be specified, e.g.
43 // for use cases like "U+000A".
44 16 size_t buf_umax_to_hex_str(uintmax_t x, char *buf, size_t min_digits)
45 {
46
2/2
✓ Branch 0 (3→4) taken 10 times.
✓ Branch 1 (3→6) taken 6 times.
16 const size_t ndigits = MAX(min_digits, umax_count_base16_digits(x));
47 10 BUG_ON(ndigits < 1);
48 16 size_t i = ndigits;
49 16 buf[i--] = '\0';
50 118 do {
51 118 unsigned int nibble = x & 0xF;
52 118 buf[i] = hextab_upper[nibble];
53 118 x >>= 4;
54
2/2
✓ Branch 0 (7→7) taken 102 times.
✓ Branch 1 (7→8) taken 16 times.
118 } while (i--);
55 16 return ndigits;
56 }
57
58 // Writes the decimal string representation of `x` into a static
59 // buffer and returns a pointer to it. Unlike buf_umax_to_str(),
60 // this can be done without counting the number of digits first,
61 // by beginning at the end of the buffer and returning a pointer
62 // to the "first" (last written) byte offset.
63 12033 const char *umax_to_str(uintmax_t x)
64 {
65 12033 static char buf[DECIMAL_STR_MAX(x)];
66 12033 size_t i = sizeof(buf) - 2;
67 49018 do {
68 49018 buf[i--] = (x % 10) + '0';
69
2/2
✓ Branch 0 (3→4) taken 36985 times.
✓ Branch 1 (3→5) taken 12033 times.
49018 } while (x /= 10);
70 12033 return &buf[i + 1];
71 }
72
73 // Like buf_umax_to_str() but for uint8_t values (1-3 digits)
74 28 size_t buf_u8_to_str(uint8_t x, char *buf)
75 {
76 28 size_t ndigits = 1;
77
2/2
✓ Branch 0 (2→3) taken 15 times.
✓ Branch 1 (2→4) taken 13 times.
28 if (x >= 100) {
78 15 buf[2] = (x % 10) + '0';
79 15 x /= 10;
80 15 ndigits++;
81 }
82
83
2/2
✓ Branch 0 (4→5) taken 24 times.
✓ Branch 1 (4→6) taken 4 times.
28 if (x >= 10) {
84 24 buf[1] = (x % 10) + '0';
85 24 x /= 10;
86 24 ndigits++;
87 }
88
89 28 buf[0] = (x % 10) + '0';
90 28 return ndigits;
91 }
92
93 12021 const char *uint_to_str(unsigned int x)
94 {
95 12021 return umax_to_str(x);
96 }
97
98 2 const char *ulong_to_str(unsigned long x)
99 {
100 2 return umax_to_str(x);
101 }
102
103 995 size_t buf_uint_to_str(unsigned int x, char *buf)
104 {
105 995 return buf_umax_to_str(x, buf);
106 }
107
108 #ifdef S_ISVTX
109 # define VTXBIT (S_ISVTX)
110 #else
111 # define VTXBIT ((mode_t)0)
112 #endif
113
114 /*
115 * Write the string representation of permission bits from `mode` into `buf`.
116 * This follows the POSIX ls(1) format, but excludes the "is a directory"
117 * clause for the T/t field (as permitted by the spec).
118 *
119 * See also:
120 *
121 * • https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html#tag_20_73_10:~:text=three%20character%20positions
122 * • https://gnu.org/software/coreutils/manual/html_node/What-information-is-listed.html#index-long-ls-format
123 */
124 20 char *file_permissions_to_str(mode_t mode, char buf[10])
125 {
126 20 static const char xmap[8] = "-xSs-xTt";
127
128 // Owner
129
2/2
✓ Branch 0 (2→3) taken 14 times.
✓ Branch 1 (2→4) taken 6 times.
20 buf[0] = (mode & S_IRUSR) ? 'r' : '-';
130
2/2
✓ Branch 0 (4→5) taken 15 times.
✓ Branch 1 (4→6) taken 5 times.
20 buf[1] = (mode & S_IWUSR) ? 'w' : '-';
131 20 buf[2] = xmap[((mode & S_IXUSR) >> 6) | ((mode & S_ISUID) >> 10)];
132
133 // Group
134
2/2
✓ Branch 0 (6→7) taken 15 times.
✓ Branch 1 (6→8) taken 5 times.
20 buf[3] = (mode & S_IRGRP) ? 'r' : '-';
135
2/2
✓ Branch 0 (8→9) taken 16 times.
✓ Branch 1 (8→10) taken 4 times.
20 buf[4] = (mode & S_IWGRP) ? 'w' : '-';
136 20 buf[5] = xmap[((mode & S_IXGRP) >> 3) | ((mode & S_ISGID) >> 9)];
137
138 // Others
139
2/2
✓ Branch 0 (10→11) taken 14 times.
✓ Branch 1 (10→12) taken 6 times.
20 buf[6] = (mode & S_IROTH) ? 'r' : '-';
140
2/2
✓ Branch 0 (12→13) taken 15 times.
✓ Branch 1 (12→14) taken 5 times.
20 buf[7] = (mode & S_IWOTH) ? 'w' : '-';
141 20 buf[8] = xmap[4 + ((mode & S_IXOTH) | ((mode & VTXBIT) >> 8))];
142
143 20 buf[9] = '\0';
144 20 return buf;
145 }
146
147 // Write an approximate, human-readable representation of `bytes` into
148 // `buf`, using IEC 80000-13 units (e.g. KiB, MiB, etc.) and (optionally)
149 // a 2 digit decimal fraction
150 65 char *human_readable_size(uintmax_t bytes, char buf[HRSIZE_MAX])
151 {
152 65 static const char suffixes[8] = "KMGTPEZY";
153 65 unsigned int sigbits = umax_bitwidth(bytes);
154 65 unsigned int nr_unit_shifts = (sigbits - !!sigbits) / 10;
155 65 uintmax_t ipart = bytes >> (nr_unit_shifts * 10);
156 65 uintmax_t unit = ((uintmax_t)1) << (nr_unit_shifts * 10);
157 65 uintmax_t hundredth = unit / 100;
158 65 unsigned int fpart = 0;
159
160
2/2
✓ Branch 0 (2→3) taken 57 times.
✓ Branch 1 (2→5) taken 8 times.
65 if (hundredth) {
161 57 uintmax_t remainder = bytes & (unit - 1);
162 57 unsigned int f = remainder / hundredth;
163 57 ipart += (f > 99);
164
2/2
✓ Branch 0 (3→4) taken 6 times.
✓ Branch 1 (3→5) taken 51 times.
57 fpart = (f > 99) ? 0 : f;
165 }
166
167 65 size_t i = buf_umax_to_str(ipart, buf);
168
169
2/2
✓ Branch 0 (6→7) taken 24 times.
✓ Branch 1 (6→8) taken 41 times.
65 if (fpart > 0) {
170 24 buf[i++] = '.';
171 24 buf[i++] = '0' + ((fpart / 10) % 10);
172 24 buf[i++] = '0' + (fpart % 10);
173 }
174
175
2/2
✓ Branch 0 (8→9) taken 57 times.
✓ Branch 1 (8→10) taken 8 times.
65 if (nr_unit_shifts > 0) {
176 57 buf[i++] = ' ';
177 57 buf[i++] = suffixes[nr_unit_shifts - 1];
178 57 buf[i++] = 'i';
179 57 buf[i++] = 'B';
180 }
181
182 65 buf[i++] = '\0';
183 65 return buf;
184 }
185
186 // Like human_readable_size(), but also printing the exact number
187 // of bytes in parentheses, e.g. 1024 → "1 KiB (1024)"
188 6 char *filesize_to_str(uintmax_t bytes, char buf[FILESIZE_STR_MAX])
189 {
190 6 human_readable_size(bytes, buf);
191
2/2
✓ Branch 0 (3→4) taken 3 times.
✓ Branch 1 (3→6) taken 3 times.
6 if (bytes < 1024) {
192 return buf;
193 }
194 3 size_t i = strlen(buf);
195 3 buf[i++] = ' ';
196 3 buf[i++] = '(';
197 3 i += buf_umax_to_str(bytes, buf + i);
198 3 buf[i++] = ')';
199 3 buf[i] = '\0';
200 3 return buf;
201 }
202