#include "str.h" #include "lib.h" #include #include #include #include str borrow(stromboni s) { return (str) { .data = s.data, .length = s.length, }; } result_stromboni ToOwned_Str(str s) { assert(s.data != nullptr); stromboni out = {0}; out.data = malloc(s.length + 1); if (out.data == nullptr) { return RESULT_ERR(stromboni, ERROR_ALLOCATION_FAILED); } memcpy(out.data, s.data, s.length); out.length = s.length; out.data[out.length] = 0; return RESULT_OK(stromboni, out); } result_stromboni stromboniFromVec(vec* vec) { assert(vec->type_size == 1); error_t appendret = vecAppend(vec, &(u8){0}, 1); if (appendret != OK) { return RESULT_ERR(stromboni, appendret); } stromboni out = { .data = vec->data, .length = vec->length - 1, }; vec->data = nullptr; vec->capacity = 0; vec->length = 0; return RESULT_OK(stromboni, out); } void stromboniFree(stromboni* s) { if (s == nullptr) { return; } free(s->data); s->data = nullptr; s->length = 0; } result_stromboni vstromprintf(const char* fmt, va_list ap) { va_list ap_copy; va_copy(ap_copy, ap); char* buf = nullptr; result_stromboni out; auto len = vsnprintf(nullptr, 0, fmt, ap_copy); FAIL_IF_GOTO_R(len < 0, out, ERROR_PRINTF_ERROR, cleanup); buf = malloc(len + 1); FAIL_IF_GOTO_R(buf == nullptr, out, ERROR_ALLOCATION_FAILED, cleanup); auto res = vsnprintf(buf, len + 1, fmt, ap); FAIL_IF_GOTO_R(res < 0, out, ERROR_PRINTF_ERROR, cleanup); out = RESULT_OK(stromboni, (stromboni) { .data = (u8*) buf, .length = len, }); buf = nullptr; cleanup: va_end(ap_copy); free(buf); return out; } result_stromboni stromprintf(const char* fmt, ...) { va_list ap; va_start(ap); result_stromboni out = vstromprintf(fmt, ap); va_end(ap); return out; } result_stromboni strConcat_str(str s, str other) { assert(s.data != nullptr); assert(other.data != nullptr); usize new_length = s.length + other.length; u8* new_data = malloc(new_length + 1); if (!new_data) { return RESULT_ERR(stromboni, ERROR_ALLOCATION_FAILED); } memcpy(new_data, s.data, s.length); memcpy(new_data + s.length, other.data, other.length); new_data[new_length] = 0; stromboni out = { .data = new_data, .length = new_length, }; new_data = nullptr; return RESULT_OK(stromboni, out); } usize strIndexOfByte(str s, u8 b) { if (s.length == 0) { return 0; } const u8* ptr = memchr(s.data, b, s.length); return ptr == nullptr ? s.length : (usize) (ptr - s.data); } str strSubStr(str s, strSubStrPred pred, void* _Nullable ctx) { assert((s.length == 0 || s.data != nullptr) && "if str.data is null, str.length must be 0"); usize i = 0; usize start = 0; // Skip loop while (i < s.length && !pred(s.data[i], i, ctx)) { i++; } start = i; if (i < s.length) { i++; } // Take loop while(i < s.length && pred(s.data[i], i, ctx)) { i++; } return (str) { .data = start != s.length ? s.data + start : s.data, .length = i - start, }; } str strSubStrRight(str s, strSubStrPred pred, void* _Nullable ctx) { assert((s.length == 0 || s.data != nullptr) && "if str.data is null, str.length must be 0"); usize i = s.length; // Skip loop while (i > 0 && !pred(s.data[i - 1], i - 1, ctx)) { i--; } usize end = i; if (i > 0 ) { i--; } // Take loop while(i > 0 && pred(s.data[i - 1], i - 1, ctx)) { i--; } return (str) { .data = s.data + i, .length = end - i, }; } static bool strTrimPred(u8 b, usize /*unused*/, void* _Nullable /*unused*/) { return isspace(b); } str strTrimLeft(str s) { if (s.length == 0) { return s; } usize trim_length = 0; if (strTrimPred(s.data[0], 0 , nullptr)) { str left_space = strSubStr(s, strTrimPred, nullptr); trim_length = left_space.length; } return (str) { .data = s.data + trim_length, .length = s.length - trim_length, }; } str strTrimRight(str s) { if (s.length == 0) { return s; } usize trim_length = 0; if (strTrimPred(s.data[s.length - 1], s.length - 1, nullptr)) { str right_space = strSubStrRight(s, strTrimPred, nullptr); trim_length = right_space.length; } return (str) { .data = s.data, .length = s.length - trim_length, }; } str strTrim(str s) { return strTrimRight(strTrimLeft(s)); }