perf: add branch hints based on real world data

+ move stream ESC state entry outside of `nextNonUtf8`
pull/9645/head
Qwerasd 2025-11-18 11:31:17 -07:00
parent 5744fb042c
commit 212598ed66
2 changed files with 398 additions and 194 deletions

View File

@ -645,6 +645,11 @@ pub fn Stream(comptime Handler: type) type {
try self.handleCodepoint(codepoint);
}
if (!consumed) {
// We optimize for the scenario where the text being
// printed in the terminal ISN'T full of ill-formed
// UTF-8 sequences.
@branchHint(.unlikely);
const retry = self.utf8decoder.next(c);
// It should be impossible for the decoder
// to not consume the byte twice in a row.
@ -665,12 +670,16 @@ pub fn Stream(comptime Handler: type) type {
// a chain of inline functions.
@setEvalBranchQuota(100_000);
// C0 control
if (c <= 0xF) {
@branchHint(.unlikely);
try self.execute(@intCast(c));
return;
}
// ESC
if (c == 0x1B) {
try self.nextNonUtf8(@intCast(c));
self.parser.state = .escape;
self.parser.clear();
return;
}
try self.print(@intCast(c));
@ -681,14 +690,8 @@ pub fn Stream(comptime Handler: type) type {
/// This assumes that we're not in the UTF-8 decoding state. If
/// we may be in the UTF-8 decoding state call nextSlice or next.
fn nextNonUtf8(self: *Self, c: u8) !void {
assert(self.parser.state != .ground or c == 0x1B);
assert(self.parser.state != .ground);
// Fast path for ESC
if (self.parser.state == .ground and c == 0x1B) {
self.parser.state = .escape;
self.parser.clear();
return;
}
// Fast path for CSI entry.
if (self.parser.state == .escape and c == '[') {
self.parser.state = .csi_entry;
@ -696,6 +699,11 @@ pub fn Stream(comptime Handler: type) type {
}
// Fast path for CSI params.
if (self.parser.state == .csi_param) csi_param: {
// csi_param is the most common parser state
// other than ground by a fairly wide margin.
//
// ref: https://github.com/qwerasd205/asciinema-stats
@branchHint(.likely);
switch (c) {
// A C0 escape (yes, this is valid):
0x00...0x0F => try self.execute(c),
@ -814,24 +822,52 @@ pub fn Stream(comptime Handler: type) type {
}
inline fn csiDispatch(self: *Self, input: Parser.Action.CSI) !void {
// The branch hints here are based on real world data
// which indicates that the most common CSI finals are:
//
// 1. m
// 2. H
// 3. K
// 4. A
// 5. C
// 6. X
// 7. l
// 8. h
// 9. r
//
// Together, these 9 finals make up about 96% of all
// CSI sequences encountered in real world scenarios.
//
// Additionally, within the prongs, unlikely branch
// hints have been added to branches that deal with
// invalid sequences/commands, this is in order to
// optimize for the happy path where we're getting
// valid data from the program we're running.
//
// ref: https://github.com/qwerasd205/asciinema-stats
switch (input.final) {
// CUU - Cursor Up
'A', 'k' => switch (input.intermediates.len) {
0 => try self.handler.vt(.cursor_up, .{
.value = switch (input.params.len) {
0 => 1,
1 => input.params[0],
else => {
log.warn("invalid cursor up command: {f}", .{input});
return;
'A', 'k' => {
@branchHint(.likely);
switch (input.intermediates.len) {
0 => try self.handler.vt(.cursor_up, .{
.value = switch (input.params.len) {
0 => 1,
1 => input.params[0],
else => {
@branchHint(.unlikely);
log.warn("invalid cursor up command: {f}", .{input});
return;
},
},
},
}),
}),
else => log.warn(
"ignoring unimplemented CSI A with intermediates: {s}",
.{input.intermediates},
),
else => log.warn(
"ignoring unimplemented CSI A with intermediates: {s}",
.{input.intermediates},
),
}
},
// CUD - Cursor Down
@ -841,6 +877,7 @@ pub fn Stream(comptime Handler: type) type {
0 => 1,
1 => input.params[0],
else => {
@branchHint(.unlikely);
log.warn("invalid cursor down command: {f}", .{input});
return;
},
@ -854,22 +891,26 @@ pub fn Stream(comptime Handler: type) type {
},
// CUF - Cursor Right
'C' => switch (input.intermediates.len) {
0 => try self.handler.vt(.cursor_right, .{
.value = switch (input.params.len) {
0 => 1,
1 => input.params[0],
else => {
log.warn("invalid cursor right command: {f}", .{input});
return;
'C' => {
@branchHint(.likely);
switch (input.intermediates.len) {
0 => try self.handler.vt(.cursor_right, .{
.value = switch (input.params.len) {
0 => 1,
1 => input.params[0],
else => {
@branchHint(.unlikely);
log.warn("invalid cursor right command: {f}", .{input});
return;
},
},
},
}),
}),
else => log.warn(
"ignoring unimplemented CSI C with intermediates: {s}",
.{input.intermediates},
),
else => log.warn(
"ignoring unimplemented CSI C with intermediates: {s}",
.{input.intermediates},
),
}
},
// CUB - Cursor Left
@ -879,6 +920,7 @@ pub fn Stream(comptime Handler: type) type {
0 => 1,
1 => input.params[0],
else => {
@branchHint(.unlikely);
log.warn("invalid cursor left command: {f}", .{input});
return;
},
@ -899,6 +941,7 @@ pub fn Stream(comptime Handler: type) type {
0 => 1,
1 => input.params[0],
else => {
@branchHint(.unlikely);
log.warn("invalid cursor up command: {f}", .{input});
return;
},
@ -921,6 +964,7 @@ pub fn Stream(comptime Handler: type) type {
0 => 1,
1 => input.params[0],
else => {
@branchHint(.unlikely);
log.warn("invalid cursor down command: {f}", .{input});
return;
},
@ -943,6 +987,7 @@ pub fn Stream(comptime Handler: type) type {
0 => 1,
1 => input.params[0],
else => {
@branchHint(.unlikely);
log.warn("invalid HPA command: {f}", .{input});
return;
},
@ -957,24 +1002,28 @@ pub fn Stream(comptime Handler: type) type {
// CUP - Set Cursor Position.
// TODO: test
'H', 'f' => switch (input.intermediates.len) {
0 => {
const pos: streampkg.Action.CursorPos = switch (input.params.len) {
0 => .{ .row = 1, .col = 1 },
1 => .{ .row = input.params[0], .col = 1 },
2 => .{ .row = input.params[0], .col = input.params[1] },
else => {
log.warn("invalid CUP command: {f}", .{input});
return;
},
};
try self.handler.vt(.cursor_pos, pos);
},
'H', 'f' => {
@branchHint(.likely);
switch (input.intermediates.len) {
0 => {
const pos: streampkg.Action.CursorPos = switch (input.params.len) {
0 => .{ .row = 1, .col = 1 },
1 => .{ .row = input.params[0], .col = 1 },
2 => .{ .row = input.params[0], .col = input.params[1] },
else => {
@branchHint(.unlikely);
log.warn("invalid CUP command: {f}", .{input});
return;
},
};
try self.handler.vt(.cursor_pos, pos);
},
else => log.warn(
"ignoring unimplemented CSI H with intermediates: {s}",
.{input.intermediates},
),
else => log.warn(
"ignoring unimplemented CSI H with intermediates: {s}",
.{input.intermediates},
),
}
},
// CHT - Cursor Horizontal Tabulation
@ -1029,6 +1078,7 @@ pub fn Stream(comptime Handler: type) type {
// Erase Line
'K' => {
@branchHint(.likely);
const protected_: ?bool = switch (input.intermediates.len) {
0 => false,
1 => if (input.intermediates[0] == '?') true else null,
@ -1036,6 +1086,7 @@ pub fn Stream(comptime Handler: type) type {
};
const protected = protected_ orelse {
@branchHint(.unlikely);
log.warn("invalid erase line command: {f}", .{input});
return;
};
@ -1047,6 +1098,7 @@ pub fn Stream(comptime Handler: type) type {
};
const mode = mode_ orelse {
@branchHint(.unlikely);
log.warn("invalid erase line command: {f}", .{input});
return;
};
@ -1056,7 +1108,10 @@ pub fn Stream(comptime Handler: type) type {
.left => try self.handler.vt(.erase_line_left, protected),
.complete => try self.handler.vt(.erase_line_complete, protected),
.right_unless_pending_wrap => try self.handler.vt(.erase_line_right_unless_pending_wrap, protected),
_ => log.warn("invalid erase line mode: {}", .{mode}),
_ => {
@branchHint(.unlikely);
log.warn("invalid erase line mode: {}", .{mode});
},
}
},
@ -1189,20 +1244,24 @@ pub fn Stream(comptime Handler: type) type {
},
// Erase Characters (ECH)
'X' => switch (input.intermediates.len) {
0 => try self.handler.vt(.erase_chars, switch (input.params.len) {
0 => 1,
1 => input.params[0],
else => {
log.warn("invalid erase characters command: {f}", .{input});
return;
},
}),
'X' => {
@branchHint(.likely);
switch (input.intermediates.len) {
0 => try self.handler.vt(.erase_chars, switch (input.params.len) {
0 => 1,
1 => input.params[0],
else => {
@branchHint(.unlikely);
log.warn("invalid erase characters command: {f}", .{input});
return;
},
}),
else => log.warn(
"ignoring unimplemented CSI X with intermediates: {s}",
.{input.intermediates},
),
else => log.warn(
"ignoring unimplemented CSI X with intermediates: {s}",
.{input.intermediates},
),
}
},
// CHT - Cursor Horizontal Tabulation Back
@ -1342,6 +1401,7 @@ pub fn Stream(comptime Handler: type) type {
// SM - Set Mode
'h' => mode: {
@branchHint(.likely);
const ansi_mode = ansi: {
if (input.intermediates.len == 0) break :ansi true;
if (input.intermediates.len == 1 and
@ -1362,6 +1422,7 @@ pub fn Stream(comptime Handler: type) type {
// RM - Reset Mode
'l' => mode: {
@branchHint(.likely);
const ansi_mode = ansi: {
if (input.intermediates.len == 0) break :ansi true;
if (input.intermediates.len == 1 and
@ -1381,81 +1442,86 @@ pub fn Stream(comptime Handler: type) type {
},
// SGR - Select Graphic Rendition
'm' => switch (input.intermediates.len) {
0 => {
// log.info("parse SGR params={any}", .{input.params});
var p: sgr.Parser = .{
.params = input.params,
.params_sep = input.params_sep,
};
while (p.next()) |attr| {
// log.info("SGR attribute: {}", .{attr});
try self.handler.vt(.set_attribute, attr);
}
},
1 => switch (input.intermediates[0]) {
'>' => blk: {
if (input.params.len == 0) {
// Reset
try self.handler.vt(.modify_key_format, .legacy);
break :blk;
}
var format: ansi.ModifyKeyFormat = switch (input.params[0]) {
0 => .legacy,
1 => .cursor_keys,
2 => .function_keys,
4 => .other_keys_none,
else => {
log.warn("invalid setModifyKeyFormat: {f}", .{input});
break :blk;
},
'm' => {
@branchHint(.likely);
switch (input.intermediates.len) {
0 => {
// This is the most common case.
@branchHint(.likely);
// log.info("parse SGR params={any}", .{input.params});
var p: sgr.Parser = .{
.params = input.params,
.params_sep = input.params_sep,
};
if (input.params.len > 2) {
log.warn("invalid setModifyKeyFormat: {f}", .{input});
break :blk;
while (p.next()) |attr| {
// log.info("SGR attribute: {}", .{attr});
try self.handler.vt(.set_attribute, attr);
}
if (input.params.len == 2) {
switch (format) {
// We don't support any of the subparams yet for these.
.legacy => {},
.cursor_keys => {},
.function_keys => {},
// We only support the numeric form.
.other_keys_none => switch (input.params[1]) {
2 => format = .other_keys_numeric,
else => {},
},
.other_keys_numeric_except => {},
.other_keys_numeric => {},
}
}
try self.handler.vt(.modify_key_format, format);
},
else => log.warn(
"unknown CSI m with intermediate: {}",
.{input.intermediates[0]},
),
},
1 => switch (input.intermediates[0]) {
'>' => blk: {
if (input.params.len == 0) {
// Reset
try self.handler.vt(.modify_key_format, .legacy);
break :blk;
}
else => {
// Nothing, but I wanted a place to put this comment:
// there are others forms of CSI m that have intermediates.
// `vim --clean` uses `CSI ? 4 m` and I don't know what
// that means. And there is also `CSI > m` which is used
// to control modifier key reporting formats that we don't
// support yet.
log.warn(
"ignoring unimplemented CSI m with intermediates: {s}",
.{input.intermediates},
);
},
var format: ansi.ModifyKeyFormat = switch (input.params[0]) {
0 => .legacy,
1 => .cursor_keys,
2 => .function_keys,
4 => .other_keys_none,
else => {
@branchHint(.unlikely);
log.warn("invalid setModifyKeyFormat: {f}", .{input});
break :blk;
},
};
if (input.params.len > 2) {
@branchHint(.unlikely);
log.warn("invalid setModifyKeyFormat: {f}", .{input});
break :blk;
}
if (input.params.len == 2) {
switch (format) {
// We don't support any of the subparams yet for these.
.legacy => {},
.cursor_keys => {},
.function_keys => {},
// We only support the numeric form.
.other_keys_none => switch (input.params[1]) {
2 => format = .other_keys_numeric,
else => {},
},
.other_keys_numeric_except => {},
.other_keys_numeric => {},
}
}
try self.handler.vt(.modify_key_format, format);
},
else => log.warn(
"unknown CSI m with intermediate: {}",
.{input.intermediates[0]},
),
},
else => {
// Nothing, but I wanted a place to put this comment:
// there are others forms of CSI m that have intermediates.
// `vim --clean` uses `CSI ? 4 m` and I don't know what
// that means.
log.warn(
"ignoring unimplemented CSI m with intermediates: {s}",
.{input.intermediates},
);
},
}
},
// TODO: test
@ -1622,40 +1688,46 @@ pub fn Stream(comptime Handler: type) type {
),
},
'r' => switch (input.intermediates.len) {
// DECSTBM - Set Top and Bottom Margins
0 => switch (input.params.len) {
0 => try self.handler.vt(.top_and_bottom_margin, .{ .top_left = 0, .bottom_right = 0 }),
1 => try self.handler.vt(.top_and_bottom_margin, .{ .top_left = input.params[0], .bottom_right = 0 }),
2 => try self.handler.vt(.top_and_bottom_margin, .{ .top_left = input.params[0], .bottom_right = input.params[1] }),
else => log.warn("invalid DECSTBM command: {f}", .{input}),
},
'r' => {
@branchHint(.likely);
switch (input.intermediates.len) {
// DECSTBM - Set Top and Bottom Margins
0 => switch (input.params.len) {
0 => try self.handler.vt(.top_and_bottom_margin, .{ .top_left = 0, .bottom_right = 0 }),
1 => try self.handler.vt(.top_and_bottom_margin, .{ .top_left = input.params[0], .bottom_right = 0 }),
2 => try self.handler.vt(.top_and_bottom_margin, .{ .top_left = input.params[0], .bottom_right = input.params[1] }),
else => {
@branchHint(.unlikely);
log.warn("invalid DECSTBM command: {f}", .{input});
},
},
1 => switch (input.intermediates[0]) {
// Restore Mode
'?' => {
for (input.params) |mode_int| {
if (modes.modeFromInt(mode_int, false)) |mode| {
try self.handler.vt(.restore_mode, .{ .mode = mode });
} else {
log.warn(
"unimplemented restore mode: {}",
.{mode_int},
);
1 => switch (input.intermediates[0]) {
// Restore Mode
'?' => {
for (input.params) |mode_int| {
if (modes.modeFromInt(mode_int, false)) |mode| {
try self.handler.vt(.restore_mode, .{ .mode = mode });
} else {
log.warn(
"unimplemented restore mode: {}",
.{mode_int},
);
}
}
}
},
else => log.warn(
"unknown CSI s with intermediate: {f}",
.{input},
),
},
else => log.warn(
"unknown CSI s with intermediate: {f}",
"ignoring unimplemented CSI s with intermediates: {f}",
.{input},
),
},
else => log.warn(
"ignoring unimplemented CSI s with intermediates: {f}",
.{input},
),
}
},
's' => switch (input.intermediates.len) {
@ -1866,6 +1938,7 @@ pub fn Stream(comptime Handler: type) type {
0 => 1,
1 => input.params[0],
else => {
@branchHint(.unlikely);
log.warn("invalid ICH command: {f}", .{input});
return;
},
@ -1906,9 +1979,34 @@ pub fn Stream(comptime Handler: type) type {
}
inline fn oscDispatch(self: *Self, cmd: osc.Command) !void {
// The branch hints here are based on real world data
// which indicates that the most common OSC commands are:
//
// 1. hyperlink_end
// 2. change_window_title
// 3. change_window_icon
// 4. hyperlink_start
// 5. report_pwd
// 6. color_operation
// 7. prompt_start
// 8. prompt_end
//
// Together, these 8 commands make up about 96% of all
// OSC commands encountered in real world scenarios.
//
// Additionally, within the prongs, unlikely branch
// hints have been added to branches that deal with
// invalid sequences/commands, this is in order to
// optimize for the happy path where we're getting
// valid data from the program we're running.
//
// ref: https://github.com/qwerasd205/asciinema-stats
switch (cmd) {
.change_window_title => |title| {
@branchHint(.likely);
if (!std.unicode.utf8ValidateSlice(title)) {
@branchHint(.unlikely);
log.warn("change title request: invalid utf-8, ignoring request", .{});
return;
}
@ -1917,6 +2015,7 @@ pub fn Stream(comptime Handler: type) type {
},
.change_window_icon => |icon| {
@branchHint(.likely);
log.info("OSC 1 (change icon) received and ignored icon={s}", .{icon});
},
@ -1928,6 +2027,7 @@ pub fn Stream(comptime Handler: type) type {
},
.prompt_start => |v| {
@branchHint(.likely);
switch (v.kind) {
.primary, .right => try self.handler.vt(.prompt_start, .{
.aid = v.aid,
@ -1939,7 +2039,10 @@ pub fn Stream(comptime Handler: type) type {
}
},
.prompt_end => try self.handler.vt(.prompt_end, {}),
.prompt_end => {
@branchHint(.likely);
try self.handler.vt(.prompt_end, {});
},
.end_of_input => try self.handler.vt(.end_of_input, {}),
@ -1948,11 +2051,13 @@ pub fn Stream(comptime Handler: type) type {
},
.report_pwd => |v| {
@branchHint(.likely);
try self.handler.vt(.report_pwd, .{ .url = v.value });
},
.mouse_shape => |v| {
const shape = MouseShape.fromString(v.value) orelse {
@branchHint(.unlikely);
log.warn("unknown cursor shape: {s}", .{v.value});
return;
};
@ -1961,6 +2066,7 @@ pub fn Stream(comptime Handler: type) type {
},
.color_operation => |v| {
@branchHint(.likely);
try self.handler.vt(.color_operation, .{
.op = v.op,
.requests = v.requests,
@ -1980,6 +2086,7 @@ pub fn Stream(comptime Handler: type) type {
},
.hyperlink_start => |v| {
@branchHint(.likely);
try self.handler.vt(.start_hyperlink, .{
.uri = v.uri,
.id = v.id,
@ -1987,6 +2094,7 @@ pub fn Stream(comptime Handler: type) type {
},
.hyperlink_end => {
@branchHint(.likely);
try self.handler.vt(.end_hyperlink, {});
},
@ -2004,6 +2112,7 @@ pub fn Stream(comptime Handler: type) type {
},
.invalid => {
@branchHint(.cold);
// This is an invalid internal state, not an invalid OSC
// string being parsed. We shouldn't see this.
log.warn("invalid OSC, should never happen", .{});
@ -2029,6 +2138,7 @@ pub fn Stream(comptime Handler: type) type {
'*' => .G2,
'+' => .G3,
else => {
@branchHint(.unlikely);
log.warn("invalid charset intermediate: {any}", .{intermediates});
return;
},
@ -2044,22 +2154,56 @@ pub fn Stream(comptime Handler: type) type {
self: *Self,
action: Parser.Action.ESC,
) !void {
// The branch hints here are based on real world data
// which indicates that the most common ESC finals are:
//
// 1. B
// 2. \
// 3. 0
// 4. M
// 5. 8
// 6. 7
// 7. >
// 8. =
//
// Together, these 8 finals make up nearly 99% of all
// ESC sequences encountered in real world scenarios.
//
// Additionally, within the prongs, unlikely branch
// hints have been added to branches that deal with
// invalid sequences/commands, this is in order to
// optimize for the happy path where we're getting
// valid data from the program we're running.
//
// ref: https://github.com/qwerasd205/asciinema-stats
switch (action.final) {
// Charsets
'B' => try self.configureCharset(action.intermediates, .ascii),
'B' => {
@branchHint(.likely);
try self.configureCharset(action.intermediates, .ascii);
},
'A' => try self.configureCharset(action.intermediates, .british),
'0' => try self.configureCharset(action.intermediates, .dec_special),
'0' => {
@branchHint(.likely);
try self.configureCharset(action.intermediates, .dec_special);
},
// DECSC - Save Cursor
'7' => switch (action.intermediates.len) {
0 => try self.handler.vt(.save_cursor, {}),
else => {
log.warn("invalid command: {f}", .{action});
return;
},
'7' => {
@branchHint(.likely);
switch (action.intermediates.len) {
0 => try self.handler.vt(.save_cursor, {}),
else => {
@branchHint(.unlikely);
log.warn("invalid command: {f}", .{action});
return;
},
}
},
'8' => blk: {
@branchHint(.likely);
switch (action.intermediates.len) {
// DECRC - Restore Cursor
0 => {
@ -2087,6 +2231,7 @@ pub fn Stream(comptime Handler: type) type {
'D' => switch (action.intermediates.len) {
0 => try self.handler.vt(.index, {}),
else => {
@branchHint(.unlikely);
log.warn("invalid index command: {f}", .{action});
return;
},
@ -2096,6 +2241,7 @@ pub fn Stream(comptime Handler: type) type {
'E' => switch (action.intermediates.len) {
0 => try self.handler.vt(.next_line, {}),
else => {
@branchHint(.unlikely);
log.warn("invalid next line command: {f}", .{action});
return;
},
@ -2105,18 +2251,23 @@ pub fn Stream(comptime Handler: type) type {
'H' => switch (action.intermediates.len) {
0 => try self.handler.vt(.tab_set, {}),
else => {
@branchHint(.unlikely);
log.warn("invalid tab set command: {f}", .{action});
return;
},
},
// RI - Reverse Index
'M' => switch (action.intermediates.len) {
0 => try self.handler.vt(.reverse_index, {}),
else => {
log.warn("invalid reverse index command: {f}", .{action});
return;
},
'M' => {
@branchHint(.likely);
switch (action.intermediates.len) {
0 => try self.handler.vt(.reverse_index, {}),
else => {
@branchHint(.unlikely);
log.warn("invalid reverse index command: {f}", .{action});
return;
},
}
},
// SS2 - Single Shift 2
@ -2127,6 +2278,7 @@ pub fn Stream(comptime Handler: type) type {
.locking = true,
}),
else => {
@branchHint(.unlikely);
log.warn("invalid single shift 2 command: {f}", .{action});
return;
},
@ -2140,6 +2292,7 @@ pub fn Stream(comptime Handler: type) type {
.locking = true,
}),
else => {
@branchHint(.unlikely);
log.warn("invalid single shift 3 command: {f}", .{action});
return;
},
@ -2179,6 +2332,7 @@ pub fn Stream(comptime Handler: type) type {
.locking = false,
}),
else => {
@branchHint(.unlikely);
log.warn("invalid single shift 2 command: {f}", .{action});
return;
},
@ -2192,6 +2346,7 @@ pub fn Stream(comptime Handler: type) type {
.locking = false,
}),
else => {
@branchHint(.unlikely);
log.warn("invalid single shift 3 command: {f}", .{action});
return;
},
@ -2205,6 +2360,7 @@ pub fn Stream(comptime Handler: type) type {
.locking = false,
}),
else => {
@branchHint(.unlikely);
log.warn("invalid locking shift 1 right command: {f}", .{action});
return;
},
@ -2218,6 +2374,7 @@ pub fn Stream(comptime Handler: type) type {
.locking = false,
}),
else => {
@branchHint(.unlikely);
log.warn("invalid locking shift 2 right command: {f}", .{action});
return;
},
@ -2231,26 +2388,35 @@ pub fn Stream(comptime Handler: type) type {
.locking = false,
}),
else => {
@branchHint(.unlikely);
log.warn("invalid locking shift 3 right command: {f}", .{action});
return;
},
},
// Set application keypad mode
'=' => switch (action.intermediates.len) {
0 => try self.handler.vt(.set_mode, .{ .mode = .keypad_keys }),
else => log.warn("unimplemented setMode: {f}", .{action}),
'=' => {
@branchHint(.likely);
switch (action.intermediates.len) {
0 => try self.handler.vt(.set_mode, .{ .mode = .keypad_keys }),
else => log.warn("unimplemented setMode: {f}", .{action}),
}
},
// Reset application keypad mode
'>' => switch (action.intermediates.len) {
0 => try self.handler.vt(.reset_mode, .{ .mode = .keypad_keys }),
else => log.warn("unimplemented setMode: {f}", .{action}),
'>' => {
@branchHint(.likely);
switch (action.intermediates.len) {
0 => try self.handler.vt(.reset_mode, .{ .mode = .keypad_keys }),
else => log.warn("unimplemented setMode: {f}", .{action}),
}
},
// Sets ST (string terminator). We don't have to do anything
// because our parser always accepts ST.
'\\' => {},
'\\' => {
@branchHint(.likely);
},
else => log.warn("unimplemented ESC action: {f}", .{action}),
}

View File

@ -165,22 +165,47 @@ pub const StreamHandler = struct {
comptime action: Stream.Action.Tag,
value: Stream.Action.Value(action),
) !void {
// The branch hints here are based on real world data
// which indicates that the most common actions are:
//
// 1. print
// 2. set_attribute
// 3. carriage_return
// 4. line_feed
// 5. cursor_pos
//
// Together, these 5 actions make up nearly 98% of
// all actions encountered in real world scenarios.
//
// ref: https://github.com/qwerasd205/asciinema-stats
switch (action) {
.print => try self.terminal.print(value.cp),
.print => {
@branchHint(.likely);
try self.terminal.print(value.cp);
},
.print_repeat => try self.terminal.printRepeat(value),
.bell => self.bell(),
.backspace => self.terminal.backspace(),
.horizontal_tab => try self.horizontalTab(value),
.horizontal_tab_back => try self.horizontalTabBack(value),
.linefeed => try self.linefeed(),
.carriage_return => self.terminal.carriageReturn(),
.linefeed => {
@branchHint(.likely);
try self.linefeed();
},
.carriage_return => {
@branchHint(.likely);
self.terminal.carriageReturn();
},
.enquiry => try self.enquiry(),
.invoke_charset => self.terminal.invokeCharset(value.bank, value.charset, value.locking),
.cursor_up => self.terminal.cursorUp(value.value),
.cursor_down => self.terminal.cursorDown(value.value),
.cursor_left => self.terminal.cursorLeft(value.value),
.cursor_right => self.terminal.cursorRight(value.value),
.cursor_pos => self.terminal.setCursorPos(value.row, value.col),
.cursor_pos => {
@branchHint(.likely);
self.terminal.setCursorPos(value.row, value.col);
},
.cursor_col => self.terminal.setCursorPos(self.terminal.screens.active.cursor.y + 1, value.value),
.cursor_row => self.terminal.setCursorPos(value.value, self.terminal.screens.active.cursor.x + 1),
.cursor_col_relative => self.terminal.setCursorPos(
@ -290,10 +315,23 @@ pub const StreamHandler = struct {
.end_of_command => self.endOfCommand(value.exit_code),
.mouse_shape => try self.setMouseShape(value),
.configure_charset => self.configureCharset(value.slot, value.charset),
.set_attribute => switch (value) {
.unknown => |unk| log.warn("unimplemented or unknown SGR attribute: {any}", .{unk}),
else => self.terminal.setAttribute(value) catch |err|
log.warn("error setting attribute {}: {}", .{ value, err }),
.set_attribute => {
@branchHint(.likely);
switch (value) {
.unknown => |unk| {
// We optimize for the happy path scenario here, since
// unknown/invalid SGRs aren't that common in the wild.
@branchHint(.unlikely);
log.warn("unimplemented or unknown SGR attribute: {any}", .{unk});
},
else => {
@branchHint(.likely);
self.terminal.setAttribute(value) catch |err| {
@branchHint(.cold);
log.warn("error setting attribute {}: {}", .{ value, err });
};
},
}
},
.dcs_hook => try self.dcsHook(value),
.dcs_put => try self.dcsPut(value),