~martanne/devel

1

[vis][RFC] introduce s8 type and use it to simplify theme parsing

Details
Message ID
<20250106153748.1072-1-randy@rnpnr.xyz>
DKIM signature
permerror
Download raw message
Patch: +184 -87
Previously theme parsing included strlen(), even though we could
have retrieved the length directly from lua, and strdup()/free()
because we needed to split the string which required inserting a 0
for termination. This was all energy wasting busy work since all
the information we actually needed was contained in the original
string.

This commit adds ~100 lines of code because I have provided my own
implementation of some functions that we were previously provided
by the C-runtime. This code will be reused later to simplify lots
of other code in sam.c, map.c, vis-lua.c, and likely more. I can't
say if this will be a net reduction of code in vis but I can
guarantee that it will be a net reduction in total lines of code
running on the users machine - C-runtime implementations are often
massive over-complicated messes (even musl).
---
 Makefile             |   1 +
 main.c               |   2 +-
 sam.c                |   3 +-
 ui-terminal-curses.c |   7 +--
 ui-terminal-vt100.c  |  19 +++----
 ui-terminal.c        | 123 +++++++++++++++++++++----------------------
 ui.h                 |   2 +-
 util.c               |  71 +++++++++++++++++++++++++
 util.h               |  27 +++++++++-
 vis-core.h           |   3 +-
 vis-lua.c            |   8 +--
 vis-subprocess.c     |   3 +-
 vis.c                |   2 +-
 13 files changed, 184 insertions(+), 87 deletions(-)
 create mode 100644 util.c

diff --git a/Makefile b/Makefile
index a2f1df3..3d3cd23 100644
--- a/Makefile
+++ b/Makefile
@@ -17,6 +17,7 @@ SRC = array.c \
	text-util.c \
	text.c \
	ui-terminal.c \
	util.c \
	view.c \
	vis-lua.c \
	vis-marks.c \
diff --git a/main.c b/main.c
index 3e9d9ab..a915c72 100644
--- a/main.c
+++ b/main.c
@@ -10,13 +10,13 @@
#include <sys/stat.h>
#include <sys/types.h>

#include "util.h"
#include "ui.h"
#include "vis.h"
#include "vis-lua.h"
#include "text-util.h"
#include "text-motions.h"
#include "text-objects.h"
#include "util.h"
#include "libutf.h"
#include "array.h"
#include "buffer.h"
diff --git a/sam.c b/sam.c
index e5aead3..7250ab6 100644
--- a/sam.c
+++ b/sam.c
@@ -24,6 +24,8 @@
#include <unistd.h>
#include <limits.h>
#include <fcntl.h>

#include "util.h"
#include "sam.h"
#include "vis-core.h"
#include "buffer.h"
@@ -31,7 +33,6 @@
#include "text-motions.h"
#include "text-objects.h"
#include "text-regex.h"
#include "util.h"

#define MAX_ARGV 8

diff --git a/ui-terminal-curses.c b/ui-terminal-curses.c
index a2aa7ec..779caff 100644
--- a/ui-terminal-curses.c
+++ b/ui-terminal-curses.c
@@ -73,13 +73,13 @@ static void undo_palette(void)
}

/* Work out the nearest color from the 256 color set, or perhaps exactly. */
static CellColor color_rgb(Ui *ui, uint8_t r, uint8_t g, uint8_t b)
static CellColor color_rgb(bool config_change_colors, u8 r, u8 g, u8 b)
{
	static short color_clobber_idx = 0;
	static uint32_t clobbering_colors[MAX_COLOR_CLOBBER];

	if (change_colors == -1)
		change_colors = ui->vis->change_colors && can_change_color() && COLORS >= 256;
		change_colors = config_change_colors && can_change_color() && COLORS >= 256;
	if (change_colors) {
		uint32_t hexrep = (r << 16) | (g << 8) | b;
		for (short i = 0; i < MAX_COLOR_CLOBBER; ++i) {
@@ -151,7 +151,8 @@ static CellColor color_rgb(Ui *ui, uint8_t r, uint8_t g, uint8_t b)
	return i;
}

static CellColor color_terminal(Ui *ui, uint8_t index) {
static CellColor color_terminal(u8 index)
{
	return index;
}

diff --git a/ui-terminal-vt100.c b/ui-terminal-vt100.c
index 3c63c11..959be81 100644
--- a/ui-terminal-vt100.c
+++ b/ui-terminal-vt100.c
@@ -59,34 +59,31 @@
#define CELL_ATTR_DIM       (1 << 5)

static inline bool cell_color_equal(CellColor c1, CellColor c2) {
	if (c1.index != (uint8_t)-1 || c2.index != (uint8_t)-1)
		return c1.index == c2.index;
	return c1.r == c2.r && c1.g == c2.g && c1.b == c2.b;
	return c1.index == c2.index && c1.r == c2.r && c1.g == c2.g && c1.b == c2.b;
}

static CellColor color_rgb(Ui *ui, uint8_t r, uint8_t g, uint8_t b) {
	return (CellColor){ .r = r, .g = g, .b = b, .index = (uint8_t)-1 };
static CellColor color_rgb(bool unused, u8 r, u8 g, u8 b) {
	return (CellColor){ .r = r, .g = g, .b = b, .index = (u8)-1 };
}

static CellColor color_terminal(Ui *ui, uint8_t index) {
static CellColor color_terminal(u8 index) {
	return (CellColor){ .r = 0, .g = 0, .b = 0, .index = index };
}


static void output(const char *data, size_t len) {
	write(STDERR_FILENO, data, len);
}

static void output_literal(const char *data) {
	output(data, strlen(data));
static void output_s8(s8 msg) {
	output((char *)msg.data, msg.len);
}

static void screen_alternate(bool alternate) {
	output_literal(alternate ? "\x1b[?1049h" : "\x1b[0m" "\x1b[?1049l" "\x1b[0m" );
	output_s8(alternate ? s8("\x1b[?1049h") : s8("\x1b[0m" "\x1b[?1049l" "\x1b[0m"));
}

static void cursor_visible(bool visible) {
	output_literal(visible ? "\x1b[?25h" : "\x1b[?25l");
	output_s8(visible ? s8("\x1b[?25h") : s8("\x1b[?25l"));
}

static void ui_term_backend_blit(Ui *tui) {
diff --git a/ui-terminal.c b/ui-terminal.c
index 7cfb377..801358a 100644
--- a/ui-terminal.c
+++ b/ui-terminal.c
@@ -1,9 +1,6 @@
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <limits.h>
#include <ctype.h>
#include <locale.h>
#include <poll.h>
#include <sys/ioctl.h>
@@ -13,10 +10,10 @@
#include <termios.h>
#include <errno.h>

#include "util.h"
#include "vis.h"
#include "vis-core.h"
#include "text.h"
#include "util.h"
#include "text-util.h"

#ifndef DEBUG_UI
@@ -77,46 +74,52 @@ static void ui_window_move(Win *win, int x, int y) {
	win->y = y;
}

static bool color_fromstring(Ui *ui, CellColor *color, const char *s)
static b32
color_from_s8(Vis *vis, CellColor *color, s8 s)
{
	if (!s)
	if (s.len <= 0)
		return false;
	if (*s == '#' && strlen(s) == 7) {
		const char *cp;
		unsigned char r, g, b;
		for (cp = s + 1; isxdigit((unsigned char)*cp); cp++);
		if (*cp != '\0')
			return false;
		int n = sscanf(s + 1, "%2hhx%2hhx%2hhx", &r, &g, &b);
		if (n != 3)
			return false;
		*color = color_rgb(ui, r, g, b);

	#if 0
	/* TODO(rnp): this can fire before the default style gets set which can break
	 * it for some reason. */
	if (s.data[0] == '#' && s.len > 7) {
		vis_info_show(vis, "WARNING: style_define: ignoring alpha channel in: %*s",
		              (int)s.len, s.data);
		s.len = 7;
	}
	#endif

	if (s.data[0] == '#' && s.len == 7) {
		u32 rgb = s8_hex_to_u32(s);
		*color = color_rgb(vis->change_colors,
		                   (rgb >> 16) & 0xFFu, (rgb >> 8) & 0xFFu, (rgb >> 0) & 0xFFu);
		return true;
	} else if ('0' <= *s && *s <= '9') {
		int index = atoi(s);
	} else if ('0' <= s.data[0] && s.data[0] <= '9') {
		int index = s8_to_i64(s);
		if (index <= 0 || index > 255)
			return false;
		*color = color_terminal(ui, index);
		*color = color_terminal(index);
		return true;
	}

	static const struct {
		const char *name;
		s8        name;
		CellColor color;
	} color_names[] = {
		{ "black",   CELL_COLOR_BLACK   },
		{ "red",     CELL_COLOR_RED     },
		{ "green",   CELL_COLOR_GREEN   },
		{ "yellow",  CELL_COLOR_YELLOW  },
		{ "blue",    CELL_COLOR_BLUE    },
		{ "magenta", CELL_COLOR_MAGENTA },
		{ "cyan",    CELL_COLOR_CYAN    },
		{ "white",   CELL_COLOR_WHITE   },
		{ "default", CELL_COLOR_DEFAULT },
		{ s8("black"),   CELL_COLOR_BLACK   },
		{ s8("red"),     CELL_COLOR_RED     },
		{ s8("green"),   CELL_COLOR_GREEN   },
		{ s8("yellow"),  CELL_COLOR_YELLOW  },
		{ s8("blue"),    CELL_COLOR_BLUE    },
		{ s8("magenta"), CELL_COLOR_MAGENTA },
		{ s8("cyan"),    CELL_COLOR_CYAN    },
		{ s8("white"),   CELL_COLOR_WHITE   },
		{ s8("default"), CELL_COLOR_DEFAULT },
	};

	for (size_t i = 0; i < LENGTH(color_names); i++) {
		if (strcasecmp(color_names[i].name, s) == 0) {
		if (s8_case_ignore_equal(color_names[i].name, s)) {
			*color = color_names[i].color;
			return true;
		}
@@ -125,58 +128,54 @@ static bool color_fromstring(Ui *ui, CellColor *color, const char *s)
	return false;
}

bool ui_style_define(Win *win, int id, const char *style) {
b32 ui_style_define(Win *win, int id, s8 style) {
	Ui *tui = &win->vis->ui;
	if (id >= UI_STYLE_MAX)
		return false;
	if (!style)
		return true;

	CellStyle cell_style = CELL_STYLE_DEFAULT;
	char *style_copy = strdup(style), *option = style_copy;
	while (option) {
		while (*option == ' ')
			option++;
		char *next = strchr(option, ',');
		if (next)
			*next++ = '\0';
		char *value = strchr(option, ':');
		if (value)
			for (*value++ = '\0'; *value == ' '; value++);
		if (!strcasecmp(option, "reverse")) {
	s8 option = style;
	b32 result = true;
	while (option.len) {
		s8 next, value;
		option = s8_trim_space(option);
		s8_split(option, ',', &option, &next);
		s8_split(option, ':', &option, &value);
		value  = s8_trim_space(value);

		if (s8_case_ignore_equal(option, s8("reverse"))) {
			cell_style.attr |= CELL_ATTR_REVERSE;
		} else if (!strcasecmp(option, "notreverse")) {
		} else if (s8_case_ignore_equal(option, s8("notreverse"))) {
			cell_style.attr &= CELL_ATTR_REVERSE;
		} else if (!strcasecmp(option, "bold")) {
		} else if (s8_case_ignore_equal(option, s8("bold"))) {
			cell_style.attr |= CELL_ATTR_BOLD;
		} else if (!strcasecmp(option, "notbold")) {
		} else if (s8_case_ignore_equal(option, s8("notbold"))) {
			cell_style.attr &= ~CELL_ATTR_BOLD;
		} else if (!strcasecmp(option, "dim")) {
		} else if (s8_case_ignore_equal(option, s8("dim"))) {
			cell_style.attr |= CELL_ATTR_DIM;
		} else if (!strcasecmp(option, "notdim")) {
		} else if (s8_case_ignore_equal(option, s8("notdim"))) {
			cell_style.attr &= ~CELL_ATTR_DIM;
		} else if (!strcasecmp(option, "italics")) {
		} else if (s8_case_ignore_equal(option, s8("italics"))) {
			cell_style.attr |= CELL_ATTR_ITALIC;
		} else if (!strcasecmp(option, "notitalics")) {
		} else if (s8_case_ignore_equal(option, s8("notitalics"))) {
			cell_style.attr &= ~CELL_ATTR_ITALIC;
		} else if (!strcasecmp(option, "underlined")) {
		} else if (s8_case_ignore_equal(option, s8("underlined"))) {
			cell_style.attr |= CELL_ATTR_UNDERLINE;
		} else if (!strcasecmp(option, "notunderlined")) {
		} else if (s8_case_ignore_equal(option, s8("notunderlined"))) {
			cell_style.attr &= ~CELL_ATTR_UNDERLINE;
		} else if (!strcasecmp(option, "blink")) {
		} else if (s8_case_ignore_equal(option, s8("blink"))) {
			cell_style.attr |= CELL_ATTR_BLINK;
		} else if (!strcasecmp(option, "notblink")) {
		} else if (s8_case_ignore_equal(option, s8("notblink"))) {
			cell_style.attr &= ~CELL_ATTR_BLINK;
		} else if (!strcasecmp(option, "fore")) {
			color_fromstring(&win->vis->ui, &cell_style.fg, value);
		} else if (!strcasecmp(option, "back")) {
			color_fromstring(&win->vis->ui, &cell_style.bg, value);
		} else if (s8_case_ignore_equal(option, s8("fore"))) {
			result &= color_from_s8(win->vis, &cell_style.fg, value);
		} else if (s8_case_ignore_equal(option, s8("back"))) {
			result &= color_from_s8(win->vis, &cell_style.bg, value);
		}
		option = next;
	}
	tui->styles[win->id * UI_STYLE_MAX + id] = cell_style;
	free(style_copy);
	return true;
	if (result) tui->styles[win->id * UI_STYLE_MAX + id] = cell_style;
	return result;
}

static void ui_draw_line(Ui *tui, int x, int y, char c, enum UiStyle style_id) {
diff --git a/ui.h b/ui.h
index 5680400..531af71 100644
--- a/ui.h
+++ b/ui.h
@@ -127,7 +127,7 @@ void ui_window_swap(Win *, Win *);

bool ui_getkey(Ui *, TermKeyKey *);

bool ui_style_define(Win *win, int id, const char *style);
b32  ui_style_define(Win *win, int id, s8 style);
bool ui_window_style_set_pos(Win *win, int x, int y, enum UiStyle id);
void ui_window_style_set(Win *win, Cell *cell, enum UiStyle id);

diff --git a/util.c b/util.c
new file mode 100644
index 0000000..05cdc78
--- /dev/null
+++ b/util.c
@@ -0,0 +1,71 @@
#include "util.h"

s8 s8_trim_space(s8 s)
{
	while (s.len > 0 && ISSPACE(s.data[0])) { s.len--; s.data++; }
	while (s.len > 0 && ISSPACE(s.data[s.len - 1])) s.len--;
	return s;
}

void s8_split(s8 in, u8 c, s8 *lhs, s8 *rhs)
{
	ix i;
	s8 s = in;

	for (i = 0; i < s.len && s.data[i] != c; i++);
	s.len = i;

	if (lhs) *lhs = s;

	if (rhs) {
		i32 inc   = (s.len == in.len) ? 0 : 1;
		rhs->len  = in.len  - s.len - inc;
		rhs->data = in.data + s.len + inc;
	}
}

b32 s8_case_ignore_equal(s8 a, s8 b)
{
	b32 result = a.len == b.len;
	for (ix i = 0; result && i < a.len; i++)
		result &= (a.data[i] & ~0x20u) == (b.data[i] & ~0x20u);
	return result;
}

i64 s8_to_i64(s8 s)
{
	i64 result = 0, sign = 1;
	if (s.len && s.data[0] == '-') {
		sign = -1;
		s.data++;
		s.len--;
	}
	for (ix i = 0; i < s.len; i++) {
		if (!ISDIGIT(s.data[i]))
			break;
		result *= 10;
		result += s.data[i] - '0';
	}
	return sign * result;
}

u32 s8_hex_to_u32(s8 s)
{
	u32 res = 0;
	/* NOTE: clamp length to something valid for a u32 */
	s.len = MIN(s.len, 8);
	for (; s.len > 0; s.len--, s.data++) {
		res <<= 4;
		if (ISDIGIT(*s.data)) {
			res |= *s.data - '0';
		} else if (ISHEX(*s.data)) {
			/* NOTE: convert to lowercase first then convert to value */
			u8 byte = *s.data;
			byte |= 0x20u;
			res  |= byte - 0x57u;
		} else {
			/* NOTE: do nothing (treat invalid value as 0) */
		}
	}
	return res;
}
diff --git a/util.h b/util.h
index 08a44a8..303597e 100644
--- a/util.h
+++ b/util.h
@@ -1,16 +1,32 @@
#ifndef UTIL_H
#define UTIL_H

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

#define LENGTH(x)  ((int)(sizeof (x) / sizeof *(x)))
#define MIN(a, b)  ((a) > (b) ? (b) : (a))
#define MAX(a, b)  ((a) < (b) ? (b) : (a))

#define BETWEEN(x, a, b) ((x) >= (a) && (x) <= (b))

/* is c the start of a utf8 sequence? */
#define ISUTF8(c)   (((c)&0xC0)!=0x80)
#define ISASCII(ch) ((unsigned char)ch < 0x80)
#define ISDIGIT(c)  (BETWEEN((c), '0', '9'))
#define ISHEX(c)    (ISDIGIT((c)) || BETWEEN((c), 'a', 'f') || BETWEEN((c), 'A', 'F'))
#define ISSPACE(c)  ((c) == ' ' || (c) == '\t')

typedef uint8_t   u8;
typedef int32_t   i32;
typedef uint32_t  u32;
typedef uint32_t  b32;
typedef int64_t   i64;
typedef ptrdiff_t ix;

typedef struct { ix len; u8 *data; } s8;
#define s8(s) (s8){.len = (ix)sizeof(s) - 1, .data = (u8 *)s}

#if GCC_VERSION>=5004000 || CLANG_VERSION>=4000000
#define addu __builtin_add_overflow
@@ -44,4 +60,13 @@ static void *memrchr(const void *m, int c, size_t n)
#define PATH_MAX 4096
#endif

s8 s8_trim_space(s8);

void s8_split(s8, u8 cut_byte, s8 *lhs, s8 *rhs);

i64 s8_to_i64(s8);
u32 s8_hex_to_u32(s8);

b32 s8_case_ignore_equal(s8, s8);

#endif /* UTIL_H */
diff --git a/vis-core.h b/vis-core.h
index 182c537..6bcd3ce 100644
--- a/vis-core.h
+++ b/vis-core.h
@@ -2,6 +2,8 @@
#define VIS_CORE_H

#include <setjmp.h>
#include "util.h"

#include "vis.h"
#include "sam.h"
#include "vis-lua.h"
@@ -10,7 +12,6 @@
#include "map.h"
#include "array.h"
#include "buffer.h"
#include "util.h"

/* a mode contains a set of key bindings which are currently valid.
 *
diff --git a/vis-lua.c b/vis-lua.c
index d593bda..86f4ecb 100644
--- a/vis-lua.c
+++ b/vis-lua.c
@@ -21,10 +21,10 @@
#include <sys/types.h>
#include <pwd.h>

#include "util.h"
#include "vis-lua.h"
#include "vis-core.h"
#include "text-motions.h"
#include "util.h"

#ifndef VIS_PATH
#define VIS_PATH "/usr/local/share/vis"
@@ -2015,9 +2015,9 @@ static int window_unmap(lua_State *L) {
static int window_style_define(lua_State *L) {
	Win *win = obj_ref_check(L, 1, VIS_LUA_TYPE_WINDOW);
	enum UiStyle id = luaL_checkunsigned(L, 2);
	const char *style = luaL_checkstring(L, 3);
	bool ret = ui_style_define(win, id, style);
	lua_pushboolean(L, ret);
	size_t len;
	u8 *style = (u8 *)luaL_checklstring(L, 3, &len);
	lua_pushboolean(L, ui_style_define(win, id, (s8){.len = len, .data = style}));
	return 1;
}

diff --git a/vis-subprocess.c b/vis-subprocess.c
index e17d24f..8c181c5 100644
--- a/vis-subprocess.c
+++ b/vis-subprocess.c
@@ -4,9 +4,10 @@
#include <errno.h>
#include <string.h>
#include <sys/wait.h>

#include "util.h"
#include "vis-lua.h"
#include "vis-subprocess.h"
#include "util.h"

/* Pool of information about currently running subprocesses */
static Process *process_pool;
diff --git a/vis.c b/vis.c
index c059a48..4c6e098 100644
--- a/vis.c
+++ b/vis.c
@@ -20,11 +20,11 @@
#include <libgen.h>
#include <termkey.h>

#include "util.h"
#include "vis.h"
#include "text-util.h"
#include "text-motions.h"
#include "text-objects.h"
#include "util.h"
#include "vis-core.h"
#include "sam.h"
#include "ui.h"
-- 
2.45.2
Details
Message ID
<D6V7ZHAHLFAR.14K8YWITYOKCR@cepl.eu>
In-Reply-To
<20250106153748.1072-1-randy@rnpnr.xyz> (view parent)
DKIM signature
pass
Download raw message
On Mon Jan 6, 2025 at 4:38 PM CET, Randy Palamar wrote:
> This commit adds ~100 lines of code because I have provided my own
> implementation of some functions that we were previously provided
> by the C-runtime. This code will be reused later to simplify lots
> of other code in sam.c, map.c, vis-lua.c, and likely more. I can't
> say if this will be a net reduction of code in vis but I can
> guarantee that it will be a net reduction in total lines of code
> running on the users machine - C-runtime implementations are often
> massive over-complicated messes (even musl).

I have added this patch to my `devel` branch and so far it seems
to compile and I am writing this email with it. Let me use it for
some time and we’ll see.

Best,

Matěj

-- 
http://matej.ceplovi.cz/blog/, @mcepl@en.osm.town
GPG Finger: 3C76 A027 CA45 AD70 98B5  BC1D 7920 5802 880B C9D8
 
A philosopher like Plato, according to Luther’s colorful imagery,
remains like a cow who looks at a new door, refusing to enter?
Reply to thread Export thread (mbox)