mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-22 11:46:11 +03:00
terminal/new: nothing works but everything looks right
This commit is contained in:
@ -2,8 +2,11 @@ const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const color = @import("../color.zig");
|
||||
const sgr = @import("../sgr.zig");
|
||||
const style = @import("style.zig");
|
||||
const size = @import("size.zig");
|
||||
const Offset = size.Offset;
|
||||
const hash_map = @import("hash_map.zig");
|
||||
const AutoOffsetHashMap = hash_map.AutoOffsetHashMap;
|
||||
|
||||
/// A page represents a specific section of terminal screen. The primary
|
||||
/// idea of a page is that it is a fully self-contained unit that can be
|
||||
@ -93,3 +96,14 @@ test {
|
||||
_ = Page;
|
||||
_ = Style;
|
||||
}
|
||||
|
||||
// test {
|
||||
// const testing = std.testing;
|
||||
// const cap = try std.math.ceilPowerOfTwo(usize, 350);
|
||||
// const StyleIdMap = AutoOffsetHashMap(size.CellCountInt, style.Style);
|
||||
// const StyleMetadataMap = AutoOffsetHashMap(style.Style, style.Metadata);
|
||||
//
|
||||
// var len = StyleIdMap.bufferSize(@intCast(cap));
|
||||
// len += StyleMetadataMap.bufferSize(@intCast(cap));
|
||||
// try testing.expectEqual(@as(usize, 0), len);
|
||||
// }
|
||||
|
@ -30,7 +30,7 @@ pub fn Offset(comptime T: type) type {
|
||||
// our return type is naturally aligned. We COULD modify this
|
||||
// to return arbitrary alignment, but its not something we need.
|
||||
assert(@mod(self.offset, @alignOf(T)) == 0);
|
||||
return @ptrFromInt(@intFromPtr(base) + self.offset);
|
||||
return @ptrFromInt(intFromBase(base) + self.offset);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -42,12 +42,27 @@ pub fn getOffset(
|
||||
base: anytype,
|
||||
ptr: *const T,
|
||||
) Offset(T) {
|
||||
const base_int = @intFromPtr(base);
|
||||
const base_int = intFromBase(base);
|
||||
const ptr_int = @intFromPtr(ptr);
|
||||
const offset = ptr_int - base_int;
|
||||
return .{ .offset = @intCast(offset) };
|
||||
}
|
||||
|
||||
fn intFromBase(base: anytype) usize {
|
||||
return switch (@typeInfo(@TypeOf(base))) {
|
||||
.Pointer => |v| switch (v.size) {
|
||||
.One,
|
||||
.Many,
|
||||
.C,
|
||||
=> @intFromPtr(base),
|
||||
|
||||
.Slice => @intFromPtr(base.ptr),
|
||||
},
|
||||
|
||||
else => @compileError("invalid base type"),
|
||||
};
|
||||
}
|
||||
|
||||
test "Offset" {
|
||||
// This test is here so that if Offset changes, we can be very aware
|
||||
// of this effect and think about the implications of it.
|
||||
|
@ -1,7 +1,11 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const color = @import("../color.zig");
|
||||
const sgr = @import("../sgr.zig");
|
||||
const size = @import("size.zig");
|
||||
const Offset = size.Offset;
|
||||
const hash_map = @import("hash_map.zig");
|
||||
const AutoOffsetHashMap = hash_map.AutoOffsetHashMap;
|
||||
|
||||
/// The unique identifier for a style. This is at most the number of cells
|
||||
/// that can fit into a terminal page.
|
||||
@ -43,20 +47,144 @@ pub const Style = struct {
|
||||
}
|
||||
};
|
||||
|
||||
/// A set of styles.
|
||||
pub const Set = struct {
|
||||
/// The mapping of a style to associated metadata. This is
|
||||
/// the map that contains the actual style definitions
|
||||
/// (in the form of the key).
|
||||
styles: MetadataMap,
|
||||
|
||||
/// The mapping from ID to style.
|
||||
id_map: IdMap,
|
||||
|
||||
/// The next ID to use for a style that isn't in the set.
|
||||
/// When this overflows we'll begin returning an IdOverflow
|
||||
/// error and the caller must manually compact the style
|
||||
/// set.
|
||||
next_id: Id = 1,
|
||||
|
||||
/// Maps a style definition to metadata about that style.
|
||||
pub const MetadataMap = std.AutoHashMapUnmanaged(Style, Metadata);
|
||||
const MetadataMap = AutoOffsetHashMap(Style, Metadata);
|
||||
|
||||
/// Maps the unique style ID to the concrete style definition.
|
||||
pub const IdMap = std.AutoHashMapUnmanaged(size.CellCountInt, Style);
|
||||
const IdMap = AutoOffsetHashMap(Id, Offset(Style));
|
||||
|
||||
/// Returns the memory layout for the given base offset and
|
||||
/// desired capacity. The layout can be used by the caller to
|
||||
/// determine how much memory to allocate, and the layout must
|
||||
/// be used to initialize the set so that the set knows all
|
||||
/// the offsets for the various buffers.
|
||||
pub fn layoutForCapacity(base: usize, cap: usize) Layout {
|
||||
const md_start = std.mem.alignForward(usize, base, MetadataMap.base_align);
|
||||
const md_end = md_start + MetadataMap.bufferSize(@intCast(cap));
|
||||
|
||||
const id_start = std.mem.alignForward(usize, md_end, IdMap.base_align);
|
||||
const id_end = id_start + IdMap.bufferSize(@intCast(cap));
|
||||
|
||||
const total_size = id_end - base;
|
||||
|
||||
return .{
|
||||
.cap = cap,
|
||||
.md_start = md_start,
|
||||
.id_start = id_start,
|
||||
.total_size = total_size,
|
||||
};
|
||||
}
|
||||
|
||||
pub const Layout = struct {
|
||||
cap: usize,
|
||||
md_start: usize,
|
||||
id_start: usize,
|
||||
total_size: usize,
|
||||
};
|
||||
|
||||
pub fn init(base: []u8, layout: Layout) Set {
|
||||
assert(base.len >= layout.total_size);
|
||||
|
||||
var styles = MetadataMap.init(@intCast(layout.cap), base[layout.md_start..]);
|
||||
styles.metadata.offset += @intCast(layout.md_start);
|
||||
|
||||
var id_map = IdMap.init(@intCast(layout.cap), base[layout.id_start..]);
|
||||
id_map.metadata.offset += @intCast(layout.id_start);
|
||||
|
||||
return .{
|
||||
.styles = styles,
|
||||
.id_map = id_map,
|
||||
};
|
||||
}
|
||||
|
||||
/// Upsert a style into the set and return a pointer to the metadata
|
||||
/// for that style. The pointer is valid for the lifetime of the set
|
||||
/// so long as the style is not removed.
|
||||
pub fn upsert(self: *Set, base: anytype, style: Style) !*Metadata {
|
||||
// If we already have the style in the map, this is fast.
|
||||
var map = self.styles.map(base);
|
||||
const gop = try map.getOrPut(style);
|
||||
if (gop.found_existing) return gop.value_ptr;
|
||||
|
||||
// New style, we need to setup all the metadata. First thing,
|
||||
// let's get the ID we'll assign, because if we're out of space
|
||||
// we need to fail early.
|
||||
errdefer map.removeByPtr(gop.key_ptr);
|
||||
const id = self.next_id;
|
||||
self.next_id = try std.math.add(Id, self.next_id, 1);
|
||||
errdefer self.next_id -= 1;
|
||||
gop.value_ptr.* = .{ .id = id };
|
||||
|
||||
// Setup our ID mapping
|
||||
var id_map = self.id_map.map(base);
|
||||
const id_gop = try id_map.getOrPut(id);
|
||||
errdefer id_map.removeByPtr(id_gop.key_ptr);
|
||||
assert(!id_gop.found_existing);
|
||||
id_gop.value_ptr.* = size.getOffset(Style, base, gop.key_ptr);
|
||||
return gop.value_ptr;
|
||||
}
|
||||
|
||||
/// Lookup a style by its unique identifier.
|
||||
pub fn lookupId(self: *const Set, base: anytype, id: Id) ?*Style {
|
||||
const id_map = self.id_map.map(base);
|
||||
const offset = id_map.get(id) orelse return null;
|
||||
return @ptrCast(offset.ptr(base));
|
||||
}
|
||||
};
|
||||
|
||||
/// Metadata about a style. This is used to track the reference count
|
||||
/// and the unique identifier for a style. The unique identifier is used
|
||||
/// to track the style in the full style map.
|
||||
pub const Metadata = struct {
|
||||
ref: size.CellCountInt = 0,
|
||||
id: size.CellCountInt = 0,
|
||||
ref: size.CellCountInt = 1,
|
||||
id: Id = 0,
|
||||
};
|
||||
|
||||
test {
|
||||
_ = Style;
|
||||
_ = Set;
|
||||
}
|
||||
|
||||
test "Set basic usage" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const layout = Set.layoutForCapacity(0, 16);
|
||||
const buf = try alloc.alloc(u8, layout.total_size);
|
||||
defer alloc.free(buf);
|
||||
|
||||
const style: Style = .{ .flags = .{ .bold = true } };
|
||||
|
||||
var set = Set.init(buf, layout);
|
||||
|
||||
// Upsert
|
||||
const meta = try set.upsert(buf, style);
|
||||
try testing.expect(meta.id > 0);
|
||||
|
||||
// Second upsert should return the same metadata.
|
||||
{
|
||||
const meta2 = try set.upsert(buf, style);
|
||||
try testing.expectEqual(meta.id, meta2.id);
|
||||
}
|
||||
|
||||
// Look it up
|
||||
{
|
||||
const v = set.lookupId(buf, meta.id).?;
|
||||
try testing.expect(v.flags.bold);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user