Branch data Line data Source code
1 : : /*
2 : : * This file is part of libplacebo.
3 : : *
4 : : * libplacebo is free software; you can redistribute it and/or
5 : : * modify it under the terms of the GNU Lesser General Public
6 : : * License as published by the Free Software Foundation; either
7 : : * version 2.1 of the License, or (at your option) any later version.
8 : : *
9 : : * libplacebo is distributed in the hope that it will be useful,
10 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : : * GNU Lesser General Public License for more details.
13 : : *
14 : : * You should have received a copy of the GNU Lesser General Public
15 : : * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
16 : : */
17 : :
18 : : #include "common.h"
19 : : #include "hash.h"
20 : :
21 : 735040 : static void grow_str(void *alloc, pl_str *str, size_t len)
22 : : {
23 : : // Like pl_grow, but with some extra headroom
24 [ + + ]: 735040 : if (len > pl_get_size(str->buf))
25 : 1472 : str->buf = pl_realloc(alloc, str->buf, len * 1.5);
26 : 735040 : }
27 : :
28 : 106028 : void pl_str_append(void *alloc, pl_str *str, pl_str append)
29 : : {
30 : : // Also append an extra \0 for convenience, since a lot of the time
31 : : // this function will be used to generate a string buffer
32 : 106028 : grow_str(alloc, str, str->len + append.len + 1);
33 [ + + ]: 106028 : if (append.len) {
34 : 101171 : memcpy(str->buf + str->len, append.buf, append.len);
35 : 101171 : str->len += append.len;
36 : : }
37 : 106028 : str->buf[str->len] = '\0';
38 : 106028 : }
39 : :
40 : 565554 : void pl_str_append_raw(void *alloc, pl_str *str, const void *ptr, size_t size)
41 : : {
42 [ + + ]: 565554 : if (!size)
43 : : return;
44 : 553285 : grow_str(alloc, str, str->len + size);
45 : 553285 : memcpy(str->buf + str->len, ptr, size);
46 : 553285 : str->len += size;
47 : : }
48 : :
49 : 3300 : void pl_str_append_asprintf(void *alloc, pl_str *str, const char *fmt, ...)
50 : : {
51 : : va_list ap;
52 : 3300 : va_start(ap, fmt);
53 : 3300 : pl_str_append_vasprintf(alloc, str, fmt, ap);
54 : 3300 : va_end(ap);
55 : 3300 : }
56 : :
57 : 74827 : void pl_str_append_vasprintf(void *alloc, pl_str *str, const char *fmt, va_list ap)
58 : : {
59 : : // First, we need to determine the size that will be required for
60 : : // printing the entire string. Do this by making a copy of the va_list
61 : : // and printing it to a null buffer.
62 : : va_list copy;
63 [ - + ]: 74827 : va_copy(copy, ap);
64 : : int size = vsnprintf(NULL, 0, fmt, copy);
65 : 74827 : va_end(copy);
66 [ - + ]: 74827 : if (size < 0)
67 : 0 : return;
68 : :
69 : : // Make room in `str` and format to there directly
70 : 74827 : grow_str(alloc, str, str->len + size + 1);
71 : 74827 : str->len += vsnprintf((char *) (str->buf + str->len), size + 1, fmt, ap);
72 : : }
73 : :
74 [ + - ]: 4 : int pl_str_sscanf(pl_str str, const char *fmt, ...)
75 : : {
76 : : char *tmp = pl_strdup0(NULL, str);
77 : : va_list va;
78 : 4 : va_start(va, fmt);
79 : 4 : int ret = vsscanf(tmp, fmt, va);
80 : 4 : va_end(va);
81 : 4 : pl_free(tmp);
82 : 4 : return ret;
83 : : }
84 : :
85 : 2 : int pl_strchr(pl_str str, int c)
86 : : {
87 [ + + ]: 2 : if (!str.len)
88 : : return -1;
89 : :
90 : 595 : void *pos = memchr(str.buf, c, str.len);
91 [ + + + - ]: 595 : if (pos)
92 : 1 : return (intptr_t) pos - (intptr_t) str.buf;
93 : : return -1;
94 : : }
95 : :
96 : 245 : size_t pl_strspn(pl_str str, const char *accept)
97 : : {
98 [ + + ]: 973 : for (size_t i = 0; i < str.len; i++) {
99 [ + + ]: 961 : if (!strchr(accept, str.buf[i]))
100 : 233 : return i;
101 : : }
102 : :
103 : : return str.len;
104 : : }
105 : :
106 : 2 : size_t pl_strcspn(pl_str str, const char *reject)
107 : : {
108 [ + + + + ]: 645 : for (size_t i = 0; i < str.len; i++) {
109 [ + + + + ]: 625 : if (strchr(reject, str.buf[i]))
110 : 1 : return i;
111 : : }
112 : :
113 : : return str.len;
114 : : }
115 : :
116 : : static inline bool pl_isspace(char c)
117 : : {
118 : 19158 : switch (c) {
119 : : case ' ':
120 : : case '\n':
121 : : case '\r':
122 : : case '\t':
123 : : case '\v':
124 : : case '\f':
125 : : return true;
126 : : default:
127 : : return false;
128 : : }
129 : : }
130 : :
131 : 939 : pl_str pl_str_strip(pl_str str)
132 : : {
133 [ + + + + ]: 3663 : while (str.len && pl_isspace(str.buf[0])) {
134 : 2724 : str.buf++;
135 : 2724 : str.len--;
136 : : }
137 [ + + + + ]: 15334 : while (str.len && pl_isspace(str.buf[str.len - 1]))
138 : : str.len--;
139 : 939 : return str;
140 : : }
141 : :
142 : 65 : int pl_str_find(pl_str haystack, pl_str needle)
143 : : {
144 [ + + ]: 65 : if (!needle.len)
145 : : return 0;
146 : :
147 [ + + ]: 11431 : for (size_t i = 0; i + needle.len <= haystack.len; i++) {
148 [ + + ]: 11419 : if (memcmp(&haystack.buf[i], needle.buf, needle.len) == 0)
149 : 51 : return i;
150 : : }
151 : :
152 : : return -1;
153 : : }
154 : :
155 : 598 : pl_str pl_str_split_char(pl_str str, char sep, pl_str *out_rest)
156 : : {
157 [ + + ]: 598 : int pos = pl_strchr(str, sep);
158 [ - + ]: 516 : if (pos < 0) {
159 [ + - ]: 82 : if (out_rest)
160 : 82 : *out_rest = (pl_str) {0};
161 : 82 : return str;
162 : : } else {
163 [ + + ]: 516 : if (out_rest)
164 [ + + ]: 1025 : *out_rest = pl_str_drop(str, pos + 1);
165 : 516 : return pl_str_take(str, pos);
166 : : }
167 : : }
168 : :
169 : 36 : pl_str pl_str_split_chars(pl_str str, const char *seps, pl_str *out_rest)
170 : : {
171 : 36 : int pos = pl_strcspn(str, seps);
172 [ - + ]: 36 : if (pos < 0) {
173 [ # # ]: 0 : if (out_rest)
174 : 0 : *out_rest = (pl_str) {0};
175 : 0 : return str;
176 : : } else {
177 [ + - ]: 36 : if (out_rest)
178 [ + + ]: 53 : *out_rest = pl_str_drop(str, pos + 1);
179 : 36 : return pl_str_take(str, pos);
180 : : }
181 : : }
182 : :
183 : 51 : pl_str pl_str_split_str(pl_str str, pl_str sep, pl_str *out_rest)
184 : : {
185 : 51 : int pos = pl_str_find(str, sep);
186 [ + + ]: 51 : if (pos < 0) {
187 [ + - ]: 11 : if (out_rest)
188 : 11 : *out_rest = (pl_str) {0};
189 : 11 : return str;
190 : : } else {
191 [ + - ]: 40 : if (out_rest)
192 [ + + ]: 79 : *out_rest = pl_str_drop(str, pos + sep.len);
193 : 40 : return pl_str_take(str, pos);
194 : : }
195 : : }
196 : :
197 : 321 : static bool get_hexdigit(pl_str *str, int *digit)
198 : : {
199 [ + - - + ]: 321 : while (str->len && pl_isspace(str->buf[0])) {
200 : 0 : str->buf++;
201 : 0 : str->len--;
202 : : }
203 : :
204 [ - + ]: 321 : if (!str->len) {
205 : 0 : *digit = -1; // EOF
206 : 0 : return true;
207 : : }
208 : :
209 : 321 : char c = str->buf[0];
210 : 321 : str->buf++;
211 : 321 : str->len--;
212 : :
213 [ + + ]: 321 : if (c >= '0' && c <= '9') {
214 : 140 : *digit = c - '0';
215 [ + + ]: 181 : } else if (c >= 'a' && c <= 'f') {
216 : 180 : *digit = c - 'a' + 10;
217 [ - + ]: 1 : } else if (c >= 'A' && c <= 'F') {
218 : 0 : *digit = c - 'A' + 10;
219 : : } else {
220 : : return false; // invalid char
221 : : }
222 : :
223 : : return true;
224 : : }
225 : :
226 : 12 : bool pl_str_decode_hex(void *alloc, pl_str hex, pl_str *out)
227 : : {
228 [ - + ]: 12 : if (!out)
229 : : return false;
230 : :
231 : 12 : uint8_t *buf = pl_alloc(alloc, hex.len / 2);
232 : : int len = 0;
233 : :
234 [ + + ]: 172 : while (hex.len) {
235 : : int a, b;
236 [ + + - + ]: 161 : if (!get_hexdigit(&hex, &a) || !get_hexdigit(&hex, &b))
237 : 1 : goto error; // invalid char
238 [ + - ]: 160 : if (a < 0) // EOF
239 : : break;
240 [ - + ]: 160 : if (b < 0) // only one digit
241 : 0 : goto error;
242 : :
243 : 160 : buf[len++] = (a << 4) | b;
244 : : }
245 : :
246 : 11 : *out = (pl_str) { buf, len };
247 : 11 : return true;
248 : :
249 : : error:
250 : 1 : pl_free(buf);
251 : 1 : return false;
252 : : }
253 : :
254 : : struct pl_str_builder_t {
255 : : PL_ARRAY(pl_str_template) templates;
256 : : pl_str args;
257 : : pl_str output;
258 : : };
259 : :
260 : 273 : pl_str_builder pl_str_builder_alloc(void *alloc)
261 : : {
262 : 273 : pl_str_builder b = pl_zalloc_ptr(alloc, b);
263 : 273 : return b;
264 : : }
265 : :
266 : 0 : void pl_str_builder_free(pl_str_builder *b)
267 : : {
268 [ # # ]: 0 : if (*b)
269 : 0 : pl_free_ptr(b);
270 : 0 : }
271 : :
272 : 18845 : void pl_str_builder_reset(pl_str_builder b)
273 : : {
274 : 18845 : *b = (struct pl_str_builder_t) {
275 : 18845 : .templates.elem = b->templates.elem,
276 : 18845 : .args.buf = b->args.buf,
277 : 18845 : .output.buf = b->output.buf,
278 : : };
279 : 18845 : }
280 : :
281 : 3776 : uint64_t pl_str_builder_hash(const pl_str_builder b)
282 : : {
283 : 3776 : size_t size = b->templates.num * sizeof(b->templates.elem[0]);
284 : 3776 : uint64_t hash = pl_mem_hash(b->templates.elem, size);
285 : : pl_hash_merge(&hash, pl_str_hash(b->args));
286 : 3776 : return hash;
287 : : }
288 : :
289 : 900 : pl_str pl_str_builder_exec(pl_str_builder b)
290 : : {
291 : 900 : pl_str args = b->args;
292 : :
293 : 900 : b->output.len = 0;
294 [ + + ]: 42272 : for (int i = 0; i < b->templates.num; i++) {
295 : 41372 : size_t consumed = b->templates.elem[i](b, &b->output, args.buf);
296 [ - + ]: 41372 : pl_assert(consumed <= args.len);
297 : : args = pl_str_drop(args, consumed);
298 : : }
299 : :
300 : : // Terminate with an extra \0 byte for convenience
301 : 900 : grow_str(b, &b->output, b->output.len + 1);
302 : 900 : b->output.buf[b->output.len] = '\0';
303 : 900 : return b->output;
304 : : }
305 : :
306 : 103183 : void pl_str_builder_append(pl_str_builder b, pl_str_template tmpl,
307 : : const void *args, size_t size)
308 : : {
309 [ + + + + : 103183 : PL_ARRAY_APPEND(b, b->templates, tmpl);
- + ]
310 : 103183 : pl_str_append_raw(b, &b->args, args, size);
311 : 103183 : }
312 : :
313 : 15934 : void pl_str_builder_concat(pl_str_builder b, const pl_str_builder append)
314 : : {
315 [ + + + + ]: 15934 : PL_ARRAY_CONCAT(b, b->templates, append->templates);
316 : 15934 : pl_str_append_raw(b, &b->args, append->args.buf, append->args.len);
317 : 15934 : }
318 : :
319 : 9274 : static size_t template_str_ptr(void *alloc, pl_str *buf, const uint8_t *args)
320 : : {
321 : : const char *str;
322 : : memcpy(&str, args, sizeof(str));
323 : 9274 : pl_str_append_raw(alloc, buf, str, strlen(str));
324 : 9274 : return sizeof(str);
325 : : }
326 : :
327 : 29603 : void pl_str_builder_const_str(pl_str_builder b, const char *str)
328 : : {
329 : 29603 : pl_str_builder_append(b, template_str_ptr, &str, sizeof(str));
330 : 29603 : }
331 : :
332 : 19 : static size_t template_str(void *alloc, pl_str *buf, const uint8_t *args)
333 : : {
334 : : pl_str str;
335 : : memcpy(&str.len, args, sizeof(str.len));
336 : 19 : pl_str_append_raw(alloc, buf, args + sizeof(str.len), str.len);
337 : 19 : return sizeof(str.len) + str.len;
338 : : }
339 : :
340 : 19 : void pl_str_builder_str(pl_str_builder b, const pl_str str)
341 : : {
342 : 19 : pl_str_builder_append(b, template_str, &str.len, sizeof(str.len));
343 : 19 : pl_str_append_raw(b, &b->args, str.buf, str.len);
344 : 19 : }
345 : :
346 : 66547 : void pl_str_builder_printf_c(pl_str_builder b, const char *fmt, ...)
347 : : {
348 : : va_list ap;
349 : 66547 : va_start(ap, fmt);
350 : 66547 : pl_str_builder_vprintf_c(b, fmt, ap);
351 : 66547 : va_end(ap);
352 : 66547 : }
353 : :
354 : 25977 : static size_t template_printf(void *alloc, pl_str *str, const uint8_t *args)
355 : : {
356 : : const char *fmt;
357 : : memcpy(&fmt, args, sizeof(fmt));
358 : 25977 : args += sizeof(fmt);
359 : :
360 : 25977 : return sizeof(fmt) + pl_str_append_memprintf_c(alloc, str, fmt, args);
361 : : }
362 : :
363 : 66547 : void pl_str_builder_vprintf_c(pl_str_builder b, const char *fmt, va_list ap)
364 : : {
365 : 66547 : pl_str_builder_append(b, template_printf, &fmt, sizeof(fmt));
366 : :
367 : : // Push all of the variadic arguments directly onto `b->args`
368 [ + + ]: 220864 : for (const char *c; (c = strchr(fmt, '%')) != NULL; fmt = c + 1) {
369 : 154317 : c++;
370 [ + + + + : 154317 : switch (c[0]) {
+ + + + +
+ - ]
371 : : #define WRITE(T, x) pl_str_append_raw(b, &b->args, &(T) {x}, sizeof(T))
372 : 189 : case '%': continue;
373 : 163503 : case 'c': WRITE(char, va_arg(ap, int)); break;
374 : 27939 : case 'd': WRITE(int, va_arg(ap, int)); break;
375 : 2770 : case 'u': WRITE(unsigned, va_arg(ap, unsigned)); break;
376 : 7530 : case 'f': WRITE(double, va_arg(ap, double)); break;
377 : 70942 : case 'h':
378 [ - + ]: 70942 : assert(c[1] == 'x');
379 : 70942 : WRITE(unsigned short, va_arg(ap, unsigned));
380 : 70942 : c++;
381 : 70942 : break;
382 : 144 : case 'l':
383 [ - + ]: 144 : assert(c[1] == 'l');
384 [ + + - ]: 144 : switch (c[2]) {
385 : 143 : case 'u': WRITE(long long unsigned, va_arg(ap, long long unsigned)); break;
386 : 1 : case 'd': WRITE(long long int, va_arg(ap, long long int)); break;
387 : 0 : default: abort();
388 : : }
389 : 144 : c += 2;
390 : 144 : break;
391 : 2364 : case 'z':
392 [ - + ]: 2364 : assert(c[1] == 'u');
393 : 2364 : WRITE(size_t, va_arg(ap, size_t));
394 : 2364 : c++;
395 : 2364 : break;
396 : 32827 : case 's': {
397 : 32827 : pl_str str = pl_str0(va_arg(ap, const char *));
398 : 32827 : pl_str_append(b, &b->args, str);
399 : 32827 : b->args.len++; // expand to include \0 byte (from pl_str_append)
400 : : break;
401 : : }
402 : 237 : case '.': {
403 [ - + ]: 237 : assert(c[1] == '*');
404 [ - + ]: 237 : assert(c[2] == 's');
405 : 237 : int len = va_arg(ap, int);
406 : 237 : const char *str = va_arg(ap, const char *);
407 : 237 : WRITE(int, len);
408 : 237 : pl_str_append_raw(b, &b->args, str, len);
409 : 237 : c += 2;
410 : : break;
411 : : }
412 : 0 : default:
413 : 0 : fprintf(stderr, "Invalid conversion character: '%c'!\n", c[0]);
414 : 0 : abort();
415 : : }
416 : : #undef WRITE
417 : : }
418 : 66547 : }
|