- commit
- 52dda8a
- parent
- 55084ff
- author
- Eric Bower
- date
- 2025-10-13 14:18:26 -0400 EDT
refactor(daemon): we dont need to buffer pty read for partial utf8 ghostty already handles this for us.
1 files changed,
+7,
-52
+7,
-52
1@@ -86,10 +86,6 @@ const Session = struct {
2 vt_handler: VTHandler,
3 attached_clients: std.AutoHashMap(std.posix.fd_t, void),
4
5- // Buffer for incomplete UTF-8 sequences from previous read
6- utf8_partial: [3]u8,
7- utf8_partial_len: usize,
8-
9 fn deinit(self: *Session) void {
10 self.allocator.free(self.name);
11 self.vt.deinit(self.allocator);
12@@ -308,7 +304,7 @@ fn readCallback(
13 }
14
15 fn handleMessage(client: *Client, data: []const u8) !void {
16- std.debug.print("Received message from client fd={d}: {s}", .{ client.fd, data });
17+ std.debug.print("Received message from client fd={d}: {s}\n", .{ client.fd, data });
18
19 // Parse message type first for dispatching
20 const type_parsed = try protocol.parseMessageType(client.allocator, data);
21@@ -547,7 +543,7 @@ fn handleListSessions(ctx: *ServerContext, client: *Client) !void {
22
23 try response.appendSlice(client.allocator, "]}}\n");
24
25- std.debug.print("Sending list response to client fd={d}: {s}", .{ client.fd, response.items });
26+ std.debug.print("Sending list response to client fd={d}: {s}\n", .{ client.fd, response.items });
27
28 const written = posix.write(client.fd, response.items) catch |err| {
29 std.debug.print("Error writing to fd={d}: {s}\n", .{ client.fd, @errorName(err) });
30@@ -814,57 +810,18 @@ fn readPtyCallback(
31 return .disarm;
32 }
33
34- // Combine any partial UTF-8 from previous read with new data
35- var combined_buf: [4096 + 3]u8 = undefined;
36- const total_len = session.utf8_partial_len + bytes_read;
37-
38- if (session.utf8_partial_len > 0) {
39- @memcpy(combined_buf[0..session.utf8_partial_len], session.utf8_partial[0..session.utf8_partial_len]);
40- @memcpy(combined_buf[session.utf8_partial_len..total_len], read_buffer.slice[0..bytes_read]);
41- } else {
42- @memcpy(combined_buf[0..bytes_read], read_buffer.slice[0..bytes_read]);
43- }
44-
45- const data = combined_buf[0..total_len];
46- std.debug.print("PTY output ({d} bytes, {d} from partial)\n", .{ bytes_read, session.utf8_partial_len });
47-
48- // Check for incomplete UTF-8 sequence at end
49- var valid_len = total_len;
50- session.utf8_partial_len = 0;
51-
52- if (total_len > 0) {
53- // Scan backwards to find if we have a partial UTF-8 sequence
54- var i = total_len;
55- const scan_start = if (total_len >= 4) total_len - 4 else 0;
56- while (i > 0 and i > scan_start) {
57- i -= 1;
58- const byte = data[i];
59- // Check if this is a UTF-8 start byte
60- if (byte & 0x80 == 0) break; // ASCII, we're good
61- if (byte & 0xC0 == 0xC0) {
62- // This is a UTF-8 start byte, check if sequence is complete
63- const expected_len: usize = if (byte & 0xE0 == 0xC0) 2 else if (byte & 0xF0 == 0xE0) 3 else if (byte & 0xF8 == 0xF0) 4 else 1;
64- if (i + expected_len > total_len) {
65- // Save partial sequence for next read
66- session.utf8_partial_len = total_len - i;
67- @memcpy(session.utf8_partial[0..session.utf8_partial_len], data[i..total_len]);
68- valid_len = i;
69- }
70- break;
71- }
72- }
73- }
74-
75- const valid_data = data[0..valid_len];
76+ const total_len = bytes_read;
77+ const data = read_buffer.slice[0..bytes_read];
78+ std.debug.print("PTY output ({d} bytes)\n", .{bytes_read});
79
80 // Build a sanitized buffer that only includes bytes we can safely send
81- var sanitized_buf = std.ArrayList(u8).initCapacity(session.allocator, valid_len) catch return .disarm;
82+ var sanitized_buf = std.ArrayList(u8).initCapacity(session.allocator, total_len) catch return .disarm;
83 defer sanitized_buf.deinit(session.allocator);
84
85 // Parse through libghostty-vt byte-by-byte to handle invalid data
86 // This is necessary because binary data (like /dev/urandom) can cause
87 // panics in @enumFromInt when high bytes appear during escape sequences
88- for (valid_data) |byte| {
89+ for (data) |byte| {
90 // Skip high bytes when parser is not in ground state to avoid
91 // @enumFromInt panic in execute() which expects u7 (0-127)
92 if (session.vt_stream.parser.state != .ground and byte > 127) {
93@@ -1103,8 +1060,6 @@ fn createSession(allocator: std.mem.Allocator, session_name: []const u8) !*Sessi
94 },
95 .vt_stream = undefined,
96 .attached_clients = std.AutoHashMap(std.posix.fd_t, void).init(allocator),
97- .utf8_partial = undefined,
98- .utf8_partial_len = 0,
99 };
100
101 // Initialize the stream after session is created since handler needs terminal pointer