Files
CST-agent/vendor/sashidown/src/line_index.c
T
WHCandCursor 1f1fcba7ad Initial CST-Agent: portable Linux/Termux/Windows coding agent.
Vendored sashidown, OpenAI-compatible env (OPENAI_*), and bootstrap scripts.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-07 22:42:32 +08:00

185 lines
4.5 KiB
C

#include "line_index.h"
#include <stdlib.h>
#include <string.h>
#define LINE_IX_DYNAMIC_INIT 256
static size_t scan_line_end(const char *input, size_t len, size_t pos)
{
const char *hit = memchr(input + pos, '\n', len - pos);
return hit ? (size_t)(hit - input) : len;
}
static size_t scan_skip_line(const char *input, size_t len, size_t pos)
{
pos = scan_line_end(input, len, pos);
if (pos < len)
pos++;
return pos;
}
static int line_index_ensure(line_index_t *ix, uint32_t need_slots)
{
if (need_slots <= ix->cap)
return 0;
if (!ix->owned) {
ix->valid = 0;
return -1;
}
uint32_t cap = ix->cap ? ix->cap : LINE_IX_DYNAMIC_INIT;
while (cap < need_slots)
cap *= 2;
uint32_t *p = (uint32_t *)realloc(ix->off, (size_t)cap * sizeof(uint32_t));
if (!p) {
ix->valid = 0;
return -1;
}
ix->off = p;
ix->cap = cap;
return 0;
}
int line_index_init_dynamic(line_index_t *ix, uint32_t initial_cap)
{
if (!ix)
return -1;
if (initial_cap < 2)
initial_cap = LINE_IX_DYNAMIC_INIT;
uint32_t *off = (uint32_t *)malloc((size_t)initial_cap * sizeof(uint32_t));
if (!off)
return -1;
ix->off = off;
ix->cap = initial_cap;
ix->n = 1;
ix->off[0] = 0;
ix->off[1] = 0;
ix->cur = 0;
ix->valid = 1;
ix->owned = 1;
return 0;
}
void line_index_free(line_index_t *ix)
{
if (!ix)
return;
if (ix->owned) {
free(ix->off);
ix->off = NULL;
ix->owned = 0;
ix->cap = 0;
}
}
void line_index_build(const char *input, size_t len, line_index_t *ix)
{
ix->n = 1;
ix->off[0] = 0;
ix->cur = 0;
ix->valid = 1;
const char *start = input;
const char *end = input + len;
const char *hit;
while ((hit = memchr(start, '\n', (size_t)(end - start))) != NULL) {
if (ix->n + 1 >= ix->cap) {
if (line_index_ensure(ix, ix->n + 2) != 0)
return;
}
ix->off[ix->n++] = (uint32_t)(hit - input) + 1;
start = hit + 1;
}
if (ix->n >= ix->cap) {
if (line_index_ensure(ix, ix->n + 1) != 0)
return;
}
ix->off[ix->n] = (uint32_t)len;
}
size_t line_index_extend(line_index_t *ix, const char *input,
size_t old_len, size_t new_len)
{
if (!ix || !ix->valid || new_len <= old_len)
return 0;
size_t last_nl = 0;
const char *start = input + old_len;
const char *end = input + new_len;
const char *hit;
while ((hit = memchr(start, '\n', (size_t)(end - start))) != NULL) {
if (ix->n + 1 >= ix->cap) {
if (line_index_ensure(ix, ix->n + 2) != 0)
return last_nl;
}
last_nl = (size_t)(hit - input) + 1;
ix->off[ix->n++] = (uint32_t)last_nl;
start = hit + 1;
}
if (ix->n >= ix->cap) {
if (line_index_ensure(ix, ix->n + 1) != 0)
return last_nl;
}
ix->off[ix->n] = (uint32_t)new_len;
return last_nl;
}
static uint32_t line_no(const line_index_t *ix, size_t pos)
{
uint32_t lo = 0;
uint32_t hi = ix->n;
while (lo + 1 < hi) {
uint32_t mid = (lo + hi) / 2;
if (ix->off[mid] <= pos)
lo = mid;
else
hi = mid;
}
return lo;
}
void line_index_seek(line_index_t *ix, size_t pos)
{
if (!ix || !ix->valid)
return;
ix->cur = line_no(ix, pos);
}
static uint32_t resolve_line(line_index_t *ix, size_t pos)
{
/* Sequential hot path: render walks lines in order. */
if (ix->cur < ix->n && ix->off[ix->cur] == (uint32_t)pos)
return ix->cur;
if (ix->cur + 1 < ix->n && ix->off[ix->cur + 1] == (uint32_t)pos) {
ix->cur++;
return ix->cur;
}
ix->cur = line_no(ix, pos);
return ix->cur;
}
size_t line_end_ix(line_index_t *ix, const char *input, size_t len, size_t pos)
{
if (!ix || !ix->valid)
return scan_line_end(input, len, pos);
uint32_t k = resolve_line(ix, pos);
size_t next = (size_t)ix->off[k + 1];
if (next > 0 && next <= len && input[next - 1] == '\n')
return next - 1;
return next <= len ? next : len;
}
size_t skip_line_ix(line_index_t *ix, const char *input, size_t len, size_t pos)
{
if (!ix || !ix->valid)
return scan_skip_line(input, len, pos);
uint32_t k = resolve_line(ix, pos);
size_t next = (size_t)ix->off[k + 1];
if (next > len)
return len;
/* Advance cursor to the next line start (may equal n at EOF). */
ix->cur = k + 1;
return next;
}