#ifndef _AL_WSTR_H #define _AL_WSTR_H #include "lib.h" #include "str.h" #include typedef struct { u32 length; u32 alloc; wchar_t *__sized_by(alloc) data; } wstr; #define AL_WIDE_CHARS_IF_LITERAL(wc) ((wchar_t *)(L"" wc L"")) // Use al_wstr_cs/r for non-static char arrays. #define AL_WSTR_NO_POS ((u32)-1) #define al_wstr_null() ((wstr){ 0, 0, NULL }) #define al_wstr_at(w, index) (w)->data[index] #define al_wstr_atr(w, index) (w)->data[(w)->length - (index)] #define al_wstr_c(wc) ((wstr){ (u32)((sizeof(wc) / sizeof(wchar_t)) - 1), 0, AL_WIDE_CHARS_IF_LITERAL(wc) }) #define al_wstr_cs(wc) ((wstr){ (u32)al_wcsnlen(wc, sizeof(wc) / sizeof(wchar_t)), 0, ((wchar_t *)(wc)) }) // (s)ized #define al_wstr_cr(wc) ((wstr){ (u32)al_wcsnlen(wc, MAX_ALLOC_32 / sizeof(wchar_t)), 0, ((wchar_t *)(wc)) }) // (r)untime #define al_wstr_w(wc, index, n) ((wstr){ ((u32)(n)), 0, (wc) + (index) }) #define al_wstr_substr(w, start, end) al_wstr_w((w)->data, start, ((end) - (start))) #define al_wstr_x(w) (w)->data // e(x)pand static inline size_t al_wcsnlen(const wchar_t *w, size_t n) { const wchar_t *p = (const wchar_t *)wmemchr(w, L'\0', n); return p ? (size_t)(p - w) : n; } static inline s32 al_wchar_width(wchar_t wc) { #ifdef AL_HAVE_WIDE_STRING_WIDTH return wcwidth(wc); #else (void)wc; al_assert_and_return(0); #endif } static inline bool al_wstr_is_empty(wstr *w) { return w->length == 0; } static inline void al_wstr_free(wstr *w) { if (w->alloc) al_free(w->data); } static inline u32 al_wstr_reserve(wstr *w, u32 size) { return al_growing_allocation((void **)&w->data, w->alloc, size, al_realloc); } static inline void al_wstr_sized(wstr *w, u32 size) { *w = al_wstr_null(); w->alloc = al_wstr_reserve(w, size * sizeof(wchar_t)); } static inline void al_wstr_clone(wstr *dest, wstr *src) { u32 length = src->length; al_wstr_sized(dest, length + 1); al_memcpy(dest->data, src->data, length * sizeof(wchar_t)); dest->data[length] = L'\0'; dest->length = src->length; } static inline bool al_wstr_from_cstr(wstr *w, char *c) { #ifdef AL_HAVE_WIDE_STRING mbstate_t mbstate = { 0 }; size_t length; length = mbsrtowcs(NULL, (const char **)&c, 0, &mbstate); if (length == (size_t)-1) { return false; } al_wstr_sized(w, (u32)(length + 1)); w->length = (u32)mbsrtowcs(w->data, (const char **)&c, length, &mbstate); al_assert(w->length == (u32)length); w->data[w->length] = L'\0'; return true; #else (void)c; (void)w; al_assert_and_return(false); #endif } static inline void al_wstr_from_str(wstr *w, str *s) { char *c_str = al_str_to_c_str(s); al_wstr_from_cstr(w, c_str); al_free(c_str); } // This is horrible. static inline bool al_str_from_wstr(str *s, wstr *w) { // This is broken on Windows. Locale issue? #ifdef AL_HAVE_WIDE_STRING wstr copy; // We have to make a copy here incase w->length is less than the // full string pointed to by w->data. al_wstr_clone(©, w); wchar_t *copy_data = copy.data; mbstate_t mbstate = { 0 }; size_t length = wcsrtombs(NULL, (const wchar_t **)©_data, 0, &mbstate); if (length == (size_t)-1) { return false; } al_str_sized(s, (u32)length); // Excludes the null terminator. s->length = (u32)wcsrtombs(s->data, (const wchar_t **)©_data, length, &mbstate); al_assert(s->length == (u32)length); al_wstr_free(©); return true; #else (void)s; (void)w; al_assert_and_return(false); #endif } static inline void al_wstr_cat(wstr *w, wstr *c) { w->alloc = al_wstr_reserve(w, ((w->length + c->length) + 1) * sizeof(wchar_t)); al_memcpy(w->data + w->length, c->data, c->length * sizeof(wchar_t)); w->length += c->length; w->data[w->length] = L'\0'; } static inline void al_wstr_insert(wstr *w, u32 index, wstr *c) { w->alloc = al_wstr_reserve(w, ((w->length + c->length) + 1) * sizeof(wchar_t)); wchar_t *pos = w->data + index; if (index < w->length) { al_memmove(pos + c->length, pos, (w->length - index) * sizeof(wchar_t)); } al_memcpy(pos, c->data, c->length * sizeof(wchar_t)); w->length += c->length; w->data[w->length] = L'\0'; } static inline void al_wstr_remove_at(wstr *w, u32 index) { al_assert(index < w->length); if (index != --w->length) { al_memmove(w->data + index, w->data + index + 1, (w->length - index) * sizeof(wchar_t)); } w->data[w->length] = L'\0'; } static inline s32 al_wstr_width(wstr *w) { #ifdef AL_HAVE_WIDE_STRING_WIDTH return wcswidth(w->data, w->length); #else (void)w; al_assert_and_return(0); #endif } static inline s32 al_wstr_cmp(wstr *w, wstr *c, u32 start, u32 length) { #ifdef AL_HAVE_WIDE_STRING if (length > c->length) return 1; else if (length + start > w->length) return -1; return wcsncmp(&al_wstr_at(w, start), c->data, length); #else (void)w; (void)c; (void)start; (void)len; al_assert_and_return(0); #endif } static inline bool al_wstr_eq(wstr *w, wstr *c) { return al_wstr_cmp(w, c, 0, MAX(w->length, c->length)) == 0; } static inline u32 al_wstr_find(wstr *w, wchar_t c) { for (u32 i = 0; i < w->length; i++) { if (al_wstr_at(w, i) == c) { return i; } } return AL_WSTR_NO_POS; } static inline u32 al_wstr_rfind(wstr *w, wchar_t c) { for (u32 i = w->length; --i > 0;) { if (al_wstr_at(w, i) == c) { return i; } } return AL_WSTR_NO_POS; } static inline bool al_wstr_tok(wstr *result, wchar_t delim) { u32 index = al_wstr_find(result, delim); if (index == AL_WSTR_NO_POS) { return false; } *result = al_wstr_substr(result, index + 1, result->length); return true; } static inline s64 al_wstr_to_long(wstr *w, u32 base, bool *error) { str s; al_str_from_wstr(&s, w); s64 num = al_str_to_long(&s, base, error); al_str_free(&s); return num; } #endif // _AL_WSTR_H