Something went wrong. Try again.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241const std = @import("std");const Allocator = std.mem.Allocator;const hash_map = @import("hash_map.zig");const AutoOffsetHashMap = hash_map.AutoOffsetHashMap;const pagepkg = @import("page.zig");const size = @import("size.zig");const Offset = size.Offset;const Cell = pagepkg.Cell;const Page = pagepkg.Page;const RefCountedSet = @import("ref_counted_set.zig").RefCountedSet;const Wyhash = std.hash.Wyhash;const autoHash = std.hash.autoHash;const autoHashStrat = std.hash.autoHashStrat;
/// The unique identifier for a hyperlink. This is at most the number of cells/// that can fit in a single terminal page, since each cell can only contain/// at most one hyperlink.pub const Id = size.HyperlinkCountInt;
// The mapping of cell to hyperlink. We use an offset hash map to save space// since its very unlikely a cell is a hyperlink, so its a waste to store// the hyperlink ID in the cell itself.pub const Map = AutoOffsetHashMap(Offset(Cell), Id);
/// A fully decoded hyperlink that may or may not have its/// memory within a page. The memory location of this is dependent/// on the context so users should check with the source of the/// hyperlink.pub const Hyperlink = struct { id: Hyperlink.Id, uri: []const u8,
/// See PageEntry.Id pub const Id = union(enum) { explicit: []const u8, implicit: size.OffsetInt, };
/// Deinit and deallocate all the pointers using the given /// allocator. /// /// WARNING: This should only be called if the hyperlink was /// heap-allocated. This DOES NOT need to be unconditionally /// called. pub fn deinit(self: *const Hyperlink, alloc: Allocator) void { alloc.free(self.uri); switch (self.id) { .implicit => {}, .explicit => |v| alloc.free(v), } }
/// Duplicate a hyperlink by allocating all values with the /// given allocator. The returned hyperlink should have deinit /// called. pub fn dupe( self: *const Hyperlink, alloc: Allocator, ) Allocator.Error!Hyperlink { const uri = try alloc.dupe(u8, self.uri); errdefer alloc.free(uri);
const id: Hyperlink.Id = switch (self.id) { .implicit => self.id, .explicit => |v| .{ .explicit = try alloc.dupe(u8, v) }, }; errdefer switch (id) { .implicit => {}, .explicit => |v| alloc.free(v), };
return .{ .id = id, .uri = uri }; }};
/// A hyperlink that has been committed to page memory. This/// is a "page entry" because while it represents a hyperlink,/// some decoding (pointer chasing) is still necessary to get the/// fully realized ID, URI, etc.pub const PageEntry = struct { id: PageEntry.Id, uri: Offset(u8).Slice,
pub const Id = union(enum) { /// An explicitly provided ID via the OSC8 sequence. explicit: Offset(u8).Slice,
/// No ID was provided so we auto-generate the ID based on an /// incrementing counter attached to the screen. implicit: size.OffsetInt, };
/// Duplicate this hyperlink from one page to another. pub fn dupe( self: *const PageEntry, self_page: *const Page, dst_page: *Page, ) error{OutOfMemory}!PageEntry { var copy = self.*;
// If the pages are the same then we can return a shallow copy. if (self_page == dst_page) return copy;
// Copy the URI { const uri = self.uri.slice(self_page.memory); const buf = try dst_page.string_alloc.alloc(u8, dst_page.memory, uri.len); @memcpy(buf, uri); copy.uri = .{ .offset = size.getOffset(u8, dst_page.memory, &buf[0]), .len = uri.len, }; } errdefer dst_page.string_alloc.free( dst_page.memory, copy.uri.slice(dst_page.memory), );
// Copy the ID switch (copy.id) { .implicit => {}, // Shallow is fine .explicit => |slice| { const id = slice.slice(self_page.memory); const buf = try dst_page.string_alloc.alloc(u8, dst_page.memory, id.len); @memcpy(buf, id); copy.id = .{ .explicit = .{ .offset = size.getOffset(u8, dst_page.memory, &buf[0]), .len = id.len, } }; }, } errdefer switch (copy.id) { .implicit => {}, .explicit => |v| dst_page.string_alloc.free( dst_page.memory, v.slice(dst_page.memory), ), };
return copy; }
pub fn hash(self: *const PageEntry, base: anytype) u64 { var hasher = Wyhash.init(0); autoHash(&hasher, std.meta.activeTag(self.id)); switch (self.id) { .implicit => |v| autoHash(&hasher, v), .explicit => |slice| autoHashStrat( &hasher, slice.slice(base), .Deep, ), } autoHashStrat( &hasher, self.uri.slice(base), .Deep, ); return hasher.final(); }
pub fn eql( self: *const PageEntry, self_base: anytype, other: *const PageEntry, other_base: anytype, ) bool { if (std.meta.activeTag(self.id) != std.meta.activeTag(other.id)) return false; switch (self.id) { .implicit => if (self.id.implicit != other.id.implicit) return false, .explicit => { const self_ptr = self.id.explicit.offset.ptr(self_base); const other_ptr = other.id.explicit.offset.ptr(other_base); if (!std.mem.eql( u8, self_ptr[0..self.id.explicit.len], other_ptr[0..other.id.explicit.len], )) return false; }, }
return std.mem.eql( u8, self.uri.slice(self_base), other.uri.slice(other_base), ); }
/// Free the memory for this entry from its page. pub fn free( self: *const PageEntry, page: *Page, ) void { const alloc = &page.string_alloc; switch (self.id) { .implicit => {}, .explicit => |v| alloc.free( page.memory, v.slice(page.memory), ), } alloc.free( page.memory, self.uri.slice(page.memory), ); }};
/// The set of hyperlinks. This is ref-counted so that a set of cells/// can share the same hyperlink without duplicating the data.pub const Set = RefCountedSet( PageEntry, Id, size.CellCountInt, struct { /// The page which holds the strings for items in this set. page: ?*Page = null,
/// The page which holds the strings for items /// looked up with, e.g., `add` or `lookup`, /// if different from the destination page. src_page: ?*const Page = null,
pub fn hash(self: *const @This(), link: PageEntry) u64 { return link.hash((self.src_page orelse self.page.?).memory); }
pub fn eql(self: *const @This(), a: PageEntry, b: PageEntry) bool { return a.eql( (self.src_page orelse self.page.?).memory, &b, self.page.?.memory, ); }
pub fn deleted(self: *const @This(), link: PageEntry) void { link.free(self.page.?); } },);