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