perf: add branch hints based on real world data
+ move stream ESC state entry outside of `nextNonUtf8`pull/9645/head
parent
5744fb042c
commit
212598ed66
|
|
@ -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}),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
Loading…
Reference in New Issue