terminal/stream: fast-path APC termination

APC payload bytes are bulk consumed, but the terminating byte still passed 
through the generic parser action loop. Handle ESC and C1 ST directly after 
bulk consumption while leaving other transitions on the scalar path.
This commit is contained in:
Mitchell Hashimoto
2026-08-08 08:14:58 -07:00
parent 2602886144
commit afb351f838

View File

@@ -766,8 +766,26 @@ pub fn Stream(comptime H: type) type {
if (self.parser.state == .sos_pm_apc_string) {
offset += self.consumeApcString(input[offset..]);
if (offset >= input.len) return input.len;
// The next byte exits the string state; let
// nextNonUtf8 below handle it.
// Fast-path normal string termination. This matches
// Parser.next's exit and entry actions while avoiding
// the generic action loop for every completed APC.
switch (input[offset]) {
std.ascii.control_code.esc => {
self.parser.clear();
self.parser.state = .escape;
self.handler.vt(.apc_end, {});
offset += 1;
continue;
},
0x9C => {
self.parser.state = .ground;
self.handler.vt(.apc_end, {});
offset += 1;
continue;
},
else => {},
}
}
}
@@ -3989,6 +4007,18 @@ test "stream: apc bulk slice" {
}
}
test "stream: apc bulk slice C1 ST" {
var s: Stream(ApcTestHandler) = .init(.{ .handler = .{} });
s.nextSlice("\x1b_Gpayload\x9c");
try testing.expectEqual(@as(usize, 1), s.handler.started);
try testing.expectEqual(@as(usize, 1), s.handler.ended);
try testing.expectEqualStrings(
"Gpayload",
s.handler.buf[0..s.handler.len],
);
}
test "stream: apc bulk slice split across inputs" {
var s: Stream(ApcTestHandler) = .init(.{ .handler = .{} });
s.nextSlice("\x1b_Gf=24,s=10");