impotent/stc/priv/cstr_prv.c
2025-08-31 16:22:38 +03:00

292 lines
9.3 KiB
C

/* MIT License
*
* Copyright (c) 2025 Tyge Løvset
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// ------------------- STC_CSTR_CORE --------------------
#if !defined STC_CSTR_CORE_C_INCLUDED && \
(defined i_implement || defined STC_CSTR_CORE)
#define STC_CSTR_CORE_C_INCLUDED
void cstr_drop(const cstr* self) {
if (cstr_is_long(self))
cstr_l_drop(self);
}
cstr* cstr_take(cstr* self, const cstr s) {
if (cstr_is_long(self) && self->lon.data != s.lon.data)
cstr_l_drop(self);
*self = s;
return self;
}
size_t cstr_hash(const cstr *self) {
csview sv = cstr_sv(self);
return c_hash_str(sv.buf);
}
isize cstr_find_sv(const cstr* self, csview search) {
csview sv = cstr_sv(self);
char* res = c_strnstrn(sv.buf, sv.size, search.buf, search.size);
return res ? (res - sv.buf) : c_NPOS;
}
char* _cstr_internal_move(cstr* self, const isize pos1, const isize pos2) {
cstr_buf b = cstr_getbuf(self);
if (pos1 != pos2) {
const isize newlen = (b.size + pos2 - pos1);
if (newlen > b.cap)
b.data = cstr_reserve(self, b.size*3/2 + pos2 - pos1);
c_memmove(&b.data[pos2], &b.data[pos1], b.size - pos1);
_cstr_set_size(self, newlen);
}
return b.data;
}
char* _cstr_init(cstr* self, const isize len, const isize cap) {
if (cap > cstr_s_cap) {
self->lon.data = (char *)c_malloc(cap + 1);
cstr_l_set_size(self, len);
cstr_l_set_cap(self, cap);
return self->lon.data;
}
cstr_s_set_size(self, len);
return self->sml.data;
}
char* cstr_reserve(cstr* self, const isize cap) {
if (cstr_is_long(self)) {
if (cap > cstr_l_cap(self)) {
self->lon.data = (char *)c_realloc(self->lon.data, cstr_l_cap(self) + 1, cap + 1);
cstr_l_set_cap(self, cap);
}
return self->lon.data;
}
/* from short to long: */
if (cap > cstr_s_cap) {
char* data = (char *)c_malloc(cap + 1);
const isize len = cstr_s_size(self);
/* copy full short buffer to emulate realloc() */
c_memcpy(data, self->sml.data, c_sizeof self->sml);
self->lon.data = data;
self->lon.size = (size_t)len;
cstr_l_set_cap(self, cap);
return data;
}
return self->sml.data;
}
char* cstr_resize(cstr* self, const isize size, const char value) {
cstr_buf b = cstr_getbuf(self);
if (size > b.size) {
if (size > b.cap && (b.data = cstr_reserve(self, size)) == NULL)
return NULL;
c_memset(b.data + b.size, value, size - b.size);
}
_cstr_set_size(self, size);
return b.data;
}
isize cstr_find_at(const cstr* self, const isize pos, const char* search) {
csview sv = cstr_sv(self);
if (pos > sv.size) return c_NPOS;
const char* res = strstr((char*)sv.buf + pos, search);
return res ? (res - sv.buf) : c_NPOS;
}
char* cstr_assign_n(cstr* self, const char* str, const isize len) {
char* d = cstr_reserve(self, len);
if (d) { _cstr_set_size(self, len); c_memmove(d, str, len); }
return d;
}
char* cstr_append_n(cstr* self, const char* str, const isize len) {
cstr_buf b = cstr_getbuf(self);
if (b.size + len > b.cap) {
const size_t off = (size_t)(str - b.data);
b.data = cstr_reserve(self, b.size*3/2 + len);
if (b.data == NULL) return NULL;
if (off <= (size_t)b.size) str = b.data + off; /* handle self append */
}
c_memcpy(b.data + b.size, str, len);
_cstr_set_size(self, b.size + len);
return b.data;
}
cstr cstr_from_replace(csview in, csview search, csview repl, int32_t count) {
cstr out = cstr_init();
isize from = 0; char* res;
if (count == 0) count = INT32_MAX;
if (search.size)
while (count-- && (res = c_strnstrn(in.buf + from, in.size - from, search.buf, search.size))) {
const isize pos = (res - in.buf);
cstr_append_n(&out, in.buf + from, pos - from);
cstr_append_n(&out, repl.buf, repl.size);
from = pos + search.size;
}
cstr_append_n(&out, in.buf + from, in.size - from);
return out;
}
void cstr_erase(cstr* self, const isize pos, isize len) {
cstr_buf b = cstr_getbuf(self);
if (len > b.size - pos) len = b.size - pos;
c_memmove(&b.data[pos], &b.data[pos + len], b.size - (pos + len));
_cstr_set_size(self, b.size - len);
}
void cstr_shrink_to_fit(cstr* self) {
cstr_buf b = cstr_getbuf(self);
if (b.size == b.cap)
return;
if (b.size > cstr_s_cap) {
self->lon.data = (char *)c_realloc(self->lon.data, cstr_l_cap(self) + 1, b.size + 1);
cstr_l_set_cap(self, b.size);
} else if (b.cap > cstr_s_cap) {
c_memcpy(self->sml.data, b.data, b.size + 1);
cstr_s_set_size(self, b.size);
c_free(b.data, b.cap + 1);
}
}
#endif // STC_CSTR_CORE_C_INCLUDED
// ------------------- STC_CSTR_IO --------------------
#if !defined STC_CSTR_IO_C_INCLUDED && \
(defined i_import || defined STC_CSTR_IO)
#define STC_CSTR_IO_C_INCLUDED
char* cstr_append_uninit(cstr *self, isize len) {
cstr_buf b = cstr_getbuf(self);
if (b.size + len > b.cap && (b.data = cstr_reserve(self, b.size*3/2 + len)) == NULL)
return NULL;
_cstr_set_size(self, b.size + len);
return b.data + b.size;
}
bool cstr_getdelim(cstr *self, const int delim, FILE *fp) {
int c = fgetc(fp);
if (c == EOF)
return false;
isize pos = 0;
cstr_buf b = cstr_getbuf(self);
for (;;) {
if (c == delim || c == EOF) {
_cstr_set_size(self, pos);
return true;
}
if (pos == b.cap) {
_cstr_set_size(self, pos);
char* data = cstr_reserve(self, (b.cap = b.cap*3/2 + 16));
b.data = data;
}
b.data[pos++] = (char) c;
c = fgetc(fp);
}
}
isize cstr_vfmt(cstr* self, isize start, const char* fmt, va_list args) {
va_list args2;
va_copy(args2, args);
const int n = vsnprintf(NULL, 0ULL, fmt, args);
vsnprintf(cstr_reserve(self, start + n) + start, (size_t)n+1, fmt, args2);
va_end(args2);
_cstr_set_size(self, start + n);
return n;
}
cstr cstr_from_fmt(const char* fmt, ...) {
cstr s = cstr_init();
va_list args;
va_start(args, fmt);
cstr_vfmt(&s, 0, fmt, args);
va_end(args);
return s;
}
isize cstr_append_fmt(cstr* self, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
const isize n = cstr_vfmt(self, cstr_size(self), fmt, args);
va_end(args);
return n;
}
/* NB! self-data in args is UB */
isize cstr_printf(cstr* self, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
const isize n = cstr_vfmt(self, 0, fmt, args);
va_end(args);
return n;
}
#endif // STC_CSTR_IO_C_INCLUDED
// ------------------- STC_CSTR_UTF8 --------------------
#if !defined STC_CSTR_UTF8_C_INCLUDED && \
(defined i_import || defined STC_CSTR_UTF8 || defined STC_UTF8_PRV_C_INCLUDED)
#define STC_CSTR_UTF8_C_INCLUDED
#include <ctype.h>
void cstr_u8_erase(cstr* self, const isize u8pos, const isize u8len) {
csview b = cstr_sv(self);
csview span = utf8_subview(b.buf, u8pos, u8len);
c_memmove((void *)&span.buf[0], &span.buf[span.size], b.size - span.size - (span.buf - b.buf));
_cstr_set_size(self, b.size - span.size);
}
bool cstr_u8_valid(const cstr* self)
{ return utf8_valid(cstr_str(self)); }
static int toLower(int c)
{ return c >= 'A' && c <= 'Z' ? c + 32 : c; }
static int toUpper(int c)
{ return c >= 'a' && c <= 'z' ? c - 32 : c; }
static struct {
int (*conv_asc)(int);
uint32_t (*conv_utf)(uint32_t);
}
fn_tocase[] = {{toLower, utf8_casefold},
{toLower, utf8_tolower},
{toUpper, utf8_toupper}};
cstr cstr_tocase_sv(csview sv, int k) {
cstr out = {0};
char *buf = cstr_reserve(&out, sv.size*3/2);
isize sz = 0;
utf8_decode_t d = {.state=0};
const char* end = sv.buf + sv.size;
while (sv.buf < end) {
sv.buf += utf8_decode_codepoint(&d, sv.buf, end);
if (d.codep < 0x80)
buf[sz++] = (char)fn_tocase[k].conv_asc((int)d.codep);
else {
uint32_t cp = fn_tocase[k].conv_utf(d.codep);
sz += utf8_encode(buf + sz, cp);
}
}
_cstr_set_size(&out, sz);
cstr_shrink_to_fit(&out);
return out;
}
#endif // i_import STC_CSTR_UTF8_C_INCLUDED