summaryrefslogtreecommitdiffstats
path: root/src/pl_string.h
blob: 7a0005cd3421f6f0a3c57e0fec0d5939986f3cdb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
/*
 * This file is part of libplacebo.
 *
 * libplacebo is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * libplacebo is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include "common.h"

PL_API_BEGIN

typedef struct pl_str {
    uint8_t *buf;
    size_t len;
} pl_str;

// For formatting with "%.*s"
#define PL_STR_FMT(str) (int)((str).len), ((str).buf ? (char *)((str).buf) : "")

static inline pl_str pl_str0(const char *str)
{
    return (pl_str) {
        .buf = (uint8_t *) str,
        .len = str ? strlen(str) : 0,
    };
}

// Macro version of pl_str0, for constants
#define PL_STR0(str) ((pl_str) { (uint8_t *) (str), (str) ? strlen(str) : 0 })

static inline pl_str pl_strdup(void *alloc, pl_str str)
{
    return (pl_str) {
        .buf = (uint8_t *) (str.len ? pl_memdup(alloc, str.buf, str.len) : NULL),
        .len = str.len,
    };
}

// Always returns a valid string
static inline char *pl_strdup0(void *alloc, pl_str str)
{
    return pl_strndup0(alloc, str.len ? (char *) str.buf : "", str.len);
}

// Adds a trailing \0 for convenience, even if `append` is an empty string
void pl_str_append(void *alloc, pl_str *str, pl_str append);

// Like `pl_str_append` but for raw memory, omits trailing \0
void pl_str_append_raw(void *alloc, pl_str *str, const void *ptr, size_t size);

// Locale-sensitive string functions
char *pl_asprintf(void *parent, const char *fmt, ...)
    PL_PRINTF(2, 3);
char *pl_vasprintf(void *parent, const char *fmt, va_list ap)
    PL_PRINTF(2, 0);
void pl_str_append_asprintf(void *alloc, pl_str *str, const char *fmt, ...)
    PL_PRINTF(3, 4);
void pl_str_append_vasprintf(void *alloc, pl_str *str, const char *fmt, va_list va)
    PL_PRINTF(3, 0);
int pl_str_sscanf(pl_str str, const char *fmt, ...);

// Locale-invariant versions of append_(v)asprintf
//
// NOTE: These only support a small handful of modifiers. Check `format.c`
// for a list. Calling them on an invalid string will abort!
void pl_str_append_asprintf_c(void *alloc, pl_str *str, const char *fmt, ...)
    PL_PRINTF(3, 4);
void pl_str_append_vasprintf_c(void *alloc, pl_str *str, const char *fmt, va_list va)
    PL_PRINTF(3, 0);

// Variant of the above which takes arguments directly from a pointer in memory,
// reading them incrementally (tightly packed). Returns the amount of bytes
// read from `args`, as determined by the following table:
//
// %c: sizeof(char)
// %d, %u: sizeof(int)
// %f: sizeof(double)
// %lld, %llu: sizeof(long long int)
// %zu: sizeof(size_t)
// %s: \0 terminated string
// %.*s: sizeof(int) + that many bytes (no \0 terminator)
size_t pl_str_append_memprintf_c(void *alloc, pl_str *str, const char *fmt,
                                 const void *args)
    PL_PRINTF(3, 0);

// Locale-invariant number printing
int pl_str_print_hex(char *buf, size_t len, unsigned short n);
int pl_str_print_int(char *buf, size_t len, int n);
int pl_str_print_uint(char *buf, size_t len, unsigned int n);
int pl_str_print_int64(char *buf, size_t len, int64_t n);
int pl_str_print_uint64(char *buf, size_t len, uint64_t n);
int pl_str_print_float(char *buf, size_t len, float n);
int pl_str_print_double(char *buf, size_t len, double n);

// Locale-invariant number parsing
bool pl_str_parse_hex(pl_str str, unsigned short *out);
bool pl_str_parse_int(pl_str str, int *out);
bool pl_str_parse_uint(pl_str str, unsigned int *out);
bool pl_str_parse_int64(pl_str str, int64_t *out);
bool pl_str_parse_uint64(pl_str str, uint64_t *out);
bool pl_str_parse_float(pl_str str, float *out);
bool pl_str_parse_double(pl_str str, double *out);

// Variants of string.h functions
int pl_strchr(pl_str str, int c);
size_t pl_strspn(pl_str str, const char *accept);
size_t pl_strcspn(pl_str str, const char *reject);

// Strip leading/trailing whitespace
pl_str pl_str_strip(pl_str str);

// Generic functions for cutting up strings
static inline pl_str pl_str_take(pl_str str, size_t len)
{
    if (len < str.len)
        str.len = len;
    return str;
}

static inline pl_str pl_str_drop(pl_str str, size_t len)
{
    if (len >= str.len)
        return (pl_str) { .buf = NULL, .len = 0 };

    str.buf += len;
    str.len -= len;
    return str;
}

// Find a substring in another string, and return its index (or -1)
int pl_str_find(pl_str haystack, pl_str needle);

// String splitting functions. These return the part of the string before
// the separator, and optionally the rest (in `out_rest`).
//
// Note that the separator is not included as part of either string.
pl_str pl_str_split_char(pl_str str, char sep, pl_str *out_rest);
pl_str pl_str_split_str(pl_str str, pl_str sep, pl_str *out_rest);

// Like `pl_str_split_char`, but splits on any char in `seps`
pl_str pl_str_split_chars(pl_str str, const char *seps, pl_str *out_rest);

static inline pl_str pl_str_getline(pl_str str, pl_str *out_rest)
{
    return pl_str_split_char(str, '\n', out_rest);
}

// Decode a string containing hexadecimal data. All whitespace will be silently
// ignored. When successful, this allocates a new array to store the output.
bool pl_str_decode_hex(void *alloc, pl_str hex, pl_str *out);

static inline bool pl_str_equals(pl_str str1, pl_str str2)
{
    if (str1.len != str2.len)
        return false;
    if (str1.buf == str2.buf || !str1.len)
        return true;
    return memcmp(str1.buf, str2.buf, str1.len) == 0;
}

static inline bool pl_str_startswith(pl_str str, pl_str prefix)
{
    if (!prefix.len)
        return true;
    if (str.len < prefix.len)
        return false;
    return memcmp(str.buf, prefix.buf, prefix.len) == 0;
}

static inline bool pl_str_endswith(pl_str str, pl_str suffix)
{
    if (!suffix.len)
        return true;
    if (str.len < suffix.len)
        return false;
    return memcmp(str.buf + str.len - suffix.len, suffix.buf, suffix.len) == 0;
}

static inline bool pl_str_eatstart(pl_str *str, pl_str prefix)
{
    if (!pl_str_startswith(*str, prefix))
        return false;

    str->buf += prefix.len;
    str->len -= prefix.len;
    return true;
}

static inline bool pl_str_eatend(pl_str *str, pl_str suffix)
{
    if (!pl_str_endswith(*str, suffix))
        return false;

    str->len -= suffix.len;
    return true;
}

// Convenience wrappers for the above which save the use of a pl_str0
static inline pl_str pl_str_split_str0(pl_str str, const char *sep, pl_str *out_rest)
{
    return pl_str_split_str(str, pl_str0(sep), out_rest);
}

static inline bool pl_str_startswith0(pl_str str, const char *prefix)
{
    return pl_str_startswith(str, pl_str0(prefix));
}

static inline bool pl_str_endswith0(pl_str str, const char *suffix)
{
    return pl_str_endswith(str, pl_str0(suffix));
}

static inline bool pl_str_equals0(pl_str str1, const char *str2)
{
    return pl_str_equals(str1, pl_str0(str2));
}

static inline bool pl_str_eatstart0(pl_str *str, const char *prefix)
{
    return pl_str_eatstart(str, pl_str0(prefix));
}

static inline bool pl_str_eatend0(pl_str *str, const char *prefix)
{
    return pl_str_eatend(str, pl_str0(prefix));
}

// String building helpers, used to lazily construct a string by appending a
// series of string templates which can be executed on-demand into a final
// output buffer.
typedef struct pl_str_builder_t *pl_str_builder;

// Returns the number of bytes consumed from `args`. Be warned that the pointer
// given will not necessarily be aligned to the type you need it as, so make
// sure to use `memcpy` or some other method of safely loading arbitrary data
// from memory.
typedef size_t (*pl_str_template)(void *alloc, pl_str *buf, const uint8_t *args);

pl_str_builder pl_str_builder_alloc(void *alloc);
void pl_str_builder_free(pl_str_builder *builder);

// Resets string builder without destroying buffer
void pl_str_builder_reset(pl_str_builder builder);

// Returns a representative hash of the string builder's output, without
// actually executing it. Note that this is *not* the same as a pl_str_hash of
// the string builder's output.
//
// Note also that the output of this may not survive a process restart because
// of position-independent code and address randomization moving around the
// locatons of template functions, so special care must be taken not to
// compare such hashes across process invocations.
uint64_t pl_str_builder_hash(const pl_str_builder builder);

// Executes a string builder, dispatching all templates. The resulting string
// is guaranteed to be \0-terminated, as a minor convenience.
//
// Calling any other `pl_str_builder_*` function on this builder causes the
// contents of the returned string to become undefined.
pl_str pl_str_builder_exec(pl_str_builder builder);

// Append a template and its arguments to a string builder
void pl_str_builder_append(pl_str_builder builder, pl_str_template tmpl,
                           const void *args, size_t args_size);

// Append an entire other `pl_str_builder` onto `builder`
void pl_str_builder_concat(pl_str_builder builder, const pl_str_builder append);

// Append a constant string. This will only record &str into the buffer, which
// may have a number of unwanted consequences if the memory pointed at by
// `str` mutates at any point in time in the future, or if `str` is not
// at a stable location in memory.
//
// This is intended for strings which are compile-time constants.
void pl_str_builder_const_str(pl_str_builder builder, const char *str);

// Append a string. This will make a full copy of `str`
void pl_str_builder_str(pl_str_builder builder, const pl_str str);
#define pl_str_builder_str0(b, str) pl_str_builder_str(b, pl_str0(str))

// Append a string printf-style. This will preprocess `fmt` to determine the
// number and type of arguments. Supports the same format conversion characters
// as `pl_str_append_asprintf_c`.
void pl_str_builder_printf_c(pl_str_builder builder, const char *fmt, ...)
    PL_PRINTF(2, 3);

void pl_str_builder_vprintf_c(pl_str_builder builder, const char *fmt, va_list ap)
    PL_PRINTF(2, 0);

// Helper macro to specialize `pl_str_builder_printf_c` to
// `pl_str_builder_const_str` if it contains no format characters.
#define pl_str_builder_addf(builder, ...) do                                    \
{                                                                               \
    if (_contains_fmt_chars(__VA_ARGS__)) {                                     \
        pl_str_builder_printf_c(builder, __VA_ARGS__);                          \
    } else {                                                                    \
        pl_str_builder_const_str(builder, _get_fmt(__VA_ARGS__));               \
    }                                                                           \
} while (0)

// Helper macros to deal with the non-portability of __VA_OPT__(,)
#define _contains_fmt_chars(fmt, ...)   (strchr(fmt, '%'))
#define _get_fmt(fmt, ...)              fmt

PL_API_END