408 lines
No EOL
9 KiB
C
408 lines
No EOL
9 KiB
C
#include "str.h"
|
|
|
|
#include "warnings/colla_warn_beg.h"
|
|
|
|
#include "arena.h"
|
|
#include "format.h"
|
|
#include "tracelog.h"
|
|
|
|
#if COLLA_WIN
|
|
|
|
#include <windows.h>
|
|
|
|
#else
|
|
|
|
#include <wchar.h>
|
|
|
|
#endif
|
|
|
|
#if COLLA_TCC
|
|
#include "tcc/colla_tcc.h"
|
|
#endif
|
|
|
|
// == STR_T ========================================================
|
|
|
|
str_t strInit(arena_t *arena, const char *buf) {
|
|
return buf ? strInitLen(arena, buf, strlen(buf)) : STR_EMPTY;
|
|
}
|
|
|
|
str_t strInitLen(arena_t *arena, const char *buf, usize len) {
|
|
if (!buf || !len) return STR_EMPTY;
|
|
|
|
str_t out = {
|
|
.buf = alloc(arena, char, len + 1),
|
|
.len = len
|
|
};
|
|
|
|
memcpy(out.buf, buf, len);
|
|
|
|
return out;
|
|
}
|
|
|
|
str_t strInitView(arena_t *arena, strview_t view) {
|
|
return strInitLen(arena, view.buf, view.len);
|
|
}
|
|
|
|
str_t strFmt(arena_t *arena, const char *fmt, ...) {
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
str_t out = strFmtv(arena, fmt, args);
|
|
va_end(args);
|
|
return out;
|
|
}
|
|
|
|
str_t strFmtv(arena_t *arena, const char *fmt, va_list args) {
|
|
va_list vcopy;
|
|
va_copy(vcopy, args);
|
|
// stb_vsnprintf returns the length + null_term
|
|
int len = fmtBufferv(NULL, 0, fmt, vcopy);
|
|
va_end(vcopy);
|
|
|
|
char *buffer = alloc(arena, char, len + 1);
|
|
fmtBufferv(buffer, len + 1, fmt, args);
|
|
|
|
return (str_t) { .buf = buffer, .len = (usize)len };
|
|
}
|
|
|
|
str_t strFromWChar(arena_t *arena, const wchar_t *src) {
|
|
return strFromWCharLen(arena, src, 0);
|
|
}
|
|
|
|
str_t strFromWCharLen(arena_t *arena, const wchar_t *src, usize srclen) {
|
|
if (!src) return STR_EMPTY;
|
|
if (!srclen) srclen = wcslen(src);
|
|
|
|
str_t out = {0};
|
|
|
|
#if COLLA_WIN
|
|
int outlen = WideCharToMultiByte(
|
|
CP_UTF8, 0,
|
|
src, (int)srclen,
|
|
NULL, 0,
|
|
NULL, NULL
|
|
);
|
|
|
|
if (outlen == 0) {
|
|
unsigned long error = GetLastError();
|
|
if (error == ERROR_NO_UNICODE_TRANSLATION) {
|
|
err("couldn't translate wide string (%S) to utf8, no unicode translation", src);
|
|
}
|
|
else {
|
|
err("couldn't translate wide string (%S) to utf8, %u", error);
|
|
}
|
|
|
|
return STR_EMPTY;
|
|
}
|
|
|
|
out.buf = alloc(arena, char, outlen + 1);
|
|
WideCharToMultiByte(
|
|
CP_UTF8, 0,
|
|
src, (int)srclen,
|
|
out.buf, outlen,
|
|
NULL, NULL
|
|
);
|
|
|
|
out.len = outlen;
|
|
|
|
#elif COLLA_LIN
|
|
fatal("strFromWChar not implemented yet!");
|
|
#endif
|
|
|
|
return out;
|
|
}
|
|
|
|
bool strEquals(str_t a, str_t b) {
|
|
return strCompare(a, b) == 0;
|
|
}
|
|
|
|
int strCompare(str_t a, str_t b) {
|
|
return a.len == b.len ?
|
|
memcmp(a.buf, b.buf, a.len) :
|
|
(int)(a.len - b.len);
|
|
}
|
|
|
|
str_t strDup(arena_t *arena, str_t src) {
|
|
return strInitLen(arena, src.buf, src.len);
|
|
}
|
|
|
|
bool strIsEmpty(str_t ctx) {
|
|
return ctx.len == 0 || ctx.buf == NULL;
|
|
}
|
|
|
|
void strReplace(str_t *ctx, char from, char to) {
|
|
if (!ctx) return;
|
|
char *buf = ctx->buf;
|
|
for (usize i = 0; i < ctx->len; ++i) {
|
|
buf[i] = buf[i] == from ? to : buf[i];
|
|
}
|
|
}
|
|
|
|
strview_t strSub(str_t ctx, usize from, usize to) {
|
|
if (to > ctx.len) to = ctx.len;
|
|
if (from > to) from = to;
|
|
return (strview_t){ ctx.buf + from, to - from };
|
|
}
|
|
|
|
void strLower(str_t *ctx) {
|
|
char *buf = ctx->buf;
|
|
for (usize i = 0; i < ctx->len; ++i) {
|
|
buf[i] = (buf[i] >= 'A' && buf[i] <= 'Z') ?
|
|
buf[i] += 'a' - 'A' :
|
|
buf[i];
|
|
}
|
|
}
|
|
|
|
void strUpper(str_t *ctx) {
|
|
char *buf = ctx->buf;
|
|
for (usize i = 0; i < ctx->len; ++i) {
|
|
buf[i] = (buf[i] >= 'a' && buf[i] <= 'z') ?
|
|
buf[i] -= 'a' - 'A' :
|
|
buf[i];
|
|
}
|
|
}
|
|
|
|
str_t strToLower(arena_t *arena, str_t ctx) {
|
|
strLower(&ctx);
|
|
return strDup(arena, ctx);
|
|
}
|
|
|
|
str_t strToUpper(arena_t *arena, str_t ctx) {
|
|
strUpper(&ctx);
|
|
return strDup(arena, ctx);
|
|
}
|
|
|
|
|
|
// == STRVIEW_T ====================================================
|
|
|
|
strview_t strvInit(const char *cstr) {
|
|
return (strview_t){
|
|
.buf = cstr,
|
|
.len = cstr ? strlen(cstr) : 0,
|
|
};
|
|
}
|
|
|
|
strview_t strvInitLen(const char *buf, usize size) {
|
|
return (strview_t){
|
|
.buf = buf,
|
|
.len = size,
|
|
};
|
|
}
|
|
|
|
strview_t strvInitStr(str_t str) {
|
|
return (strview_t){
|
|
.buf = str.buf,
|
|
.len = str.len
|
|
};
|
|
}
|
|
|
|
|
|
bool strvIsEmpty(strview_t ctx) {
|
|
return ctx.len == 0 || !ctx.buf;
|
|
}
|
|
|
|
bool strvEquals(strview_t a, strview_t b) {
|
|
return strvCompare(a, b) == 0;
|
|
}
|
|
|
|
int strvCompare(strview_t a, strview_t b) {
|
|
return a.len == b.len ?
|
|
memcmp(a.buf, b.buf, a.len) :
|
|
(int)(a.len - b.len);
|
|
}
|
|
|
|
char strvFront(strview_t ctx) {
|
|
return ctx.len > 0 ? ctx.buf[0] : '\0';
|
|
}
|
|
|
|
char strvBack(strview_t ctx) {
|
|
return ctx.len > 0 ? ctx.buf[ctx.len - 1] : '\0';
|
|
}
|
|
|
|
|
|
wchar_t *strvToWChar(arena_t *arena, strview_t ctx, usize *outlen) {
|
|
wchar_t *out = NULL;
|
|
int len = 0;
|
|
|
|
if (strvIsEmpty(ctx)) {
|
|
goto error;
|
|
}
|
|
|
|
#if COLLA_WIN
|
|
len = MultiByteToWideChar(
|
|
CP_UTF8, 0,
|
|
ctx.buf, (int)ctx.len,
|
|
NULL, 0
|
|
);
|
|
|
|
if (len == 0) {
|
|
unsigned long error = GetLastError();
|
|
if (error == ERROR_NO_UNICODE_TRANSLATION) {
|
|
err("couldn't translate string (%v) to a wide string, no unicode translation", ctx);
|
|
}
|
|
else {
|
|
err("couldn't translate string (%v) to a wide string, %u", ctx, error);
|
|
}
|
|
|
|
goto error;
|
|
}
|
|
|
|
out = alloc(arena, wchar_t, len + 1);
|
|
|
|
MultiByteToWideChar(
|
|
CP_UTF8, 0,
|
|
ctx.buf, (int)ctx.len,
|
|
out, len
|
|
);
|
|
|
|
#elif COLLA_LIN
|
|
fatal("strFromWChar not implemented yet!");
|
|
#endif
|
|
|
|
error:
|
|
if (outlen) {
|
|
*outlen = (usize)len;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
TCHAR *strvToTChar(arena_t *arena, strview_t str) {
|
|
#if UNICODE
|
|
return strvToWChar(arena, str, NULL);
|
|
#else
|
|
char *cstr = alloc(arena, char, str.len + 1);
|
|
memcpy(cstr, str.buf, str.len);
|
|
return cstr;
|
|
#endif
|
|
}
|
|
|
|
strview_t strvRemovePrefix(strview_t ctx, usize n) {
|
|
if (n > ctx.len) n = ctx.len;
|
|
return (strview_t){
|
|
.buf = ctx.buf + n,
|
|
.len = ctx.len - n,
|
|
};
|
|
}
|
|
|
|
strview_t strvRemoveSuffix(strview_t ctx, usize n) {
|
|
if (n > ctx.len) n = ctx.len;
|
|
return (strview_t){
|
|
.buf = ctx.buf,
|
|
.len = ctx.len - n,
|
|
};
|
|
}
|
|
|
|
strview_t strvTrim(strview_t ctx) {
|
|
return strvTrimLeft(strvTrimRight(ctx));
|
|
}
|
|
|
|
strview_t strvTrimLeft(strview_t ctx) {
|
|
strview_t out = ctx;
|
|
for (usize i = 0; i < ctx.len; ++i) {
|
|
char c = ctx.buf[i];
|
|
if (c != ' ' && (c < '\t' || c > '\r')) {
|
|
break;
|
|
}
|
|
out.buf++;
|
|
out.len--;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
strview_t strvTrimRight(strview_t ctx) {
|
|
strview_t out = ctx;
|
|
for (isize i = ctx.len - 1; i >= 0; --i) {
|
|
char c = ctx.buf[i];
|
|
if (c != ' ' && (c < '\t' || c > '\r')) {
|
|
break;
|
|
}
|
|
out.len--;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
strview_t strvSub(strview_t ctx, usize from, usize to) {
|
|
if (to > ctx.len) to = ctx.len;
|
|
if (from > to) from = to;
|
|
return (strview_t){ ctx.buf + from, to - from };
|
|
}
|
|
|
|
bool strvStartsWith(strview_t ctx, char c) {
|
|
return ctx.len > 0 && ctx.buf[0] == c;
|
|
}
|
|
|
|
bool strvStartsWithView(strview_t ctx, strview_t view) {
|
|
return ctx.len >= view.len && memcmp(ctx.buf, view.buf, view.len) == 0;
|
|
}
|
|
|
|
bool strvEndsWith(strview_t ctx, char c) {
|
|
return ctx.len > 0 && ctx.buf[ctx.len - 1] == c;
|
|
}
|
|
|
|
bool strvEndsWithView(strview_t ctx, strview_t view) {
|
|
return ctx.len >= view.len && memcmp(ctx.buf + ctx.len - view.len, view.buf, view.len) == 0;
|
|
}
|
|
|
|
bool strvContains(strview_t ctx, char c) {
|
|
for(usize i = 0; i < ctx.len; ++i) {
|
|
if(ctx.buf[i] == c) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool strvContainsView(strview_t ctx, strview_t view) {
|
|
if (ctx.len < view.len) return false;
|
|
usize end = ctx.len - view.len;
|
|
for (usize i = 0; i < end; ++i) {
|
|
if (memcmp(ctx.buf + i, view.buf, view.len) == 0) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
usize strvFind(strview_t ctx, char c, usize from) {
|
|
for (usize i = from; i < ctx.len; ++i) {
|
|
if (ctx.buf[i] == c) {
|
|
return i;
|
|
}
|
|
}
|
|
return STR_NONE;
|
|
}
|
|
|
|
usize strvFindView(strview_t ctx, strview_t view, usize from) {
|
|
if (ctx.len < view.len) return STR_NONE;
|
|
usize end = ctx.len - view.len;
|
|
for (usize i = from; i < end; ++i) {
|
|
if (memcmp(ctx.buf + i, view.buf, view.len) == 0) {
|
|
return i;
|
|
}
|
|
}
|
|
return STR_NONE;
|
|
}
|
|
|
|
usize strvRFind(strview_t ctx, char c, usize from_right) {
|
|
if (from_right > ctx.len) from_right = ctx.len;
|
|
isize end = (isize)(ctx.len - from_right);
|
|
for (isize i = end; i >= 0; --i) {
|
|
if (ctx.buf[i] == c) {
|
|
return (usize)i;
|
|
}
|
|
}
|
|
return STR_NONE;
|
|
}
|
|
|
|
usize strvRFindView(strview_t ctx, strview_t view, usize from_right) {
|
|
if (from_right > ctx.len) from_right = ctx.len;
|
|
isize end = (isize)(ctx.len - from_right);
|
|
if (end < (isize)view.len) return STR_NONE;
|
|
for (isize i = end - view.len; i >= 0; --i) {
|
|
if (memcmp(ctx.buf + i, view.buf, view.len) == 0) {
|
|
return (usize)i;
|
|
}
|
|
}
|
|
return STR_NONE;
|
|
}
|
|
|
|
#include "warnings/colla_warn_beg.h" |