mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-08-24 16:11:43 +00:00
terminal: size dynamic tabstops by bits
Dynamic tabstop storage treated the number of columns above the inline capacity as a byte count even though each byte stores eight stops. This was wasteful, although in practice this is in the order of just bytes. Round the extra column count up to whole storage units and grow the existing slice with realloc, preserve existing stops.
This commit is contained in:
@@ -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 = .{};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user