296 lines
6.9 KiB
C
296 lines
6.9 KiB
C
#pragma once
|
|
#include "core.h"
|
|
|
|
typedef struct Str Str;
|
|
struct Str {
|
|
U8 *str;
|
|
U32 len;
|
|
};
|
|
|
|
Str str_empty(void);
|
|
Str str_slice(Str str, U32 start, U32 len);
|
|
// returns -1 if not found, or index start of first needle
|
|
S64 str_index(Str haystack, Str needle);
|
|
|
|
Str str_concat(Allocator *alloc, Str first, Str second);
|
|
Str str_push(Allocator *alloc, Str str);
|
|
Str str_ascii_trim(Str str);
|
|
Str str_ascii_trim_left(Str str);
|
|
Str str_ascii_trim_right(Str str);
|
|
|
|
|
|
#define str_from_lit(str) str_create(((U8 *)str), sizeof(str) -1)
|
|
#define S(str) str_from_lit(str)
|
|
#define cstr_len(str) (U32)strlen(str)
|
|
#define str_dup(alloc, str) str_push((alloc), (str))
|
|
|
|
static INLINE bool str_eq(Str first, Str second)
|
|
{
|
|
if (first.len != second.len) {
|
|
return false;
|
|
}
|
|
return MemCompare(first.str, second.str, first.len) == 0;
|
|
}
|
|
|
|
static INLINE bool str_is_empty(Str str)
|
|
{
|
|
return str.len == 0;
|
|
}
|
|
|
|
static INLINE Str str_create(U8 *start, U32 len)
|
|
{
|
|
Str str = {start, len};
|
|
return str;
|
|
}
|
|
|
|
static INLINE Str str_from_cstr(const char *cstr)
|
|
{
|
|
Str str = str_create((U8 *)cstr, cstr_len(cstr));
|
|
return str;
|
|
}
|
|
|
|
static INLINE Str str_push_cstr(Allocator *alloc, const char *cstr)
|
|
{
|
|
Str str = str_from_cstr(cstr);
|
|
return str_push(alloc, str);
|
|
}
|
|
|
|
static INLINE S64 str_find_byte(Str str, U8 byte)
|
|
{
|
|
for (U32 i = 0; i < str.len; i++) {
|
|
if (str.str[i] == byte) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static INLINE Str str_replace_byte(Str str, U8 orig_byte, U8 new_byte)
|
|
{
|
|
for (U32 i = 0; i < str.len; i++) {
|
|
if (str.str[i] == orig_byte) {
|
|
str.str[i] = new_byte;
|
|
}
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
static INLINE bool str_starts_with(Str str, Str prefix)
|
|
{
|
|
if (prefix.len > str.len) {
|
|
return false;
|
|
}
|
|
|
|
return MemCompare(str.str, prefix.str, prefix.len) == 0;
|
|
}
|
|
|
|
static INLINE bool str_ends_with(Str str, Str suffix)
|
|
{
|
|
if (suffix.len > str.len) {
|
|
return false;
|
|
}
|
|
U32 start = str.len - suffix.len;
|
|
return MemCompare(&str.str[start], suffix.str, suffix.len) == 0;
|
|
}
|
|
|
|
static INLINE Str str_skip(Str str, U32 len)
|
|
{
|
|
U32 skip_len = Min(str.len, len);
|
|
Str new_str = str_slice(str, skip_len, str.len - skip_len);
|
|
|
|
return new_str;
|
|
}
|
|
|
|
// hex_result must be at least source.len * 2. Returns hex_result.
|
|
Str str_hex_encode(Str binary_data, Str hex_result);
|
|
// result must be at least source.len / 2. Returns result.
|
|
Str str_hex_decode(Str hex_str, Str result);
|
|
|
|
static INLINE Str str_hex_encode_alloc(Allocator *allocator, Str binary_data)
|
|
{
|
|
Assert(binary_data.len <= U32_Max / 2);
|
|
U8 hex_len = binary_data.len * 2;
|
|
U8 *result = allocator_push_array(allocator, U8, hex_len);
|
|
Str destination = str_create(result, hex_len);
|
|
|
|
return str_hex_encode(binary_data, destination);
|
|
}
|
|
|
|
static INLINE Str str_hex_decode_alloc(Allocator *allocator, Str hex_str)
|
|
{
|
|
U8 binary_len = hex_str.len / 2;
|
|
U8 *result = allocator_push_array(allocator, U8, binary_len);
|
|
Str destination = str_create(result, binary_len);
|
|
|
|
return str_hex_decode(hex_str, destination);
|
|
}
|
|
// String Vector
|
|
typedef struct StrVec StrVec;
|
|
struct StrVec {
|
|
Str *strings;
|
|
U32 len;
|
|
U32 max_len;
|
|
};
|
|
#define STRVEC_STATIC_INITIALIZER(strings_) STRVEC_INITIALIZER((strings_), ArraySize(strings_), ArraySize(strings_))
|
|
#define STRVEC_STATIC_INITIALIZER_EMPTY(strings_) STRVEC_INITIALIZER((strings_), 0, ArraySize(strings_))
|
|
#define STRVEC_INITIALIZER(strings_, len_, max_len_) {.strings = (strings_), .len = (len_), .max_len = (max_len_)}
|
|
|
|
#define STRVEC_DEFINE(var_name, len_) \
|
|
struct { \
|
|
Str strings[len_];\
|
|
} var_name##_strvec_array; \
|
|
StrVec var_name = STRVEC_STATIC_INITIALIZER_EMPTY(var_name##_strvec_array.strings)
|
|
|
|
void str_split(Str str, Str separators, StrVec *vec);
|
|
Str str_join(Allocator *allocator, StrVec strings, Str separator);
|
|
|
|
|
|
// Characters
|
|
|
|
static INLINE bool char_is_ascii_whitespace(U8 byte)
|
|
{
|
|
return byte == ' ' || byte == '\t' || byte == '\v' || byte == '\f' || byte == '\r' || byte == '\n';
|
|
}
|
|
|
|
static INLINE bool char_is_digit(U8 byte)
|
|
{
|
|
return byte >= '0' && byte <= '9';
|
|
}
|
|
|
|
|
|
// UTF-8/Unicode
|
|
|
|
typedef S32 Rune;
|
|
|
|
|
|
/*
|
|
* Unicode replacement character.
|
|
*
|
|
* Returned for malformed UTF-8
|
|
*/
|
|
#define RUNE_ERROR ((Rune)0xFFFD)
|
|
|
|
#define RUNE_EOF ((Rune)-1)
|
|
#define RUNE_INVALID (-1)
|
|
|
|
// Maximum valid Unicode code point.
|
|
#define RUNE_MAX 0x10FFFF
|
|
#define RUNE_SURROGATE_MIN 0xD800
|
|
#define RUNE_SURROGATE_MAX 0xDFFF
|
|
#define RUNE_1_MAX ((1 << 7)- 1)
|
|
#define RUNE_2_MAX ((1 << 11)- 1)
|
|
#define RUNE_3_MAX ((1 << 16)- 1)
|
|
|
|
typedef struct {
|
|
Rune rune;
|
|
|
|
/*
|
|
* Number of input bytes consumed.
|
|
*
|
|
* valid UTF-8 : 1..4
|
|
* invalid : 1
|
|
* end of input: 0
|
|
*/
|
|
U8 width;
|
|
|
|
/*
|
|
* True only when rune was decoded from valid UTF-8.
|
|
*
|
|
* U+FFFD itself may legitimately occur in valid
|
|
* input, so rune == RUNE_ERROR does not imply an error.
|
|
*/
|
|
bool valid;
|
|
} RuneDecode;
|
|
|
|
// Helper:
|
|
RuneDecode _str_utf8_decode_full(Str str, U32 offset);
|
|
/*
|
|
* Decode one Unicode scalar value from UTF-8.
|
|
*
|
|
* Valid input:
|
|
*
|
|
* returns the decoded Rune
|
|
* valid = true
|
|
* width = number of UTF-8 bytes consumed
|
|
*
|
|
* Invalid input:
|
|
*
|
|
* returns U+FFFD
|
|
* valid = false
|
|
* width = 1
|
|
*
|
|
* Consuming one byte on malformed input guarantees forward progress and
|
|
* allows decoding to resume immediately after the offending byte.
|
|
*
|
|
* End of input:
|
|
*
|
|
* returns RUNE_EOF
|
|
* valid = false
|
|
* width = 0
|
|
*/
|
|
static INLINE RuneDecode str_utf8_decode(Str str, U32 offset)
|
|
{
|
|
if (str.len == 0 || str.len <= offset) {
|
|
return (RuneDecode) {
|
|
.rune = RUNE_EOF,
|
|
.width = 0,
|
|
.valid = false,
|
|
};
|
|
}
|
|
|
|
U8 b0 = str.str[offset];
|
|
|
|
/*
|
|
* Testing b0 directly also avoids consulting utf8_ranges[] for the
|
|
* overwhelmingly common single-byte/ascii case.
|
|
*/
|
|
if (b0 <= RUNE_1_MAX) {
|
|
return (RuneDecode) {
|
|
.rune = (Rune)b0,
|
|
.width = 1,
|
|
.valid = true,
|
|
};
|
|
}
|
|
|
|
return _str_utf8_decode_full(str, offset);
|
|
}
|
|
|
|
// Length of bytes to encode the Rune in utf-8.
|
|
// returns -1 if the Rune is not a valid value to encode in UTF-8.
|
|
static INLINE S32 rune_utf8_len(Rune rune) {
|
|
|
|
if (rune < 0) {
|
|
return RUNE_INVALID;
|
|
} else if (rune <= RUNE_1_MAX) {
|
|
return 1;
|
|
} else if (rune <= RUNE_2_MAX) {
|
|
return 2;
|
|
} else if (rune >= RUNE_SURROGATE_MIN && rune <= RUNE_SURROGATE_MAX) {
|
|
return RUNE_INVALID;
|
|
} else if (rune <= RUNE_3_MAX) {
|
|
return 3;
|
|
} else if (rune <= RUNE_MAX) {
|
|
return 4;
|
|
}
|
|
|
|
return RUNE_INVALID;
|
|
}
|
|
|
|
// Number of unicode Runes in str. 1 invalid UTF-8 byte is counted as 1 Rune.
|
|
static INLINE U32 str_rune_count(Str str)
|
|
{
|
|
U32 offset = 0;
|
|
U32 runes = 0;
|
|
|
|
RuneDecode d = str_utf8_decode(str, offset);
|
|
while (d.rune != RUNE_EOF) {
|
|
offset += d.width;
|
|
runes++;
|
|
d = str_utf8_decode(str, offset);
|
|
}
|
|
|
|
return runes;
|
|
}
|