diff --git a/src/terminal/Tabstops.zig b/src/terminal/Tabstops.zig index 8955447f2..7e74bc961 100644 --- a/src/terminal/Tabstops.zig +++ b/src/terminal/Tabstops.zig @@ -14,7 +14,6 @@ const tripwire = @import("../tripwire.zig"); const Allocator = std.mem.Allocator; const testing = std.testing; const assert = @import("../quirks.zig").inlineAssert; -const fastmem = @import("../fastmem.zig"); /// Unit is the type we use per tabstop unit (see file docs). const Unit = u8; @@ -138,24 +137,21 @@ pub fn resize( return; } - // What we need in the dynamic size - const size = cols - prealloc_columns; - if (size <= self.dynamic_stops.len) { + // Number of units needed beyond the preallocated columns. + const dynamic_count = std.math.divCeil( + usize, + cols - prealloc_columns, + unit_bits, + ) catch unreachable; + if (dynamic_count <= self.dynamic_stops.len) { self.cols = cols; return; } - // Note: we can probably try to realloc here but I'm not sure it matters. try tw.check(.dynamic_alloc); - const new = try alloc.alloc(Unit, size); - errdefer comptime unreachable; - @memset(new, 0); - if (self.dynamic_stops.len > 0) { - fastmem.copy(Unit, new, self.dynamic_stops); - alloc.free(self.dynamic_stops); - } - - self.dynamic_stops = new; + const old_len = self.dynamic_stops.len; + self.dynamic_stops = try alloc.realloc(self.dynamic_stops, dynamic_count); + @memset(self.dynamic_stops[old_len..], 0); self.cols = cols; } @@ -208,21 +204,27 @@ test "Tabstops: dynamic allocations" { var t: Tabstops = .{}; defer t.deinit(testing.allocator); - // Grow the capacity by 2. + // Grow by less than one unit to verify the allocation rounds up. const cap = t.capacity(); - try t.resize(testing.allocator, cap * 2); + try t.resize(testing.allocator, cap + 5); + try testing.expectEqual(cap + unit_bits, t.capacity()); // Set something that was out of range of the first - t.set(cap + 5); - try testing.expect(t.get(cap + 5)); - try testing.expect(!t.get(cap + 4)); + t.set(cap + 4); + try testing.expect(t.get(cap + 4)); + try testing.expect(!t.get(cap + 3)); + + // Growing again preserves existing stops and clears the new unit. + try t.resize(testing.allocator, cap + unit_bits + 1); + try testing.expect(t.get(cap + 4)); + try testing.expect(!t.get(cap + unit_bits)); // Prealloc still works try testing.expect(!t.get(5)); } test "Tabstops: resize to existing capacity does not allocate" { - var backing: [prealloc_columns]Unit = undefined; + var backing: [prealloc_count]Unit = undefined; var fixed = std.heap.FixedBufferAllocator.init(&backing); var t: Tabstops = .{};