terminal/snapshot: 8-byte grid cells with blank elision

Rework the PAGE grid encoding for codec speed and size. This is a
breaking change to the work-in-progress version 1 wire format.

Cells were previously a fixed 16-byte header plus inline grapheme
suffixes: one byte each for content kind, width, and flags, a
reserved byte, 16-bit style and hyperlink IDs, a 32-bit value, and an
always-present 32-bit suffix count that was almost always zero. Cells
are now one 64-bit little-endian word with a documented bit registry
that carries the hyperlink ID in its high bits. The layout
deliberately coincides with the native cell so clean rows encode as a
straight copy of page memory and decode as one bulk read plus an
in-place normalization pass; a comptime check falls back to a
portable field-by-field codec if the native layout ever diverges.

Each row header also gains an encoded cell count so trailing default
cells are elided instead of spending 16 bytes apiece encoding
nothing: on typical shell output most of every row is blank, and
measurement showed 97% of encoded snapshot bytes were zero. Grapheme
suffixes move out of the cell stream into a per-grid section of
(row, column, codepoints) entries, which keeps row decoding
fixed-stride and bulk-copyable.

Decode ID remapping switches from hash maps to direct-indexed tables
sized by the 16-bit encoded ID space, removing per-styled-cell hash
lookups.

Benchmark deltas at this commit (terminal-snapshot, M-series,
ReleaseFast, 1 MB corpora):

  ascii lines 1-70:  34.16 MB -> 7.66 MB (4.5x)
                     encode 92.8 -> 18.2 ms, decode 119.8 -> 28.0 ms
  ascii full-wrap:   16.01 MB -> 8.04 MB (2.0x)
                     encode 43.6 -> 18.4 ms, decode  56.2 -> 25.6 ms
  utf8:               4.33 MB -> 1.90 MB (2.3x)
                     encode 12.4 ->  4.9 ms, decode  19.0 ->  9.6 ms
This commit is contained in:
Mitchell Hashimoto
2026-08-02 09:33:43 -07:00
parent 0b5e12453b
commit ed0f54fb8c
8 changed files with 1197 additions and 649 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -63,19 +63,19 @@
//! | hyperlink_count entries |
//! +---------------------------+
//! | Grid |
//! | one record per row |
//! | rows and grapheme section |
//! +---------------------------+
//!
//! Style entry = encoded ID + style record
//! Hyperlink entry = encoded ID + hyperlink record
//! Grid = row 0 ... row (rows - 1)
//! Row = row header + cell 0 ... cell (columns - 1)
//! Cell = cell header + grapheme suffix codepoints
//! Grid = row 0 ... row (rows - 1) + grapheme suffix section
//! Row = row header + encoded cells
//! ```
//!
//! Following the header, the payload contains exactly `style_count` style
//! records and `hyperlink_count` hyperlink records, followed by exactly
//! `rows` row records. There is no padding between records.
//! `rows` row records and one grapheme suffix section. There is no padding
//! between records.
//!
//! Each style begins with an ID (`u16`) followed by the typical style
//! binary representation (in style.zig). The ID is what cells will use
@@ -119,6 +119,7 @@ const PayloadEncodeError = hyperlink.EncodeError || grid.EncodeError;
const PayloadDecodeError = std.Io.Reader.Error ||
Header.CapacityError ||
grid.DecodeError ||
error{
/// The hyperlink kind is not defined by snapshot version 1.
InvalidKind,
@@ -284,15 +285,13 @@ fn decodePayloadBody(
page.pauseIntegrityChecks(true);
defer page.pauseIntegrityChecks(false);
var style_remap = grid.StyleRemap.init(alloc);
defer style_remap.deinit();
style_remap.ensureTotalCapacity(header.style_count) catch
var style_remap = grid.StyleRemap.init(alloc) catch
return error.OutOfMemory;
defer style_remap.deinit(alloc);
var hyperlink_remap = grid.HyperlinkRemap.init(alloc);
defer hyperlink_remap.deinit();
hyperlink_remap.ensureTotalCapacity(header.hyperlink_count) catch
var hyperlink_remap = grid.HyperlinkRemap.init(alloc) catch
return error.OutOfMemory;
defer hyperlink_remap.deinit(alloc);
// Styles
for (0..header.style_count) |_| {
@@ -316,10 +315,7 @@ fn decodePayloadBody(
valid,
) catch 0;
} else 0;
style_remap.putAssumeCapacityNoClobber(
native_id,
decoded_id,
);
style_remap.put(native_id, decoded_id);
}
// Hyperlinks
@@ -338,10 +334,7 @@ fn decodePayloadBody(
// Zero records an ignored table entry. Grid decoding treats every
// reference to it as no hyperlink.
hyperlink_remap.putAssumeCapacityNoClobber(
native_id,
decoded_id,
);
hyperlink_remap.put(native_id, decoded_id);
}
// Rows and cells
@@ -860,8 +853,7 @@ test "decode accepts unordered sparse style IDs and ignores zero" {
var descending: [
Header.len +
2 * (2 + style.len) +
1 +
16
7
]u8 = undefined;
var descending_writer: std.Io.Writer = .fixed(&descending);
try header.encode(&descending_writer);
@@ -869,8 +861,9 @@ test "decode accepts unordered sparse style IDs and ignores zero" {
try style.encode(.{ .flags = .{ .bold = true } }, &descending_writer);
try io.writeInt(&descending_writer, TerminalStyleId, 2);
try style.encode(.{ .flags = .{ .italic = true } }, &descending_writer);
try descending_writer.writeByte(0);
try descending_writer.splatByteAll(0, 16);
try descending_writer.writeByte(0); // row flags
try io.writeInt(&descending_writer, u16, 0); // cell count
try io.writeInt(&descending_writer, u32, 0); // grapheme section
var descending_reader: std.Io.Reader = .fixed(
descending_writer.buffered(),
@@ -893,7 +886,7 @@ test "decode accepts unordered sparse style IDs and ignores zero" {
.string_capacity_bytes = 0,
};
var zero: [Header.len + 2 + style.len + 17]u8 = undefined;
var zero: [Header.len + 2 + style.len + 7]u8 = undefined;
var zero_writer: std.Io.Writer = .fixed(&zero);
try one_header.encode(&zero_writer);
try io.writeInt(&zero_writer, TerminalStyleId, 0);
@@ -936,8 +929,7 @@ test "decode accepts unordered sparse hyperlink IDs" {
var encoded: [
Header.len +
2 * 14 +
1 +
16
7
]u8 = undefined;
var writer: std.Io.Writer = .fixed(&encoded);
try header.encode(&writer);
@@ -945,8 +937,9 @@ test "decode accepts unordered sparse hyperlink IDs" {
try hyperlink.encode(first, &writer);
try io.writeInt(&writer, TerminalHyperlinkId, 2);
try hyperlink.encode(second, &writer);
try writer.writeByte(0);
try writer.splatByteAll(0, 16);
try writer.writeByte(0); // row flags
try io.writeInt(&writer, u16, 0); // cell count
try io.writeInt(&writer, u32, 0); // grapheme section
var reader: std.Io.Reader = .fixed(writer.buffered());
var decoded = try decodePayload(
@@ -973,15 +966,15 @@ test "decode defaults missing sparse cell references" {
.string_capacity_bytes = 0,
};
var style_encoded: [Header.len + 1 + 16]u8 = undefined;
var style_encoded: [Header.len + 3 + 8 + 4]u8 = undefined;
var style_writer: std.Io.Writer = .fixed(&style_encoded);
try style_header.encode(&style_writer);
try style_writer.writeByte(0);
try style_writer.writeAll(&.{ 0, 0, 0, 0 });
try io.writeInt(&style_writer, TerminalStyleId, 1);
try io.writeInt(&style_writer, TerminalHyperlinkId, 0);
try io.writeInt(&style_writer, u32, 0);
try io.writeInt(&style_writer, u32, 0);
try style_writer.writeByte(0); // row flags
try io.writeInt(&style_writer, u16, 1); // cell count
try io.writeInt(&style_writer, u64, @bitCast(grid.Cell{
.style_id = 1,
}));
try io.writeInt(&style_writer, u32, 0); // grapheme section
var style_reader: std.Io.Reader = .fixed(style_writer.buffered());
var style_page = try decodePayload(
@@ -1006,15 +999,16 @@ test "decode defaults missing sparse cell references" {
.string_capacity_bytes = 0,
};
var hyperlink_encoded: [Header.len + 1 + 16]u8 = undefined;
var hyperlink_encoded: [Header.len + 3 + 8 + 4]u8 = undefined;
var hyperlink_writer: std.Io.Writer = .fixed(&hyperlink_encoded);
try hyperlink_header.encode(&hyperlink_writer);
try hyperlink_writer.writeByte(0);
try hyperlink_writer.writeAll(&.{ 0, 0, 0, 0 });
try io.writeInt(&hyperlink_writer, TerminalStyleId, 0);
try io.writeInt(&hyperlink_writer, TerminalHyperlinkId, 1);
try io.writeInt(&hyperlink_writer, u32, 0);
try io.writeInt(&hyperlink_writer, u32, 0);
try hyperlink_writer.writeByte(0); // row flags
try io.writeInt(&hyperlink_writer, u16, 1); // cell count
try io.writeInt(&hyperlink_writer, u64, @bitCast(grid.Cell{
.hyperlink = true,
.hyperlink_id = 1,
}));
try io.writeInt(&hyperlink_writer, u32, 0); // grapheme section
var hyperlink_reader: std.Io.Reader = .fixed(
hyperlink_writer.buffered(),
@@ -1044,41 +1038,42 @@ test "decode normalizes invalid grid semantics" {
.string_capacity_bytes = 0,
};
var encoded: [Header.len + 1 + 3 * 16 + 12]u8 = undefined;
var encoded: [Header.len + 3 + 3 * 8 + 4 + 10]u8 = undefined;
var writer: std.Io.Writer = .fixed(&encoded);
try header.encode(&writer);
// Preserve wrap while degrading the unknown semantic-prompt value to none
// and ignoring all reserved row bits.
try writer.writeByte(0xFD);
try io.writeInt(&writer, u16, 3);
// An unknown content and width kind, invalid semantic content, reserved
// bytes, and suffix data all degrade to a blank narrow output cell.
try writer.writeAll(&.{ 3, 4, 0xFF, 0xFF });
try io.writeInt(&writer, TerminalStyleId, 0);
try io.writeInt(&writer, TerminalHyperlinkId, 0);
try io.writeInt(&writer, u32, 0xD800);
try io.writeInt(&writer, u32, 1);
try io.writeInt(&writer, u32, 0x110000);
// A text cell replaces an invalid Unicode scalar with U+FFFD while its
// reserved semantic content degrades to output.
try io.writeInt(&writer, u64, @bitCast(grid.Cell{
.content = 0xD800,
.semantic_content = 3,
}));
// A recognized text cell replaces an invalid Unicode scalar with U+FFFD.
// Its valid suffix cannot fit the advertised zero capacity, so decoding
// preserves the base character and drops the optional suffix.
try writer.writeAll(&.{ 0, 0, 0, 0 });
try io.writeInt(&writer, TerminalStyleId, 0);
try io.writeInt(&writer, TerminalHyperlinkId, 0);
try io.writeInt(&writer, u32, 0xD800);
try io.writeInt(&writer, u32, 1);
try io.writeInt(&writer, u32, 'x');
// A declared grapheme suffix cannot fit the advertised zero capacity,
// so decoding preserves the base character and drops the suffix.
try io.writeInt(&writer, u64, @bitCast(grid.Cell{
.kind = 1,
.content = 'x',
}));
// Reserved palette bits and a nonsensical suffix do not obscure the valid
// low palette byte; the suffix is consumed and ignored.
try writer.writeAll(&.{ 1, 0, 0, 0xFF });
try io.writeInt(&writer, TerminalStyleId, 0);
try io.writeInt(&writer, TerminalHyperlinkId, 0);
try io.writeInt(&writer, u32, 0xFFFFFF07);
// Reserved palette content bits do not obscure the valid low palette
// byte.
try io.writeInt(&writer, u64, @bitCast(grid.Cell{
.kind = 2,
.content = 0xFFFF07,
}));
// The grapheme section carries the suffix for the second cell.
try io.writeInt(&writer, u32, 1);
try io.writeInt(&writer, u32, 'x');
try io.writeInt(&writer, u16, 0);
try io.writeInt(&writer, u16, 1);
try io.writeInt(&writer, u16, 1);
try io.writeInt(&writer, u32, 0x0301);
var reader: std.Io.Reader = .fixed(writer.buffered());
var page = try decodePayload(&reader, std.testing.allocator);
@@ -1091,7 +1086,7 @@ test "decode normalizes invalid grid semantics" {
terminal_page.Row.SemanticPrompt.none,
first.row.semantic_prompt,
);
try std.testing.expectEqual(@as(u21, 0), first.cell.codepoint());
try std.testing.expectEqual(@as(u21, 0xFFFD), first.cell.codepoint());
try std.testing.expectEqual(
terminal_page.Cell.Wide.narrow,
first.cell.wide,
@@ -1104,7 +1099,7 @@ test "decode normalizes invalid grid semantics" {
try std.testing.expect(!first.cell.hasGrapheme());
const second = page.getRowAndCell(1, 0).cell;
try std.testing.expectEqual(@as(u21, 0xFFFD), second.codepoint());
try std.testing.expectEqual(@as(u21, 'x'), second.codepoint());
try std.testing.expect(!second.hasGrapheme());
const third = page.getRowAndCell(2, 0).cell;

View File

@@ -205,6 +205,7 @@ const Allocator = std.mem.Allocator;
const hyperlink = @import("hyperlink.zig");
const test_fixture = @import("fixture.zig");
const io = @import("io.zig");
const grid = @import("grid.zig");
const page = @import("page.zig");
const record = @import("record.zig");
const style = @import("style.zig");
@@ -2261,20 +2262,14 @@ test "SCREEN decode ignores a PAGE with an empty hyperlink URI" {
// One narrow codepoint cell refers to the hyperlink table entry above.
// Since that entry is ignored, the cell must restore without a hyperlink.
try page_payload.writeByte(0);
try page_payload.writeAll(&.{ 0, 0, 0, 0 });
try io.writeInt(
page_payload,
terminal_style.Id,
0,
);
try io.writeInt(
page_payload,
terminal_hyperlink.Id,
1,
);
try io.writeInt(page_payload, u32, 'A');
try io.writeInt(page_payload, u32, 0);
try page_payload.writeByte(0); // row flags
try io.writeInt(page_payload, u16, 1); // cell count
try io.writeInt(page_payload, u64, @bitCast(grid.Cell{
.content = 'A',
.hyperlink = true,
.hyperlink_id = 1,
}));
try io.writeInt(page_payload, u32, 0); // grapheme section
try stream.finish();
var source: std.Io.Reader = .fixed(destination.written());

View File

@@ -914,18 +914,28 @@ types:
type: grid_row(columns)
repeat: expr
repeat-expr: num_rows
- id: num_grapheme_entries
type: u4
- id: grapheme_entries
type: grapheme_entry(num_rows, columns)
repeat: expr
repeat-expr: num_grapheme_entries
grid_row:
params:
- id: num_cells
- id: columns
type: u2
seq:
- id: flags
type: grid_row_flags
- id: cell_count
type: u2
valid:
expr: _ <= columns
- id: cells
type: grid_cell(_index, num_cells, flags.wrap)
type: grid_cell(_index, columns, flags.wrap)
repeat: expr
repeat-expr: num_cells
repeat-expr: cell_count
grid_row_flags:
seq:
@@ -942,6 +952,17 @@ types:
value: (raw >> 2) & 0x3
grid_cell:
doc: |
One 64-bit little-endian cell word. The word is parsed as two 32-bit
halves so every derived field stays within JavaScript's safe integer
range, following the same approach as mode_set.
Canonical rules validated here: reserved semantic content is not
emitted, the hyperlink flag matches a nonzero hyperlink ID, codepoint
content is a Unicode scalar value, palette content uses only its low
eight bits, and spacer relationships match the preceding cell. Wide
markers cannot be validated forward because their spacer tail may be
the next cell or the implicit narrow cell after a short row.
params:
- id: index
type: u2
@@ -950,59 +971,61 @@ types:
- id: row_wrap
type: bool
seq:
- id: content_kind
type: u1
valid:
max: 2
- id: width
type: u1
- id: lo
type: u4
- id: hi
type: u4
valid:
expr: |
_ <= 3 and
(_ != 2 or
semantic_content <= 2 and
hyperlink == (hyperlink_id != 0) and
(content_kind >= 2 or
(content <= 0x10ffff and
not (content >= 0xd800 and content <= 0xdfff))) and
(content_kind != 2 or content <= 0xff) and
(width != 2 or
(index > 0 and _parent.cells[index - 1].width == 1)) and
(_ != 3 or
(index + 1 == columns and row_wrap))
- id: flags
type: grid_cell_flags
- id: reserved
type: u1
valid: 0
- id: style_id
(width != 3 or (index + 1 == columns and row_wrap))
instances:
content_kind:
value: lo % 4
content:
value: (lo / 4) % 16777216
style_id:
value: (lo / 67108864) + (hi % 1024) * 64
width:
value: (hi / 1024) % 4
protected:
value: (hi / 4096) % 2 != 0
hyperlink:
value: (hi / 8192) % 2 != 0
semantic_content:
value: (hi / 16384) % 4
hyperlink_id:
value: hi / 65536
grapheme_entry:
seq:
- id: row
type: u2
- id: hyperlink_id
valid:
expr: _ < num_rows
- id: col
type: u2
- id: value
type: u4
valid:
expr: |
content_kind == 0 ?
(_ <= 0x10ffff and not (_ >= 0xd800 and _ <= 0xdfff) and _ != 0x10eeee) :
content_kind == 1 ?
_ <= 0xff :
_ <= 0xffffff
- id: num_graphemes
type: u4
expr: _ < columns
- id: num_codepoints
type: u2
valid:
expr: |
content_kind == 0 ?
(_ == 0 or value != 0) :
_ == 0
- id: graphemes
min: 1
- id: codepoints
type: u4
repeat: expr
repeat-expr: num_graphemes
repeat-expr: num_codepoints
valid:
expr: _ <= 0x10ffff and not (_ >= 0xd800 and _ <= 0xdfff) and _ != 0x10eeee
grid_cell_flags:
seq:
- id: raw
type: u1
valid:
expr: (_ & 0xf8) == 0 and ((_ >> 1) & 0x3) <= 2
instances:
protected:
value: (raw & (1 << 0)) != 0
semantic_content:
value: (raw >> 1) & 0x3
expr: _ <= 0x10ffff and not (_ >= 0xd800 and _ <= 0xdfff)
params:
- id: num_rows
type: u2
- id: columns
type: u2

View File

@@ -78,65 +78,52 @@ ee ee 80 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x0000037a
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x000003ec
00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x000003fc
# offset 0x0000040c: page record, payload 119 bytes
03 00 77 00 00 00 48 b7 91 cb 02 00 03 00 00 00 # 0x0000040c
00 00 10 00 c0 00 00 04 00 00 00 08 00 00 00 00 # 0x0000041c
00 00 00 00 00 00 00 43 00 00 00 00 00 00 00 00 # 0x0000042c
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x0000043c
00 00 00 00 00 00 00 00 44 00 00 00 00 00 00 00 # 0x0000044c
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x0000045c
00 00 00 00 00 00 00 00 00 45 00 00 00 00 00 00 # 0x0000046c
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x0000047c
00 # 0x0000048c
# offset 0x0000040c: page record, payload 57 bytes
03 00 39 00 00 00 26 5a 94 0b 02 00 03 00 00 00 # 0x0000040c
00 00 10 00 c0 00 00 04 00 00 00 08 00 00 00 01 # 0x0000041c
00 0c 01 00 00 00 00 00 00 00 01 00 10 01 00 00 # 0x0000042c
00 00 00 00 00 01 00 14 01 00 00 00 00 00 00 00 # 0x0000043c
00 00 00 # 0x0000044c
# offset 0x0000048d: screen record, payload 54 bytes
02 00 36 00 00 00 ce 1f 9f 08 01 00 01 00 00 00 # 0x0000048d
00 00 00 00 00 00 01 00 02 00 01 00 00 00 00 00 # 0x0000049d
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x000004ad
00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x000004bd
# offset 0x0000044f: screen record, payload 54 bytes
02 00 36 00 00 00 ce 1f 9f 08 01 00 01 00 00 00 # 0x0000044f
00 00 00 00 00 00 01 00 02 00 01 00 00 00 00 00 # 0x0000045f
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x0000046f
00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x0000047f
# offset 0x000004cd: page record, payload 119 bytes
03 00 77 00 00 00 84 a6 e9 08 02 00 03 00 00 00 # 0x000004cd
00 00 80 00 c0 00 00 02 00 00 00 08 00 00 03 00 # 0x000004dd
00 00 00 00 00 00 00 72 00 00 00 00 00 00 00 00 # 0x000004ed
00 00 00 00 00 00 00 6e 00 00 00 00 00 00 00 03 # 0x000004fd
00 00 00 00 00 00 00 00 61 00 00 00 00 00 00 00 # 0x0000050d
00 00 00 00 00 00 00 00 74 00 00 00 00 00 00 00 # 0x0000051d
03 00 00 00 00 00 00 00 00 65 00 00 00 00 00 00 # 0x0000052d
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x0000053d
00 # 0x0000054d
# offset 0x0000048f: page record, payload 73 bytes
03 00 49 00 00 00 37 af 30 0e 02 00 03 00 00 00 # 0x0000048f
00 00 80 00 c0 00 00 02 00 00 00 08 00 00 03 02 # 0x0000049f
00 c8 01 00 00 00 00 00 00 b8 01 00 00 00 00 00 # 0x000004af
00 03 02 00 84 01 00 00 00 00 00 00 d0 01 00 00 # 0x000004bf
00 00 00 00 03 01 00 94 01 00 00 00 00 00 00 00 # 0x000004cf
00 00 00 # 0x000004df
# offset 0x0000054e: continuation record, payload 0 bytes
07 00 00 00 00 00 27 80 63 d1 # 0x0000054e
# offset 0x000004e2: continuation record, payload 0 bytes
07 00 00 00 00 00 27 80 63 d1 # 0x000004e2
# offset 0x00000558: ready record, payload 32 bytes
05 00 20 00 00 00 d4 d1 fc d0 8d 69 76 c1 6e c6 # 0x00000558
88 21 bd 10 97 41 97 35 63 2d ce 9e 3c b8 fa cf # 0x00000568
57 04 f5 4b e0 70 66 5e 7e b6 # 0x00000578
# offset 0x000004ec: ready record, payload 32 bytes
05 00 20 00 00 00 5d f3 97 80 41 a2 18 18 cb 82 # 0x000004ec
8c 97 b6 58 14 60 26 9a 88 2e be d4 c3 4b 83 c9 # 0x000004fc
9d 32 9d de e6 70 17 4a 5d bc # 0x0000050c
# offset 0x00000582: history record, payload 6 bytes
04 00 06 00 00 00 20 32 ed e1 00 00 02 00 00 00 # 0x00000582
# offset 0x00000516: history record, payload 6 bytes
04 00 06 00 00 00 20 32 ed e1 00 00 02 00 00 00 # 0x00000516
# offset 0x00000592: page record, payload 86 bytes
03 00 56 00 00 00 52 3b 6e 8d 02 00 02 00 00 00 # 0x00000592
00 00 10 00 c0 00 00 04 00 00 00 08 00 00 00 00 # 0x000005a2
00 00 00 00 00 00 00 42 00 00 00 00 00 00 00 00 # 0x000005b2
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x000005c2
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x000005d2
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x000005e2
# offset 0x00000526: page record, payload 38 bytes
03 00 26 00 00 00 9e 10 a2 93 02 00 02 00 00 00 # 0x00000526
00 00 10 00 c0 00 00 04 00 00 00 08 00 00 00 01 # 0x00000536
00 08 01 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x00000546
# offset 0x000005f2: page record, payload 86 bytes
03 00 56 00 00 00 fb bc 15 06 02 00 02 00 00 00 # 0x000005f2
00 00 10 00 c0 00 00 04 00 00 00 08 00 00 00 00 # 0x00000602
00 00 00 00 00 00 00 41 00 00 00 00 00 00 00 00 # 0x00000612
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x00000622
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x00000632
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x00000642
# offset 0x00000556: page record, payload 38 bytes
03 00 26 00 00 00 70 e1 36 3a 02 00 02 00 00 00 # 0x00000556
00 00 10 00 c0 00 00 04 00 00 00 08 00 00 00 01 # 0x00000566
00 04 01 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x00000576
# offset 0x00000652: history record, payload 6 bytes
04 00 06 00 00 00 b8 7a ba b1 01 00 00 00 00 00 # 0x00000652
# offset 0x00000586: history record, payload 6 bytes
04 00 06 00 00 00 b8 7a ba b1 01 00 00 00 00 00 # 0x00000586
# offset 0x00000662: finish record, payload 32 bytes
06 00 20 00 00 00 f3 a4 cf b6 f2 2b 17 88 1a 28 # 0x00000662
a7 27 6e f7 de bc 7a 52 f9 a4 f9 c3 32 5b e0 09 # 0x00000672
8a 1a 7d a0 9f 8b 32 31 67 41 # 0x00000682
# offset 0x00000596: finish record, payload 32 bytes
06 00 20 00 00 00 20 0f e0 7e 10 4f 2c da 90 fa # 0x00000596
65 19 a9 73 26 75 2a 5b 33 cd 9d fa 73 81 86 07 # 0x000005a6
9e 57 d4 83 41 9f a9 81 64 e7 # 0x000005b6

View File

@@ -6,27 +6,9 @@
# Generated by its snapshot test; review before replacing.
# On mismatch, the candidate is copied to the repository root.
# row 0: prompt
04
# row 0, cell 0: protected prompt, wide "A"
00 01 05 00 00 00 00 00 41 00 00 00 00 00 00 00
# row 0, cell 1: input spacer tail
00 02 02 00 00 00 00 00 00 00 00 00 00 00 00 00
# row 0, cell 2: "x" with two grapheme suffixes
00 00 00 00 00 00 00 00 78 00 00 00 02 00 00 00
01 03 00 00 02 03 00 00
# row 1: wrapped prompt continuation
0b
# row 1, cell 0: palette background 7
01 00 00 00 00 00 00 00 07 00 00 00 00 00 00 00
# row 1, cell 1: protected RGB background
02 00 01 00 00 00 00 00 aa bb cc 00 00 00 00 00
# row 1, cell 2: spacer head
00 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00
# offset 0x00000000: encoded bytes
04 03 00 04 01 00 00 00 94 00 00 00 00 00 00 00 # 0x00000000
48 00 00 e1 01 00 00 00 00 00 00 0b 03 00 1e 00 # 0x00000010
00 00 00 00 00 00 ab ee 32 03 00 10 00 00 00 00 # 0x00000020
00 00 00 0c 00 00 01 00 00 00 00 00 02 00 02 00 # 0x00000030
01 03 00 00 02 03 00 00 # 0x00000040

View File

@@ -7,6 +7,6 @@
# On mismatch, the candidate is copied to the repository root.
# offset 0x00000000: encoded bytes
03 00 25 00 00 00 8c 05 d6 d3 01 00 01 00 00 00 # 0x00000000
03 00 1b 00 00 00 5e 0d 0a d4 01 00 01 00 00 00 # 0x00000000
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x00000010
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x00000020
00 00 00 00 00 # 0x00000020

View File

@@ -15,10 +15,8 @@
00 00 03 00 00 00 00 00 01 2a 00 00 00 00 00 00 # 0x00000024
00 00 00 00 01 00 02 01 00 00 00 61 05 00 00 00 # 0x00000034
61 6c 70 68 61 03 00 01 04 03 02 01 04 00 00 00 # 0x00000044
62 65 74 61 04 00 01 05 00 01 00 01 00 41 00 00 # 0x00000054
00 00 00 00 00 00 02 02 00 03 00 03 00 00 00 00 # 0x00000064
00 00 00 00 00 01 00 00 00 01 00 01 00 07 00 00 # 0x00000074
00 00 00 00 00 0b 00 00 00 00 00 00 00 00 78 00 # 0x00000084
00 00 02 00 00 00 01 03 00 00 02 03 00 00 02 00 # 0x00000094
01 00 00 00 00 00 aa bb cc 00 00 00 00 00 00 03 # 0x000000a4
00 00 00 00 00 00 00 00 00 00 00 00 00 00 # 0x000000b4
62 65 74 61 04 03 00 04 01 00 04 00 b4 01 00 00 # 0x00000054
00 00 0c 00 68 03 00 1e 00 00 04 00 20 01 00 0b # 0x00000064
03 00 e1 01 00 00 00 00 00 00 ab ee 32 03 00 10 # 0x00000074
00 00 00 00 00 00 00 0c 00 00 01 00 00 00 01 00 # 0x00000084
00 00 02 00 01 03 00 00 02 03 00 00 # 0x00000094