#ifndef _AL_STR_H #define _AL_STR_H #include "lib.h" typedef struct { u32 length; u32 alloc; char *__sized_by(alloc) data; } str; #define AL_STR_NO_POS ((u32)-1) #define al_str_null() ((str){ 0, 0, NULL }) #define al_str_at(s, index) (s)->data[index] #define al_str_atr(s, index) (s)->data[(s)->length - (index)] #define al_chars_if_literal(s) ((char *)("" s "")) // use al_str_cs/cr for non-static arrays. #define al_str_c(c) ((str){ (u32)(sizeof(c) - 1), 0, al_chars_if_literal(c) }) #define al_str_cs(c) ((str){ (u32)al_strnlen(c, sizeof(c)), 0, ((char *)(c)) }) // (s)ized #define al_str_cr(c) ((str){ (u32)al_strnlen(c, MAX_ALLOC_32), 0, ((char *)(c)) }) // (r)untime #define al_str_w(c, index, n) ((str){ ((u32)(n)), 0, (c) + (index) }) #define al_str_substr(s, start, end) al_str_w((s)->data, start, ((end) - (start))) #define al_str_x(s) (s)->length, (s)->data // e(x)pand #define al_str_x_maxlen(s, len) MIN((s)->length, (len)), (s)->data static inline bool al_str_is_empty(str *s) { return s->length == 0; } static inline void al_str_free(str *s) { if (s->alloc) al_free(s->data); } static inline u32 al_str_reserve(str *s, u32 size) { return al_growing_allocation((void **)&s->data, s->alloc, size, al_realloc); } static inline void al_str_sized(str *s, u32 size) { *s = al_str_null(); s->alloc = al_str_reserve(s, size); } static inline void al_str_clone(str *dest, str *src) { al_str_sized(dest, src->length); al_memcpy(dest->data, src->data, src->length); dest->length = src->length; } static inline void al_str_from(str *s, const char *c_str) { size_t length = al_strlen(c_str); al_str_sized(s, (u32)length); al_memcpy(s->data, c_str, length); s->length = (u32)length; } static inline void al_str_to_lower(str *s) { for (u32 i = 0; i < s->length; i++) { al_str_at(s, i) = al_tolower(al_str_at(s, i)); } } static inline void al_str_to_upper(str *s) { for (u32 i = 0; i < s->length; i++) { al_str_at(s, i) = al_toupper(al_str_at(s, i)); } } // Returned c_str must be free'd by the user. static inline char *al_str_to_c_str(str *s) { char *c_str = (char *)al_malloc(s->length + 1); al_memcpy(c_str, s->data, s->length); c_str[s->length] = '\0'; return c_str; } static inline void al_str_cat(str *s, str *c) { s->alloc = al_str_reserve(s, s->length + c->length); al_memcpy(s->data + s->length, c->data, c->length); s->length += c->length; } static inline void al_str_insert(str *s, u32 index, str *c) { s->alloc = al_str_reserve(s, s->length + c->length); char *pos = s->data + index; if (index < s->length) { al_memmove(pos + c->length, pos, s->length - index); } al_memcpy(pos, c->data, c->length); s->length += c->length; } static inline void al_str_prepend(str *s, str *c) { al_str_insert(s, 0, c); } static inline void al_str_remove_at(str *s, u32 index) { al_assert(index < s->length); if (index != --s->length) { al_memmove(s->data + index, s->data + index + 1, s->length - index); } } static inline void al_str_replace(str *s, char c, char n) { for (u32 i = 0; i < s->length; i++) { if (al_str_at(s, i) == c) { al_str_at(s, i) = n; } } } static inline s32 al_str_cmp(str *s, str *c, u32 start, u32 length) { if (length > c->length) return 1; else if (length + start > s->length) return -1; return al_memcmp(&al_str_at(s, start), c->data, length); } static inline bool al_str_eq(str *s, str *c) { return al_str_cmp(s, c, 0, MAX(s->length, c->length)) == 0; } static inline u32 al_str_find(str *s, char c) { char *loc = (char *)al_memchr(s->data, c, s->length); return loc ? (u32)(loc - s->data) : AL_STR_NO_POS; } static inline u32 al_str_rfind(str *s, char c) { char *loc = (char *)al_memrchr(s->data, c, s->length); return loc ? (u32)(loc - s->data) : AL_STR_NO_POS; } // extremely slow. static inline u32 al_str_find_str(str *s, str *c) { if (c->length <= s->length) { for (u32 i = 0; i < s->length - c->length; i++) { if (al_str_at(s, i) == al_str_at(c, 0) && (al_str_cmp(s, c, i, c->length) == 0)) { return i; } } } return AL_STR_NO_POS; } static inline bool al_str_tok(str *result, char delim) { u32 index = al_str_find(result, delim); if (index == AL_STR_NO_POS) { return false; } *result = al_str_substr(result, index + 1, result->length); return true; } // djb2 hash. static inline u32 al_str_hash(str *s) { u32 hash = 5381; for (u32 i = 0; i < s->length; i++) { // hash * 33 + c hash = ((hash << 5) + hash) + (uchar)al_str_at(s, i); } return hash; } s64 al_str_to_long(str *s, u32 base, bool *error); bool al_str_get_line(str *buffer, char sep, str *line); #endif // _AL_STR_H