terminal: add some integrity assertions

This commit is contained in:
Mitchell Hashimoto
2024-03-21 16:53:42 -07:00
parent 1be06e8f3f
commit 1649641d18
2 changed files with 38 additions and 1 deletions

View File

@ -848,6 +848,7 @@ pub fn clearPrompt(self: *Screen) void {
while (clear_it.next()) |p| { while (clear_it.next()) |p| {
const row = p.rowAndCell().row; const row = p.rowAndCell().row;
p.page.data.clearCells(row, 0, p.page.data.size.cols); p.page.data.clearCells(row, 0, p.page.data.size.cols);
p.page.data.assertIntegrity();
} }
} }
} }
@ -1133,6 +1134,7 @@ pub fn manualStyleUpdate(self: *Screen) !void {
/// Append a grapheme to the given cell within the current cursor row. /// Append a grapheme to the given cell within the current cursor row.
pub fn appendGrapheme(self: *Screen, cell: *Cell, cp: u21) !void { pub fn appendGrapheme(self: *Screen, cell: *Cell, cp: u21) !void {
defer self.cursor.page_pin.page.data.assertIntegrity();
self.cursor.page_pin.page.data.appendGrapheme( self.cursor.page_pin.page.data.appendGrapheme(
self.cursor.page_row, self.cursor.page_row,
cell, cell,

View File

@ -185,6 +185,15 @@ pub const Page = struct {
InvalidStyleCount, InvalidStyleCount,
}; };
/// A helper that can be used to assert the integrity of the page
/// when runtime safety is enabled. This is a no-op when runtime
/// safety is disabled. This uses the libc allocator.
pub fn assertIntegrity(self: *Page) void {
if (comptime std.debug.runtime_safety) {
self.verifyIntegrity(std.heap.c_allocator) catch unreachable;
}
}
/// Verifies the integrity of the page data. This is not fast, /// Verifies the integrity of the page data. This is not fast,
/// but it is useful for assertions, deserialization, etc. The /// but it is useful for assertions, deserialization, etc. The
/// allocator is only used for temporary allocations -- all memory /// allocator is only used for temporary allocations -- all memory
@ -280,6 +289,31 @@ pub const Page = struct {
return IntegrityError.InvalidGraphemeCount; return IntegrityError.InvalidGraphemeCount;
} }
// There is allowed to be exactly one zero ref count style for
// the active style. If we see this, we should add it to our seen
// styles so the math is correct.
{
const id_map = self.styles.id_map.map(self.memory);
var it = id_map.iterator();
while (it.next()) |entry| {
const style_val = self.styles.lookupId(self.memory, entry.key_ptr.*).?.*;
const md = self.styles.upsert(self.memory, style_val) catch unreachable;
if (md.ref == 0) {
const gop = try styles_seen.getOrPut(entry.key_ptr.*);
if (gop.found_existing) {
log.warn(
"page integrity violation zero ref style seen multiple times id={}",
.{entry.key_ptr.*},
);
return IntegrityError.MismatchedStyleRef;
}
gop.value_ptr.* = 0;
break;
}
}
}
// Our unique styles seen should exactly match the style count. // Our unique styles seen should exactly match the style count.
if (styles_seen.count() != self.styles.count(self.memory)) { if (styles_seen.count() != self.styles.count(self.memory)) {
log.warn( log.warn(
@ -1729,9 +1763,10 @@ test "Page verifyIntegrity styles extra" {
.bold = true, .bold = true,
} }); } });
_ = try page.styles.upsert(page.memory, .{ .flags = .{ const md2 = try page.styles.upsert(page.memory, .{ .flags = .{
.italic = true, .italic = true,
} }); } });
md2.ref += 1;
// Write // Write
for (0..page.capacity.cols) |x| { for (0..page.capacity.cols) |x| {