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

A text file in memory for the viewer and the editor: a gap buffer over the caller's bytes and an index of its lines. More...

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

Go to the source code of this file.

Data Structures

struct  text_buf_t
 The buffer. Read it through the functions; the fields are open only so the caller can place it. More...

Macros

#define TEXT_BUF_FILE_MAX   1048576u
 Bytes of a file the buffer takes, its BOM included: 1MiB.
#define TEXT_BUF_ENDS_MAX   65536u
 Line ends a text holds at most: 65,536 index entries of 4B.
#define TEXT_BUF_PROBE   4096u
 Bytes from the start of the file a NUL makes it no text.
#define TEXT_BUF_TAB   8u
 Cells between two tab stops.
#define TEXT_BUF_BOM_LEN   3u
 Bytes of the UTF-8 BOM.

Enumerations

enum  text_buf_err_t {
  TEXT_BUF_OK = 0 , TEXT_BUF_ARG = 1 , TEXT_BUF_TOO_LARGE = 2 , TEXT_BUF_TOO_MANY = 3 ,
  TEXT_BUF_NOT_TEXT = 4 , TEXT_BUF_FULL = 5
}
 What a load or an edit answers. The numbers are stable. More...
enum  text_eol_t { TEXT_EOL_LF = 0 , TEXT_EOL_CRLF = 1 , TEXT_EOL_CR = 2 , TEXT_EOL_KINDS = 3 }
 A kind of line end. The numbers are stable. More...

Functions

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.
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.
size_t text_buf_prev (const text_buf_t *tb, size_t pos)
 The start of the unit before 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_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.

Detailed Description

A text file in memory for the viewer and the editor: a gap buffer over the caller's bytes and an index of its lines.

The buffer holds the file's bytes as they are. Nothing is converted: a line end stays LF, CR LF or CR as the file has it, a code point the text faces do not draw stays as its bytes, and a NUL after the first TEXT_BUF_PROBE bytes stays a NUL. A leading UTF-8 BOM (byte order mark, EF BB BF) is taken off at load, remembered, and written back in front by text_buf_read(); it is no cell and no cursor position. Serialising an unchanged buffer gives the loaded file byte for byte.

Limits:

  • a file of at most TEXT_BUF_FILE_MAX bytes (1MiB), the BOM included; the editor stops a text from growing past what the viewer opens, so a saved file always opens again;
  • at most the caller's index entries, and TEXT_BUF_ENDS_MAX (65,536), line ends; a text of n line ends has n + 1 lines, the last one empty when the file ends with a line end;
  • no limit on the length of a line.

A unit is what the cursor steps over and DEL removes: one well-formed UTF-8 sequence, one byte that starts none (drawn as one '?' cell, as gfx_utf8_next() reads it), or one line end, CR LF included. Every unit of a line is one cell, except a tab, which reaches to the next multiple of TEXT_BUF_TAB cells. A line is drawn wrapped at a width in cells: a unit that does not fit the row starts the next one, and a tab that does not fit ends at the row's last cell. A cell number is row * width + column, so it counts along the wrapped line.

Line ends: the file's kind is that of its first line end, LF for a file without one. ENTER inserts that kind. Where it would join a neighbouring lone CR or LF into one CR LF, ENTER inserts CR LF instead, so it always adds one line. A text with two kinds is mixed (text_buf_mixed()); it is saved as it is.

Every edit moves the revision on, and it wraps after 2^32 edits: compare it for equality only. The dirty flag is set by an edit and cleared by a load and by text_buf_saved().

Every buffer comes from the caller: no heap, no stdio. Pure C.

SPDX-License-Identifier: MIT

Enumeration Type Documentation

◆ text_buf_err_t

What a load or an edit answers. The numbers are stable.

Enumerator
TEXT_BUF_OK 

done

TEXT_BUF_ARG 

a NULL, or a code point no text holds

TEXT_BUF_TOO_LARGE 

a file over the room text_buf_room() gives

TEXT_BUF_TOO_MANY 

more line ends than the index holds

TEXT_BUF_NOT_TEXT 

a NUL in the first TEXT_BUF_PROBE bytes

TEXT_BUF_FULL 

the text is at its most bytes

◆ text_eol_t

enum text_eol_t

A kind of line end. The numbers are stable.

Enumerator
TEXT_EOL_LF 

0x0A

TEXT_EOL_CRLF 

0x0D 0x0A

TEXT_EOL_CR 

0x0D not followed by 0x0A

TEXT_EOL_KINDS 

the count of kinds

Function Documentation

◆ 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.