#include "cst_util.h" #include #include #include #include static int buf_reserve(cst_buf_t *b, size_t extra) { if (b->len + extra + 1 <= b->cap) return 0; size_t nc = b->cap ? b->cap : 256; while (nc < b->len + extra + 1) nc *= 2; char *nb = realloc(b->buf, nc); if (!nb) return -1; b->buf = nb; b->cap = nc; return 0; } int cst_buf_append(cst_buf_t *b, const char *s, size_t n) { if (!b) return -1; if (buf_reserve(b, n) != 0) return -1; if (n) memcpy(b->buf + b->len, s, n); b->len += n; b->buf[b->len] = 0; return 0; } int cst_buf_puts(cst_buf_t *b, const char *s) { return cst_buf_append(b, s ? s : "", s ? strlen(s) : 0); } int cst_buf_printf(cst_buf_t *b, const char *fmt, ...) { va_list ap; va_start(ap, fmt); va_list ap2; va_copy(ap2, ap); int n = vsnprintf(NULL, 0, fmt, ap); va_end(ap); if (n < 0 || buf_reserve(b, (size_t)n) != 0) { va_end(ap2); return -1; } vsnprintf(b->buf + b->len, (size_t)n + 1, fmt, ap2); va_end(ap2); b->len += (size_t)n; return 0; } void cst_buf_free(cst_buf_t *b) { if (!b) return; free(b->buf); b->buf = NULL; b->len = b->cap = 0; } char *cst_buf_steal(cst_buf_t *b) { if (buf_reserve(b, 0) != 0) return NULL; char *s = b->buf; b->buf = NULL; b->len = b->cap = 0; return s; } size_t cst_utf8_floor(const char *s, size_t n) { if (!s || n == 0) return 0; size_t i = n; /* walk back over at most 3 continuation bytes to the lead byte */ size_t back = 0; while (i > 0 && back < 3 && ((unsigned char)s[i - 1] & 0xC0) == 0x80) { i--; back++; } if (i == 0) return n; unsigned char lead = (unsigned char)s[i - 1]; size_t need = 1; if (lead >= 0xF0) need = 4; else if (lead >= 0xE0) need = 3; else if (lead >= 0xC0) need = 2; if (need == 1) return n; /* ASCII lead; any continuation bytes after it are invalid anyway and get sanitized later */ if (back + 1 >= need) return n; return i - 1; /* sequence incomplete: cut before lead byte */ } static size_t utf8_seq_ok(const unsigned char *p, size_t left) { unsigned char c = p[0]; size_t need; unsigned cp; if (c < 0x80) return 1; if ((c & 0xE0) == 0xC0) { need = 2; cp = c & 0x1F; } else if ((c & 0xF0) == 0xE0) { need = 3; cp = c & 0x0F; } else if ((c & 0xF8) == 0xF0) { need = 4; cp = c & 0x07; } else { return 0; } if (left < need) return 0; for (size_t i = 1; i < need; i++) { if ((p[i] & 0xC0) != 0x80) return 0; cp = (cp << 6) | (p[i] & 0x3F); } if ((need == 2 && cp < 0x80) || (need == 3 && cp < 0x800) || (need == 4 && (cp < 0x10000 || cp > 0x10FFFF)) || (cp >= 0xD800 && cp <= 0xDFFF)) return 0; return need; } char *cst_utf8_sanitize(const char *s, size_t n, size_t *out_len) { char *out = malloc(n + 1); if (!out) return NULL; size_t o = 0; const unsigned char *p = (const unsigned char *)(s ? s : ""); size_t i = 0; while (i < n) { size_t k = utf8_seq_ok(p + i, n - i); if (k == 0) { out[o++] = '?'; i++; continue; } memcpy(out + o, p + i, k); o += k; i += k; } out[o] = 0; if (out_len) *out_len = o; return out; } uint64_t cst_fnv1a64(uint64_t h, const void *data, size_t n) { const unsigned char *p = data; for (size_t i = 0; i < n; i++) { h ^= p[i]; h *= 1099511628211ULL; } return h; }