diff options
Diffstat (limited to '')
-rw-r--r-- | src/pl_string.h | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/src/pl_string.h b/src/pl_string.h new file mode 100644 index 0000000..7a0005c --- /dev/null +++ b/src/pl_string.h @@ -0,0 +1,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 |