From 1649641d185351f5b18ed70cce3777e7d19a1597 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 21 Mar 2024 16:53:42 -0700 Subject: [PATCH] terminal: add some integrity assertions --- src/terminal/Screen.zig | 2 ++ src/terminal/page.zig | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/terminal/Screen.zig b/src/terminal/Screen.zig index 659219aa9..a3fd22b5d 100644 --- a/src/terminal/Screen.zig +++ b/src/terminal/Screen.zig @@ -848,6 +848,7 @@ pub fn clearPrompt(self: *Screen) void { while (clear_it.next()) |p| { const row = p.rowAndCell().row; 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. 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_row, cell, diff --git a/src/terminal/page.zig b/src/terminal/page.zig index b72d3b00f..2b36ed92e 100644 --- a/src/terminal/page.zig +++ b/src/terminal/page.zig @@ -185,6 +185,15 @@ pub const Page = struct { 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, /// but it is useful for assertions, deserialization, etc. The /// allocator is only used for temporary allocations -- all memory @@ -280,6 +289,31 @@ pub const Page = struct { 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. if (styles_seen.count() != self.styles.count(self.memory)) { log.warn( @@ -1729,9 +1763,10 @@ test "Page verifyIntegrity styles extra" { .bold = true, } }); - _ = try page.styles.upsert(page.memory, .{ .flags = .{ + const md2 = try page.styles.upsert(page.memory, .{ .flags = .{ .italic = true, } }); + md2.ref += 1; // Write for (0..page.capacity.cols) |x| {