perf: improve equality checks
Improve equality checks during render calls by creating bespoke eql methods or using std.mem instead of std.meta
This commit is contained in:
parent
019669d2f0
commit
cb685f3780
4 changed files with 70 additions and 10 deletions
52
src/Cell.zig
52
src/Cell.zig
|
@ -1,9 +1,11 @@
|
||||||
|
const std = @import("std");
|
||||||
const Image = @import("Image.zig");
|
const Image = @import("Image.zig");
|
||||||
|
|
||||||
char: Character = .{},
|
char: Character = .{},
|
||||||
style: Style = .{},
|
style: Style = .{},
|
||||||
link: Hyperlink = .{},
|
link: Hyperlink = .{},
|
||||||
image: ?Image.Placement = null,
|
image: ?Image.Placement = null,
|
||||||
|
default: bool = false,
|
||||||
|
|
||||||
/// Segment is a contiguous run of text that has a constant style
|
/// Segment is a contiguous run of text that has a constant style
|
||||||
pub const Segment = struct {
|
pub const Segment = struct {
|
||||||
|
@ -59,6 +61,43 @@ pub const Style = struct {
|
||||||
reverse: bool = false,
|
reverse: bool = false,
|
||||||
invisible: bool = false,
|
invisible: bool = false,
|
||||||
strikethrough: bool = false,
|
strikethrough: bool = false,
|
||||||
|
|
||||||
|
pub fn eql(a: Style, b: Style) bool {
|
||||||
|
const SGRBits = packed struct {
|
||||||
|
bold: bool,
|
||||||
|
dim: bool,
|
||||||
|
italic: bool,
|
||||||
|
blink: bool,
|
||||||
|
reverse: bool,
|
||||||
|
invisible: bool,
|
||||||
|
strikethrough: bool,
|
||||||
|
};
|
||||||
|
const a_sgr: SGRBits = .{
|
||||||
|
.bold = a.bold,
|
||||||
|
.dim = a.dim,
|
||||||
|
.italic = a.italic,
|
||||||
|
.blink = a.blink,
|
||||||
|
.reverse = a.reverse,
|
||||||
|
.invisible = a.invisible,
|
||||||
|
.strikethrough = a.strikethrough,
|
||||||
|
};
|
||||||
|
const b_sgr: SGRBits = .{
|
||||||
|
.bold = b.bold,
|
||||||
|
.dim = b.dim,
|
||||||
|
.italic = b.italic,
|
||||||
|
.blink = b.blink,
|
||||||
|
.reverse = b.reverse,
|
||||||
|
.invisible = b.invisible,
|
||||||
|
.strikethrough = b.strikethrough,
|
||||||
|
};
|
||||||
|
const a_cast: u7 = @bitCast(a_sgr);
|
||||||
|
const b_cast: u7 = @bitCast(b_sgr);
|
||||||
|
return a_cast == b_cast and
|
||||||
|
Color.eql(a.fg, b.fg) and
|
||||||
|
Color.eql(a.bg, b.bg) and
|
||||||
|
Color.eql(a.ul, b.ul) and
|
||||||
|
a.ul_style == b.ul_style;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Color = union(enum) {
|
pub const Color = union(enum) {
|
||||||
|
@ -66,6 +105,19 @@ pub const Color = union(enum) {
|
||||||
index: u8,
|
index: u8,
|
||||||
rgb: [3]u8,
|
rgb: [3]u8,
|
||||||
|
|
||||||
|
pub fn eql(a: Color, b: Color) bool {
|
||||||
|
if (a == .default and b == .default)
|
||||||
|
return true
|
||||||
|
else if (a == .index and b == .index)
|
||||||
|
return a.index == b.index
|
||||||
|
else if (a == .rgb and b == .rgb)
|
||||||
|
return a.rgb[0] == b.rgb[0] and
|
||||||
|
a.rgb[1] == b.rgb[1] and
|
||||||
|
a.rgb[2] == b.rgb[2]
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn rgbFromUint(val: u24) Color {
|
pub fn rgbFromUint(val: u24) Color {
|
||||||
const r_bits = val & 0b11111111_00000000_00000000;
|
const r_bits = val & 0b11111111_00000000_00000000;
|
||||||
const g_bits = val & 0b00000000_11111111_00000000;
|
const g_bits = val & 0b00000000_11111111_00000000;
|
||||||
|
|
|
@ -16,12 +16,19 @@ pub const InternalCell = struct {
|
||||||
uri_id: std.ArrayList(u8) = undefined,
|
uri_id: std.ArrayList(u8) = undefined,
|
||||||
// if we got skipped because of a wide character
|
// if we got skipped because of a wide character
|
||||||
skipped: bool = false,
|
skipped: bool = false,
|
||||||
|
default: bool = false,
|
||||||
|
|
||||||
pub fn eql(self: InternalCell, cell: Cell) bool {
|
pub fn eql(self: InternalCell, cell: Cell) bool {
|
||||||
return std.mem.eql(u8, self.char.items, cell.char.grapheme) and
|
// fastpath when both cells are default
|
||||||
std.meta.eql(self.style, cell.style) and
|
if (self.default and cell.default) return true;
|
||||||
std.mem.eql(u8, self.uri.items, cell.link.uri) and
|
// this is actually faster than std.meta.eql on the individual items.
|
||||||
std.mem.eql(u8, self.uri_id.items, cell.link.params);
|
// Our strings are always small, usually less than 4 bytes so the simd
|
||||||
|
// usage in std.mem.eql has too much overhead vs looping the bytes
|
||||||
|
if (!std.mem.eql(u8, self.char.items, cell.char.grapheme)) return false;
|
||||||
|
if (!Style.eql(self.style, cell.style)) return false;
|
||||||
|
if (!std.mem.eql(u8, self.uri.items, cell.link.uri)) return false;
|
||||||
|
if (!std.mem.eql(u8, self.uri_id.items, cell.link.params)) return false;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -94,6 +101,7 @@ pub fn writeCell(
|
||||||
log.warn("couldn't write uri_id", .{});
|
log.warn("couldn't write uri_id", .{});
|
||||||
};
|
};
|
||||||
self.buf[i].style = cell.style;
|
self.buf[i].style = cell.style;
|
||||||
|
self.buf[i].default = cell.default;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn readCell(self: *InternalScreen, col: usize, row: usize) ?Cell {
|
pub fn readCell(self: *InternalScreen, col: usize, row: usize) ?Cell {
|
||||||
|
|
|
@ -323,7 +323,7 @@ pub fn render(self: *Vaxis) !void {
|
||||||
// find out what
|
// find out what
|
||||||
|
|
||||||
// foreground
|
// foreground
|
||||||
if (!std.meta.eql(cursor.fg, cell.style.fg)) {
|
if (!Cell.Color.eql(cursor.fg, cell.style.fg)) {
|
||||||
const writer = tty.buffered_writer.writer();
|
const writer = tty.buffered_writer.writer();
|
||||||
switch (cell.style.fg) {
|
switch (cell.style.fg) {
|
||||||
.default => _ = try tty.write(ctlseqs.fg_reset),
|
.default => _ = try tty.write(ctlseqs.fg_reset),
|
||||||
|
@ -340,7 +340,7 @@ pub fn render(self: *Vaxis) !void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// background
|
// background
|
||||||
if (!std.meta.eql(cursor.bg, cell.style.bg)) {
|
if (!Cell.Color.eql(cursor.bg, cell.style.bg)) {
|
||||||
const writer = tty.buffered_writer.writer();
|
const writer = tty.buffered_writer.writer();
|
||||||
switch (cell.style.bg) {
|
switch (cell.style.bg) {
|
||||||
.default => _ = try tty.write(ctlseqs.bg_reset),
|
.default => _ = try tty.write(ctlseqs.bg_reset),
|
||||||
|
@ -357,7 +357,7 @@ pub fn render(self: *Vaxis) !void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// underline color
|
// underline color
|
||||||
if (!std.meta.eql(cursor.ul, cell.style.ul)) {
|
if (!Cell.Color.eql(cursor.ul, cell.style.ul)) {
|
||||||
const writer = tty.buffered_writer.writer();
|
const writer = tty.buffered_writer.writer();
|
||||||
switch (cell.style.bg) {
|
switch (cell.style.bg) {
|
||||||
.default => _ = try tty.write(ctlseqs.ul_reset),
|
.default => _ = try tty.write(ctlseqs.ul_reset),
|
||||||
|
@ -370,7 +370,7 @@ pub fn render(self: *Vaxis) !void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// underline style
|
// underline style
|
||||||
if (!std.meta.eql(cursor.ul_style, cell.style.ul_style)) {
|
if (cursor.ul_style != cell.style.ul_style) {
|
||||||
const seq = switch (cell.style.ul_style) {
|
const seq = switch (cell.style.ul_style) {
|
||||||
.off => ctlseqs.ul_off,
|
.off => ctlseqs.ul_off,
|
||||||
.single => ctlseqs.ul_single,
|
.single => ctlseqs.ul_single,
|
||||||
|
@ -445,7 +445,7 @@ pub fn render(self: *Vaxis) !void {
|
||||||
}
|
}
|
||||||
|
|
||||||
// url
|
// url
|
||||||
if (!std.meta.eql(link.uri, cell.link.uri)) {
|
if (!std.mem.eql(u8, link.uri, cell.link.uri)) {
|
||||||
var ps = cell.link.params;
|
var ps = cell.link.params;
|
||||||
if (cell.link.uri.len == 0) {
|
if (cell.link.uri.len == 0) {
|
||||||
// Empty out the params no matter what if we don't have
|
// Empty out the params no matter what if we don't have
|
||||||
|
|
|
@ -196,7 +196,7 @@ pub fn writeCell(self: Window, col: usize, row: usize, cell: Cell) void {
|
||||||
|
|
||||||
/// fills the window with the default cell
|
/// fills the window with the default cell
|
||||||
pub fn clear(self: Window) void {
|
pub fn clear(self: Window) void {
|
||||||
self.fill(.{});
|
self.fill(.{ .default = true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// returns the width of the grapheme. This depends on the terminal capabilities
|
/// returns the width of the grapheme. This depends on the terminal capabilities
|
||||||
|
|
Loading…
Reference in a new issue