const std = @import("std"); const Allocator = std.mem.Allocator;const max_input_bytes = 4096; pub const Runtime = struct { expected_terminal_row: u16 = 0, input: std.ArrayList(u8) = .empty, is_active: bool = false, is_disabled: bool = false, late_response_pending: bool = false, pub fn deinit(self: *Runtime, alloc: Allocator) void { self.input.deinit(alloc); } pub fn active(self: Runtime) bool { return self.is_active; } pub fn disabled(self: Runtime) bool { return self.is_disabled; } pub fn expectedRow(self: Runtime) u16 { std.debug.assert(self.is_active); return self.expected_terminal_row; } pub fn retainedInput(self: Runtime) []const u8 { return self.input.items; } pub fn begin( self: *Runtime, alloc: Allocator, expected_row: u16, first_byte: u8, ) !void { std.debug.assert(!self.is_active); std.debug.assert(expected_row > 0); self.input.clearRetainingCapacity(); try self.input.append(alloc, first_byte); self.expected_terminal_row = expected_row; self.is_active = true; self.late_response_pending = false; } pub fn canRetainBytes(self: Runtime, byte_len: usize) bool { return byte_len <= max_input_bytes -| self.input.items.len; } pub fn retainBytes( self: *Runtime, alloc: Allocator, bytes: []const u8, ) !void { std.debug.assert(self.is_active); if (!self.canRetainBytes(bytes.len)) return error.NativeClearProbeInputOverflow; try self.input.appendSlice(alloc, bytes); } pub fn settle(self: *Runtime) []const u8 { std.debug.assert(self.is_active); self.is_active = false; self.expected_terminal_row = 0; return self.input.items; } pub fn clearSettledInput(self: *Runtime) void { std.debug.assert(!self.is_active); self.input.clearRetainingCapacity(); } pub fn disable(self: *Runtime, await_late_response: bool) void { self.is_disabled = true; self.late_response_pending = await_late_response; } pub fn awaitingLateResponse(self: Runtime) bool { return self.late_response_pending; } pub fn finishLateResponse(self: *Runtime) void { self.late_response_pending = false; }}; test "native clear probe retains input until it settles" { var runtime = Runtime{}; defer runtime.deinit(std.testing.allocator); try runtime.begin(std.testing.allocator, 17, 'a'); try runtime.retainBytes(std.testing.allocator, "bc"); try std.testing.expect(runtime.active()); try std.testing.expectEqualStrings("abc", runtime.retainedInput()); const retained = runtime.settle(); try std.testing.expectEqualStrings("abc", retained); try std.testing.expect(!runtime.active());} test "native clear probe bounds held input" { const alloc = std.testing.allocator; var runtime = Runtime{}; defer runtime.deinit(alloc); try runtime.begin(alloc, 17, 'a'); const fill = try alloc.alloc(u8, max_input_bytes - 1); defer alloc.free(fill); @memset(fill, 'b'); try runtime.retainBytes(alloc, fill); try std.testing.expect(!runtime.canRetainBytes(1)); try std.testing.expectError( error.NativeClearProbeInputOverflow, runtime.retainBytes(alloc, "c"), );} test "native clear probe records disabled late-response window" { var runtime = Runtime{}; try std.testing.expect(!runtime.disabled()); try std.testing.expect(!runtime.awaitingLateResponse()); runtime.disable(true); try std.testing.expect(runtime.disabled()); try std.testing.expect(runtime.awaitingLateResponse()); runtime.finishLateResponse(); try std.testing.expect(!runtime.awaitingLateResponse());}
const std = @import("std");
const Allocator = std.mem.Allocator;
const max_input_bytes = 4096;
pub const Runtime = struct {
expected_terminal_row: u16 = 0,
input: std.ArrayList(u8) = .empty,
is_active: bool = false,
is_disabled: bool = false,
late_response_pending: bool = false,
pub fn deinit(self: *Runtime, alloc: Allocator) void {
self.input.deinit(alloc);
}
pub fn active(self: Runtime) bool {
return self.is_active;
}
pub fn disabled(self: Runtime) bool {
return self.is_disabled;
}
pub fn expectedRow(self: Runtime) u16 {
std.debug.assert(self.is_active);
return self.expected_terminal_row;
}
pub fn retainedInput(self: Runtime) []const u8 {
return self.input.items;
}
pub fn begin(
self: *Runtime,
alloc: Allocator,
expected_row: u16,
first_byte: u8,
) !void {
std.debug.assert(!self.is_active);
std.debug.assert(expected_row > 0);
self.input.clearRetainingCapacity();
try self.input.append(alloc, first_byte);
self.expected_terminal_row = expected_row;
self.is_active = true;
self.late_response_pending = false;
}
pub fn canRetainBytes(self: Runtime, byte_len: usize) bool {
return byte_len <= max_input_bytes -| self.input.items.len;
}
pub fn retainBytes(
self: *Runtime,
alloc: Allocator,
bytes: []const u8,
) !void {
std.debug.assert(self.is_active);
if (!self.canRetainBytes(bytes.len)) return error.NativeClearProbeInputOverflow;
try self.input.appendSlice(alloc, bytes);
}
pub fn settle(self: *Runtime) []const u8 {
std.debug.assert(self.is_active);
self.is_active = false;
self.expected_terminal_row = 0;
return self.input.items;
}
pub fn clearSettledInput(self: *Runtime) void {
std.debug.assert(!self.is_active);
self.input.clearRetainingCapacity();
}
pub fn disable(self: *Runtime, await_late_response: bool) void {
self.is_disabled = true;
self.late_response_pending = await_late_response;
}
pub fn awaitingLateResponse(self: Runtime) bool {
return self.late_response_pending;
}
pub fn finishLateResponse(self: *Runtime) void {
self.late_response_pending = false;
}
};
test "native clear probe retains input until it settles" {
var runtime = Runtime{};
defer runtime.deinit(std.testing.allocator);
try runtime.begin(std.testing.allocator, 17, 'a');
try runtime.retainBytes(std.testing.allocator, "bc");
try std.testing.expect(runtime.active());
try std.testing.expectEqualStrings("abc", runtime.retainedInput());
const retained = runtime.settle();
try std.testing.expectEqualStrings("abc", retained);
try std.testing.expect(!runtime.active());
}
test "native clear probe bounds held input" {
const alloc = std.testing.allocator;
var runtime = Runtime{};
defer runtime.deinit(alloc);
try runtime.begin(alloc, 17, 'a');
const fill = try alloc.alloc(u8, max_input_bytes - 1);
defer alloc.free(fill);
@memset(fill, 'b');
try runtime.retainBytes(alloc, fill);
try std.testing.expect(!runtime.canRetainBytes(1));
try std.testing.expectError(
error.NativeClearProbeInputOverflow,
runtime.retainBytes(alloc, "c"),
);
}
test "native clear probe records disabled late-response window" {
var runtime = Runtime{};
try std.testing.expect(!runtime.disabled());
try std.testing.expect(!runtime.awaitingLateResponse());
runtime.disable(true);
try std.testing.expect(runtime.disabled());
try std.testing.expect(runtime.awaitingLateResponse());
runtime.finishLateResponse();
try std.testing.expect(!runtime.awaitingLateResponse());
}