Files
nob/src/base/string.h
T

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;
}