Reorganize files to base/ folder (#1)
This commit was merged in pull request #1.
This commit is contained in:
@@ -0,0 +1,295 @@
|
||||
#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;
|
||||
}
|
||||
Reference in New Issue
Block a user