rcbench
Servo and ESC test bench: panel, IOmcu and shared C
Loading...
Searching...
No Matches
text_buf.c File Reference

A text file in memory: the gap buffer, the line index, the units and the cells (text_buf.h). More...

#include "text_buf.h"
#include <string.h>

Data Structures

struct  cells_t
 Where a walk along a line's cells is. More...

Macros

#define KIND_SHIFT   30u
 The kind of a line end in an index entry's top 2 bits.
#define POS_MASK   0x3FFFFFFFu
 The offset in an index entry.
#define EDIT_ENDS   8u
 Line ends an edit can leave in the stretch it scans again.
#define EDIT_MAX   4u
 Bytes an edit takes or puts at most.

Functions

static uint8_t at (const text_buf_t *tb, size_t i)
 Byte i of the text, read across the gap.
static size_t decode (const text_buf_t *tb, size_t i, uint32_t *cp)
 Decode the UTF-8 sequence at i, as gfx_utf8_next() does.
static size_t encode (uint32_t cp, uint8_t *out)
 Encode cp as UTF-8.
static void move_gap (text_buf_t *tb, size_t p)
 Move the gap to text offset p.
static size_t entry_pos (uint32_t e)
 The text offset an index entry holds.
static text_eol_t entry_kind (uint32_t e)
 The kind of line end an index entry holds.
static uint32_t entry (size_t pos, text_eol_t kind)
 An index entry.
static size_t eol_bytes (text_eol_t kind)
 The bytes of a kind of line end.
static bool ends_at (const text_buf_t *tb, size_t i, text_eol_t *kind)
 Whether a line ends at the byte at i, and of which kind.
static bool index_all (text_buf_t *tb)
 Count the line ends of the whole text into the index.
static void edit (text_buf_t *tb, size_t p, size_t d, const uint8_t *ins, size_t s)
 Take d bytes at p and put the s bytes of ins there, keeping the index.
static void empty (text_buf_t *tb)
 Empty the text and set the most bytes it may take.
text_buf_err_t text_buf_init (text_buf_t *tb, uint8_t *mem, size_t cap, uint32_t *ends, size_t ends_cap)
 An empty text over mem, LF line ends, no BOM.
size_t text_buf_room (const text_buf_t *tb)
 The largest file text_buf_load() and text_buf_load_done() take.
uint8_t * text_buf_load_area (text_buf_t *tb)
 Where a loader puts a file's bytes in place, text_buf_room() of them at most, before text_buf_load_done(). The text held is lost.
text_buf_err_t text_buf_load_done (text_buf_t *tb, size_t len)
 Take the len bytes a loader put at text_buf_load_area() as the text: the BOM, the line ends and their kind, the NUL test.
text_buf_err_t text_buf_load (text_buf_t *tb, const void *bytes, size_t len)
 Copy a file into the buffer and take it, as text_buf_load_done().
size_t text_buf_size (const text_buf_t *tb)
 Bytes text_buf_read() gives: the text and the BOM.
size_t text_buf_read (const text_buf_t *tb, size_t at_, void *out, size_t n)
 Serialised bytes at to at + n of the file, the BOM in front: a save writes the file in pieces with this.
size_t text_buf_len (const text_buf_t *tb)
 Text bytes, the BOM not counted.
uint8_t text_buf_byte (const text_buf_t *tb, size_t pos)
 Byte pos of the text.
size_t text_buf_lines (const text_buf_t *tb)
 Lines of the text: its line ends and one.
size_t text_buf_line_start (const text_buf_t *tb, size_t line)
 Where line line starts.
static size_t line_end (const text_buf_t *tb, size_t line)
 Where line line's text ends, before its line end.
size_t text_buf_line_len (const text_buf_t *tb, size_t line)
 Bytes of line line without its line end.
const char * text_buf_line (text_buf_t *tb, size_t line, size_t *len)
 Line line as one run of bytes, without its line end. Moves the gap out of the line when it is in it; the text is unchanged.
size_t text_buf_line_of (const text_buf_t *tb, size_t pos)
 The line text offset pos is on.
uint32_t text_buf_cp (const text_buf_t *tb, size_t pos, size_t *bytes)
 The code point of the unit at pos.
size_t text_buf_next (const text_buf_t *tb, size_t pos)
 The start of the unit after the one at pos.
bool text_buf_is_unit (const text_buf_t *tb, size_t pos)
 Whether a unit starts at pos: not inside a UTF-8 sequence and not between the CR and the LF of a CR LF.
size_t text_buf_prev (const text_buf_t *tb, size_t pos)
 The start of the unit before pos.
static void cells_start (const text_buf_t *tb, size_t line, cells_t *w)
 Start a walk over line line.
static void cells_place (const text_buf_t *tb, cells_t *w, unsigned width)
 Place the unit at w->pos: its first cell and the one after it, starting a row where it does not fit.
static void cells_step (const text_buf_t *tb, cells_t *w)
 Step the walk past the unit cells_place() placed.
static size_t cells_end (const cells_t *w, unsigned width)
 The cell after a walk's last unit.
size_t text_buf_rows (const text_buf_t *tb, size_t line, unsigned width)
 Rows line line takes, wrapped at width cells.
void text_buf_cell_of (const text_buf_t *tb, size_t pos, unsigned width, size_t *line, size_t *cell)
 The line and the cell at which the unit at pos is drawn.
size_t text_buf_pos_at (const text_buf_t *tb, size_t line, size_t cell, unsigned width)
 The unit drawn over cell cell of line line: the cursor position a tap there gives.
size_t text_buf_cursor (const text_buf_t *tb)
 The cursor, a text offset at a unit's start.
void text_buf_set_cursor (text_buf_t *tb, size_t pos)
 Put the cursor at pos, moved back to the start of the unit it is in; past the end, at the end.
bool text_buf_left (text_buf_t *tb)
 Move the cursor one unit left.
bool text_buf_right (text_buf_t *tb)
 Move the cursor one unit right.
text_buf_err_t text_buf_insert (text_buf_t *tb, uint32_t cp)
 Insert code point cp at the cursor, which goes after it.
text_buf_err_t text_buf_insert_eol (text_buf_t *tb)
 Insert a line end at the cursor, which goes to the new line.
bool text_buf_delete_before (text_buf_t *tb)
 Remove the unit before the cursor (backspace).
bool text_buf_delete_after (text_buf_t *tb)
 Remove the unit at the cursor (delete).
text_eol_t text_buf_eol (const text_buf_t *tb)
 The file's kind of line end, which ENTER inserts.
bool text_buf_mixed (const text_buf_t *tb)
 Whether the text holds line ends of two kinds or three.
bool text_buf_bom (const text_buf_t *tb)
 Whether the file starts with a BOM.
bool text_buf_dirty (const text_buf_t *tb)
 Whether the text was edited since the load or text_buf_saved().
void text_buf_saved (text_buf_t *tb)
 Note that the text as it is now is on the card.
uint32_t text_buf_revision (const text_buf_t *tb)
 The revision: moves on with each edit and load; compare it for equality only.

Variables

static const uint8_t k_bom [TEXT_BUF_BOM_LEN] = {0xEFu, 0xBBu, 0xBFu}
 The UTF-8 BOM.

Detailed Description

A text file in memory: the gap buffer, the line index, the units and the cells (text_buf.h).

The text is mem[0, gap_at) and mem[gap_at + gap_len, cap): the gap sits where the last edit was, so typing at one place moves no byte, and a cursor that goes elsewhere moves the gap with the next edit.

The index holds, per line end, the text offset of the line after it. An edit at offset p that takes d bytes and puts s in their place can change the line ends at the bytes p - 1 to p + s only: a CR at p - 1 may meet an LF that comes to p, the LF at p + s may lose or gain the CR in front of it, and every other line end depends on bytes the edit did not touch. So an edit scans those bytes again, replaces their entries, and moves every later entry by s - d. An edit is at most 4 bytes each way, so the stretch holds at most 6 line ends; the cost of an edit is one pass over the later entries, 65,536 at most.

SPDX-License-Identifier: MIT

Function Documentation

◆ at()

uint8_t at ( const text_buf_t * tb,
size_t i )
static

Byte i of the text, read across the gap.

Parameters
tbThe buffer.
iText offset under tb->len.
Returns
The byte.

◆ cells_end()

size_t cells_end ( const cells_t * w,
unsigned width )
static

The cell after a walk's last unit.

Parameters
wThe walk at the line's end.
widthCells of a row.
Returns
row * width + column.

◆ cells_place()

void cells_place ( const text_buf_t * tb,
cells_t * w,
unsigned width )
static

Place the unit at w->pos: its first cell and the one after it, starting a row where it does not fit.

Parameters
tbThe buffer.
wThe walk; w->pos under w->end.
widthCells of a row; 0 does not wrap.

◆ cells_start()

void cells_start ( const text_buf_t * tb,
size_t line,
cells_t * w )
static

Start a walk over line line.

Parameters
tbThe buffer.
lineLine index up to tb->n_ends.
wOut: the walk.

◆ cells_step()

void cells_step ( const text_buf_t * tb,
cells_t * w )
static

Step the walk past the unit cells_place() placed.

Parameters
tbThe buffer.
wThe walk.

◆ decode()

size_t decode ( const text_buf_t * tb,
size_t i,
uint32_t * cp )
static

Decode the UTF-8 sequence at i, as gfx_utf8_next() does.

Parameters
tbThe buffer.
iText offset under tb->len.
cpOut: the code point, '?' for a byte that starts no sequence.
Returns
The bytes of the sequence; 1 for a byte that starts none.

◆ edit()

void edit ( text_buf_t * tb,
size_t p,
size_t d,
const uint8_t * ins,
size_t s )
static

Take d bytes at p and put the s bytes of ins there, keeping the index.

The caller has checked the room and the index: a line end comes in only through text_buf_insert_eol(), which checks for one more entry, and no other edit adds one.

Parameters
tbThe buffer.
pText offset of a unit's start.
dBytes to take, EDIT_MAX at most.
insBytes to put; NULL with s 0.
sTheir count, EDIT_MAX at most.

◆ empty()

void empty ( text_buf_t * tb)
static

Empty the text and set the most bytes it may take.

Parameters
tbThe buffer, its memory and index set.

◆ encode()

size_t encode ( uint32_t cp,
uint8_t * out )
static

Encode cp as UTF-8.

Parameters
cpA code point up to U+10FFFF.
outOut: 4 bytes of room.
Returns
The bytes written.

◆ ends_at()

bool ends_at ( const text_buf_t * tb,
size_t i,
text_eol_t * kind )
static

Whether a line ends at the byte at i, and of which kind.

The LF of a CR LF ends it; the CR in front does not.

Parameters
tbThe buffer.
iText offset under tb->len.
kindOut: the kind when a line ends there.
Returns
true when the line ends with the byte at i.

◆ entry()

uint32_t entry ( size_t pos,
text_eol_t kind )
static

An index entry.

Parameters
posThe offset of the line after the line end.
kindThe kind of line end.
Returns
The entry.

◆ entry_kind()

text_eol_t entry_kind ( uint32_t e)
static

The kind of line end an index entry holds.

Parameters
eThe entry.
Returns
The kind.

◆ entry_pos()

size_t entry_pos ( uint32_t e)
static

The text offset an index entry holds.

Parameters
eThe entry.
Returns
The offset of the line after the line end.

◆ eol_bytes()

size_t eol_bytes ( text_eol_t kind)
static

The bytes of a kind of line end.

Parameters
kindThe kind.
Returns
2 for CR LF, 1 otherwise.

◆ index_all()

bool index_all ( text_buf_t * tb)
static

Count the line ends of the whole text into the index.

Parameters
tbThe buffer.
Returns
false when there are more than the index holds.

◆ line_end()

size_t line_end ( const text_buf_t * tb,
size_t line )
static

Where line line's text ends, before its line end.

Parameters
tbThe buffer.
lineLine index up to tb->n_ends.
Returns
Text offset.

◆ move_gap()

void move_gap ( text_buf_t * tb,
size_t p )
static

Move the gap to text offset p.

Parameters
tbThe buffer.
pText offset up to tb->len.

◆ text_buf_bom()

bool text_buf_bom ( const text_buf_t * tb)

Whether the file starts with a BOM.

Parameters
tbThe buffer.
Returns
true with a BOM.

◆ text_buf_byte()

uint8_t text_buf_byte ( const text_buf_t * tb,
size_t pos )

Byte pos of the text.

Parameters
tbThe buffer.
posText offset.
Returns
The byte; 0 past the end.

◆ text_buf_cell_of()

void text_buf_cell_of ( const text_buf_t * tb,
size_t pos,
unsigned width,
size_t * line,
size_t * cell )

The line and the cell at which the unit at pos is drawn.

At a line's end that is the cell after its last unit, on the last row (column width when that row is full).

Parameters
tbThe buffer.
posText offset of a unit's start.
widthCells of a row; 0 does not wrap.
lineOut: line index; or NULL.
cellOut: row * width + column; or NULL.

◆ text_buf_cp()

uint32_t text_buf_cp ( const text_buf_t * tb,
size_t pos,
size_t * bytes )

The code point of the unit at pos.

Parameters
tbThe buffer.
posText offset of a unit's start.
bytesOut: the unit's bytes, 0 at the end; or NULL.
Returns
The code point; '?' for a byte that starts no well-formed sequence; '\n' for a line end of any kind; 0 at the end.

◆ text_buf_cursor()

size_t text_buf_cursor ( const text_buf_t * tb)

The cursor, a text offset at a unit's start.

Parameters
tbThe buffer.
Returns
Text offset; 0 for NULL.

◆ text_buf_delete_after()

bool text_buf_delete_after ( text_buf_t * tb)

Remove the unit at the cursor (delete).

Parameters
tbThe buffer.
Returns
false at the end.

◆ text_buf_delete_before()

bool text_buf_delete_before ( text_buf_t * tb)

Remove the unit before the cursor (backspace).

Parameters
tbThe buffer.
Returns
false at the start.

◆ text_buf_dirty()

bool text_buf_dirty ( const text_buf_t * tb)

Whether the text was edited since the load or text_buf_saved().

Parameters
tbThe buffer.
Returns
true when edited.

◆ text_buf_eol()

text_eol_t text_buf_eol ( const text_buf_t * tb)

The file's kind of line end, which ENTER inserts.

Parameters
tbThe buffer.
Returns
The kind; LF for NULL.

◆ text_buf_init()

text_buf_err_t text_buf_init ( text_buf_t * tb,
uint8_t * mem,
size_t cap,
uint32_t * ends,
size_t ends_cap )

An empty text over mem, LF line ends, no BOM.

Parameters
tbThe buffer.
memBytes for the text; their count is the most it holds, up to TEXT_BUF_FILE_MAX.
capCount of mem.
endsThe line index, one entry per line end.
ends_capCount of ends; above TEXT_BUF_ENDS_MAX counts as that.
Returns
TEXT_BUF_OK, or TEXT_BUF_ARG for a NULL.

◆ text_buf_insert()

text_buf_err_t text_buf_insert ( text_buf_t * tb,
uint32_t cp )

Insert code point cp at the cursor, which goes after it.

Parameters
tbThe buffer.
cpA tab, or U+0020 and above but U+007F to U+009F, the surrogates and above U+10FFFF.
Returns
TEXT_BUF_OK; TEXT_BUF_ARG for any other code point or NULL; TEXT_BUF_FULL when the text has no room for its bytes.

◆ text_buf_insert_eol()

text_buf_err_t text_buf_insert_eol ( text_buf_t * tb)

Insert a line end at the cursor, which goes to the new line.

Parameters
tbThe buffer.
Returns
TEXT_BUF_OK; TEXT_BUF_FULL without room for its bytes; TEXT_BUF_TOO_MANY when the index is full; TEXT_BUF_ARG for NULL.

◆ text_buf_is_unit()

bool text_buf_is_unit ( const text_buf_t * tb,
size_t pos )

Whether a unit starts at pos: not inside a UTF-8 sequence and not between the CR and the LF of a CR LF.

Parameters
tbThe buffer.
posText offset.
Returns
true at a unit's start and at the end; false past it.

◆ text_buf_left()

bool text_buf_left ( text_buf_t * tb)

Move the cursor one unit left.

Parameters
tbThe buffer.
Returns
false at the start.

◆ text_buf_len()

size_t text_buf_len ( const text_buf_t * tb)

Text bytes, the BOM not counted.

Parameters
tbThe buffer.
Returns
Bytes; 0 for NULL.

◆ text_buf_line()

const char * text_buf_line ( text_buf_t * tb,
size_t line,
size_t * len )

Line line as one run of bytes, without its line end. Moves the gap out of the line when it is in it; the text is unchanged.

Parameters
tbThe buffer.
lineLine index, from 0.
lenOut: bytes of the line.
Returns
The first byte, valid until the next call on tb; NULL past the last line or for a NULL.

◆ text_buf_line_len()

size_t text_buf_line_len ( const text_buf_t * tb,
size_t line )

Bytes of line line without its line end.

Parameters
tbThe buffer.
lineLine index, from 0.
Returns
Bytes; 0 past the last line.

◆ text_buf_line_of()

size_t text_buf_line_of ( const text_buf_t * tb,
size_t pos )

The line text offset pos is on.

Parameters
tbThe buffer.
posText offset.
Returns
Line index; the last line past the end.

◆ text_buf_line_start()

size_t text_buf_line_start ( const text_buf_t * tb,
size_t line )

Where line line starts.

Parameters
tbThe buffer.
lineLine index, from 0.
Returns
Text offset; text_buf_len() past the last line.

◆ text_buf_lines()

size_t text_buf_lines ( const text_buf_t * tb)

Lines of the text: its line ends and one.

Parameters
tbThe buffer.
Returns
Lines, 1 for an empty text; 0 for NULL.

◆ text_buf_load()

text_buf_err_t text_buf_load ( text_buf_t * tb,
const void * bytes,
size_t len )

Copy a file into the buffer and take it, as text_buf_load_done().

Parameters
tbThe buffer.
bytesThe file; NULL with len 0 is an empty file.
lenIts bytes.
Returns
As text_buf_load_done(). A file over the room is refused before a byte is copied.

◆ text_buf_load_area()

uint8_t * text_buf_load_area ( text_buf_t * tb)

Where a loader puts a file's bytes in place, text_buf_room() of them at most, before text_buf_load_done(). The text held is lost.

Parameters
tbThe buffer.
Returns
The caller's bytes; NULL for NULL.

◆ text_buf_load_done()

text_buf_err_t text_buf_load_done ( text_buf_t * tb,
size_t len )

Take the len bytes a loader put at text_buf_load_area() as the text: the BOM, the line ends and their kind, the NUL test.

Refused, the buffer is empty as text_buf_init() leaves it. The revision moves on either way.

Parameters
tbThe buffer.
lenBytes of the file.
Returns
TEXT_BUF_OK; TEXT_BUF_TOO_LARGE over text_buf_room(); TEXT_BUF_NOT_TEXT; TEXT_BUF_TOO_MANY; TEXT_BUF_ARG for NULL.

◆ text_buf_mixed()

bool text_buf_mixed ( const text_buf_t * tb)

Whether the text holds line ends of two kinds or three.

Parameters
tbThe buffer.
Returns
true when mixed.

◆ text_buf_next()

size_t text_buf_next ( const text_buf_t * tb,
size_t pos )

The start of the unit after the one at pos.

Parameters
tbThe buffer.
posText offset of a unit's start.
Returns
Text offset; text_buf_len() at the end.

◆ text_buf_pos_at()

size_t text_buf_pos_at ( const text_buf_t * tb,
size_t line,
size_t cell,
unsigned width )

The unit drawn over cell cell of line line: the cursor position a tap there gives.

Parameters
tbThe buffer.
lineLine index, from 0; past the last line, the last.
cellrow * width + column.
widthCells of a row; 0 does not wrap.
Returns
The text offset of that unit; the line's end past its last unit.

◆ text_buf_prev()

size_t text_buf_prev ( const text_buf_t * tb,
size_t pos )

The start of the unit before pos.

Parameters
tbThe buffer.
posText offset of a unit's start.
Returns
Text offset; 0 at the start.

◆ text_buf_read()

size_t text_buf_read ( const text_buf_t * tb,
size_t at,
void * out,
size_t n )

Serialised bytes at to at + n of the file, the BOM in front: a save writes the file in pieces with this.

Parameters
tbThe buffer.
atOffset in the file.
outOut: the bytes.
nRoom in out.
Returns
Bytes written to out: fewer than n at the end, 0 past it.

◆ text_buf_revision()

uint32_t text_buf_revision ( const text_buf_t * tb)

The revision: moves on with each edit and load; compare it for equality only.

Parameters
tbThe buffer.
Returns
The revision; 0 for NULL.

◆ text_buf_right()

bool text_buf_right ( text_buf_t * tb)

Move the cursor one unit right.

Parameters
tbThe buffer.
Returns
false at the end.

◆ text_buf_room()

size_t text_buf_room ( const text_buf_t * tb)

The largest file text_buf_load() and text_buf_load_done() take.

Parameters
tbThe buffer.
Returns
min(cap, TEXT_BUF_FILE_MAX) bytes; 0 for NULL.

◆ text_buf_rows()

size_t text_buf_rows ( const text_buf_t * tb,
size_t line,
unsigned width )

Rows line line takes, wrapped at width cells.

Parameters
tbThe buffer.
lineLine index, from 0.
widthCells of a row; 0 does not wrap.
Returns
Rows, at least 1; 0 past the last line.

◆ text_buf_saved()

void text_buf_saved ( text_buf_t * tb)

Note that the text as it is now is on the card.

Parameters
tbThe buffer.

◆ text_buf_set_cursor()

void text_buf_set_cursor ( text_buf_t * tb,
size_t pos )

Put the cursor at pos, moved back to the start of the unit it is in; past the end, at the end.

Parameters
tbThe buffer.
posText offset.

◆ text_buf_size()

size_t text_buf_size ( const text_buf_t * tb)

Bytes text_buf_read() gives: the text and the BOM.

Parameters
tbThe buffer.
Returns
Bytes; 0 for NULL.