terminal: page memory layout for uri/hyperlink data

This commit is contained in:
Mitchell Hashimoto
2024-06-09 12:01:55 -07:00
parent 6c7b7843e9
commit 25d1e861ec

View File

@ -34,6 +34,37 @@ const grapheme_count_default = GraphemeAlloc.bitmap_bit_size;
const grapheme_bytes_default = grapheme_count_default * grapheme_chunk;
const GraphemeMap = AutoOffsetHashMap(Offset(Cell), Offset(u21).Slice);
/// The allocator for URIs (OSC8). We use a chunk size of 32 because
/// URIs are usually pretty long and that let's us represent most
/// bare domains in one chunk. This allocator is ALSO used for OSC8
/// IDs. These are usually short but the chunk length should be good.
const uri_chunk_len = 32;
const uri_chunk = uri_chunk_len * @sizeOf(u8);
const UriAlloc = BitmapAllocator(uri_chunk);
/// The hyperlinks counts are shared between URI and hyperlink entries
/// so it is a worst possible byte size. You probably don't need a large
/// value here to accomodate many cells for typical (rare) hyperlink
/// usage.
const hyperlink_count_default = 1;
const hyperlink_bytes_default = hyperlink_count_default * @max(
(uri_chunk * 2), // ID + URI
@sizeOf(HyperlinkEntry), // Entry
);
/// The hyperlink map and entry.
const HyperlinkMap = AutoOffsetHashMap(Offset(Cell), HyperlinkEntry);
const HyperlinkEntry = struct {
/// The ID for the hyperlink. This is always non-empty. We
/// auto-generate an ID if one is not provided by the user.
/// This is in UriAlloc.
id: Offset(u8).Slice,
/// The URI for the actual link.
/// This is in UriAlloc.
uri: Offset(u8).Slice,
};
/// 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
/// serialized, copied, etc. as a convenient way to represent a section
@ -88,6 +119,14 @@ pub const Page = struct {
/// path.
grapheme_map: GraphemeMap,
/// The URI data for hyperlinks (URI + ID). This is relatively rare
/// so this defaults to a very small allocation size.
uri_alloc: UriAlloc,
/// The mapping of cells to the hyperlink associated with them. This
/// only has a value when a cell has the "hyperlink" flag set.
hyperlink_map: HyperlinkMap,
/// The available set of styles in use on this page.
styles: style.Set,
@ -207,6 +246,14 @@ pub const Page = struct {
buf.add(l.grapheme_map_start),
l.grapheme_map_layout,
),
.uri_alloc = UriAlloc.init(
buf.add(l.uri_alloc_start),
l.uri_alloc_layout,
),
.hyperlink_map = HyperlinkMap.init(
buf.add(l.hyperlink_map_start),
l.hyperlink_map_layout,
),
.size = .{ .cols = cap.cols, .rows = cap.rows },
.capacity = cap,
};
@ -977,6 +1024,10 @@ pub const Page = struct {
grapheme_alloc_layout: GraphemeAlloc.Layout,
grapheme_map_start: usize,
grapheme_map_layout: GraphemeMap.Layout,
uri_alloc_start: usize,
uri_alloc_layout: UriAlloc.Layout,
hyperlink_map_start: usize,
hyperlink_map_layout: HyperlinkMap.Layout,
capacity: Capacity,
};
@ -1015,7 +1066,16 @@ pub const Page = struct {
const grapheme_map_start = alignForward(usize, grapheme_alloc_end, GraphemeMap.base_align);
const grapheme_map_end = grapheme_map_start + grapheme_map_layout.total_size;
const total_size = alignForward(usize, grapheme_map_end, std.mem.page_size);
const uri_alloc_layout = UriAlloc.layout(cap.hyperlink_bytes);
const uri_alloc_start = alignForward(usize, grapheme_map_end, UriAlloc.base_align);
const uri_alloc_end = uri_alloc_start + uri_alloc_layout.total_size;
const hyperlink_count = @divFloor(cap.hyperlink_bytes, @sizeOf(HyperlinkEntry));
const hyperlink_map_layout = HyperlinkMap.layout(@intCast(hyperlink_count));
const hyperlink_map_start = alignForward(usize, uri_alloc_end, HyperlinkMap.base_align);
const hyperlink_map_end = hyperlink_map_start + hyperlink_map_layout.total_size;
const total_size = alignForward(usize, hyperlink_map_end, std.mem.page_size);
return .{
.total_size = total_size,
@ -1031,6 +1091,10 @@ pub const Page = struct {
.grapheme_alloc_layout = grapheme_alloc_layout,
.grapheme_map_start = grapheme_map_start,
.grapheme_map_layout = grapheme_map_layout,
.uri_alloc_start = uri_alloc_start,
.uri_alloc_layout = uri_alloc_layout,
.hyperlink_map_start = hyperlink_map_start,
.hyperlink_map_layout = hyperlink_map_layout,
.capacity = cap,
};
}
@ -1064,6 +1128,10 @@ pub const Capacity = struct {
/// Number of bytes to allocate for grapheme data.
grapheme_bytes: usize = grapheme_bytes_default,
/// Number of bytes to allocate for hyperlink data. The bytes
/// are shared use for IDs, URIs, and hyperlink entries.
hyperlink_bytes: usize = hyperlink_bytes_default,
pub const Adjustment = struct {
cols: ?size.CellCountInt = null,
};
@ -1089,7 +1157,9 @@ pub const Capacity = struct {
// for rows & cells (which will allow us to calculate the number of
// rows we can fit at a certain column width) we need to layout the
// "meta" members of the page (i.e. everything else) from the end.
const grapheme_map_start = alignBackward(usize, layout.total_size - layout.grapheme_map_layout.total_size, GraphemeMap.base_align);
const hyperlink_map_start = alignBackward(usize, layout.total_size - layout.hyperlink_map_layout.total_size, HyperlinkMap.base_align);
const uri_alloc_start = alignBackward(usize, hyperlink_map_start - layout.uri_alloc_layout.total_size, UriAlloc.base_align);
const grapheme_map_start = alignBackward(usize, uri_alloc_start - layout.grapheme_map_layout.total_size, GraphemeMap.base_align);
const grapheme_alloc_start = alignBackward(usize, grapheme_map_start - layout.grapheme_alloc_layout.total_size, GraphemeAlloc.base_align);
const styles_start = alignBackward(usize, grapheme_alloc_start - layout.styles_layout.total_size, style.Set.base_align);