diff --git a/src/terminal/stream.zig b/src/terminal/stream.zig index 65c7cc5e0..37200a2c7 100644 --- a/src/terminal/stream.zig +++ b/src/terminal/stream.zig @@ -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");