dte test coverage


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