AxlTextBuffer — Editable Text Store
See AxlData — Data Structures for an overview of all data modules.
A growable, editable byte buffer with an integral line index, tuned for
an interactive text editor: load a file once, then many small
inserts/deletes near a moving cursor, with O(log n) byte-offset ↔ line
mapping on every keystroke and repaint. Storage is a gap buffer; the
line index is maintained incrementally (never a full rescan) and queried
by binary search. The store is byte-oriented — '\n' is the only
special byte — so the caller owns any UTF-8 / codepoint policy.
Header: <axl/axl-text-buffer.h>
API Reference
Typedefs
-
typedef struct AxlTextBuffer AxlTextBuffer
axl-text-buffer.h:
A growable, editable byte buffer with an integral line index, tuned for an interactive text editor: load a file once, then many small inserts/deletes near a moving cursor, and — every keystroke and every repaint — map between byte offsets and line numbers.
Storage is a gap buffer (O(1) amortized insert/delete at the gap; moving the gap to the next edit site is a single memmove). Because the bytes are not contiguous, callers read ranges out via axl_text_buffer_get rather than holding a pointer.
The store is byte-oriented and encoding-agnostic: ‘
’ (0x0A) is the only special byte (the line delimiter). Any UTF-8 / codepoint policy is the caller’s to apply on top.
Line index: a sorted index of newline offsets, maintained incrementally on every edit (never a full rescan). Offset->line and line->bounds are O(log n) binary searches — the editor calls them on every keystroke and once per visible line per frame.
Functions
-
AxlTextBuffer *axl_text_buffer_new(size_t initial_capacity)
Create an empty text buffer.
initial_capacityis a hint for the initial gap size (0 = a small default); the buffer grows as needed.- Parameters:
initial_capacity – initial byte capacity hint (0 = default)
- Returns:
new buffer (length 0, line count 1), or NULL on OOM. Free with axl_text_buffer_free().
-
void axl_text_buffer_free(AxlTextBuffer *tb)
Free a text buffer and its storage. NULL-safe.
- Parameters:
tb – buffer (NULL-safe)
-
int axl_text_buffer_set_bytes(AxlTextBuffer *tb, const char *data, size_t len)
Replace the entire contents with
data.Rebuilds the line index from scratch (this is the one O(n) operation — it is a full load, not an incremental edit).
- Parameters:
tb – buffer
data – replacement bytes (may be NULL iff
lenis 0)len – number of bytes
- Returns:
AXL_OK on success, AXL_ERR on OOM or invalid args.
-
size_t axl_text_buffer_length(const AxlTextBuffer *tb)
Total byte length of the buffer’s contents.
- Parameters:
tb – buffer
-
int axl_text_buffer_insert(AxlTextBuffer *tb, size_t offset, const char *data, size_t len)
Insert
lenbytes at byteoffset.offsetis clamped to the current length (so length is an append). The line index is updated incrementally.- Parameters:
tb – buffer
offset – byte offset to insert at (clamped to length)
data – bytes to insert
len – number of bytes
- Returns:
AXL_OK on success, AXL_ERR on OOM or invalid args (NULL
datawithlen> 0).
-
int axl_text_buffer_delete(AxlTextBuffer *tb, size_t offset, size_t len)
Delete
lenbytes starting at byteoffset.offsetpast the end deletes nothing;lenis clamped so the range never extends past the end. The line index is updated incrementally.- Parameters:
tb – buffer
offset – byte offset to delete from
len – number of bytes to delete
- Returns:
AXL_OK on success (including the no-op cases), AXL_ERR on invalid args.
-
size_t axl_text_buffer_get(const AxlTextBuffer *tb, size_t offset, size_t len, char *out, size_t cap)
Copy a byte range out into
out.Copies up to min(
len,cap) bytes of [offset, offset+len), clamped to the buffer length. A gap buffer is not contiguous, so this copy-out is how callers read content.- Parameters:
tb – buffer
offset – byte offset to read from
len – bytes requested
out – destination buffer
cap – capacity of
out
- Returns:
number of bytes copied (may be <
lenat end-of-buffer or whencapis the limit).
-
int axl_text_buffer_byte_at(const AxlTextBuffer *tb, size_t offset)
Read a single byte.
- Parameters:
tb – buffer
offset – byte offset
- Returns:
the byte (0–255) at
offset, or -1 ifoffsetis out of range.
-
size_t axl_text_buffer_line_count(const AxlTextBuffer *tb)
Number of lines.
Defined as (newline count + 1): an empty buffer is 1 line, and a trailing ‘
’ yields a real (empty) final line.
- Parameters:
tb – buffer
-
size_t axl_text_buffer_line_of_offset(const AxlTextBuffer *tb, size_t offset)
The line number (0-based) containing byte
offset.offsetis clamped to the buffer length. A ‘
’ byte belongs to the line it terminates. O(log n).
- Parameters:
tb – buffer
offset – byte offset
- Returns:
0-based line number.
-
int axl_text_buffer_line_bounds(const AxlTextBuffer *tb, size_t line, size_t *start, size_t *end)
Byte range [start, end) of line
line.endexcludes the line’s terminating ‘
’ (so it is the offset of that ‘
‘, or the buffer length for the last line). O(log n).- Parameters:
tb – buffer
line – 0-based line number
start – [out] first byte of the line
end –
[out] one past the last byte, excluding ‘
’
- Returns:
AXL_OK if
lineis valid, AXL_ERR ifline>= line count.
-
char *axl_text_buffer_get_alloc(const AxlTextBuffer *tb, size_t offset, size_t len)
Copy a byte range out into a fresh NUL-terminated buffer.
The allocating counterpart of axl_text_buffer_get (mirrors axl_piece_tree_get_alloc): allocates (clamped length + 1) bytes, copies [offset, offset+len) clamped to the buffer, and NUL-terminates. Raw bytes (embedded NULs preserved; a C-string view stops at the first).
- Parameters:
tb – buffer
offset – byte offset
len – bytes requested (clamped to the buffer)
- Returns:
newly allocated buffer (free with axl_free; an empty range yields a 1-byte “”), or NULL on OOM / NULL
tb.
-
size_t axl_text_buffer_cp_align(const AxlTextBuffer *tb, size_t offset)
Round
offsetdown to the start of the UTF-8 codepoint that contains it (mirror of axl_piece_tree_cp_align).Returns
offsetunchanged on a codepoint boundary (or at 0 / the buffer end); otherwise steps back over continuation bytes. Use it to snap an arbitrary byte offset to a valid caret position.- Parameters:
tb – buffer
offset – byte offset
-
size_t axl_text_buffer_cp_next(const AxlTextBuffer *tb, size_t offset)
Offset of the next UTF-8 codepoint boundary after
offset(caret “move right”; clamped at the buffer end).- Parameters:
tb – buffer
offset – byte offset
-
size_t axl_text_buffer_cp_prev(const AxlTextBuffer *tb, size_t offset)
Offset of the previous UTF-8 codepoint boundary before
offset(caret “move left”; clamped at 0).- Parameters:
tb – buffer
offset – byte offset
-
bool axl_text_buffer_find(AxlTextBuffer *tb, const char *needle, size_t needle_len, size_t from_offset, uint32_t flags, AxlMatch *out)
Search for a byte substring (mirror of axl_piece_tree_find).
Finds
needlestarting the scan atfrom_offset. Forward (default) returns the lowest match with start >=from_offset;AXL_FIND_BACKWARDreturns the highest match with start <=from_offset.AXL_FIND_CASE_INSENSITIVEfolds ASCII case;AXL_FIND_WHOLE_WORDrequires non-word bytes (anything but[A-Za-z0-9_]) on both sides. Matches that straddle the internal gap are handled. Wrap-around is the caller’s job.Thin wrapper over axl_find_in_source (see AxlByteReader).
- Parameters:
tb – buffer
needle – bytes to find
needle_len – length of
needlefrom_offset – where to start scanning
flags – AxlFindFlags
out – [out] match on success
- Returns:
true and fills
outon a match; false if not found (orneedle_lenis 0).