diff --git a/src/font/CodepointResolver.zig b/src/font/CodepointResolver.zig index 5caeb728f..c090356ff 100644 --- a/src/font/CodepointResolver.zig +++ b/src/font/CodepointResolver.zig @@ -383,7 +383,7 @@ test getIndex { var lib = try Library.init(); defer lib.deinit(); - var c = try Collection.init(alloc); + var c = Collection.init(); c.load_options = .{ .library = lib }; { @@ -464,7 +464,7 @@ test "getIndex disabled font style" { var lib = try Library.init(); defer lib.deinit(); - var c = try Collection.init(alloc); + var c = Collection.init(); c.load_options = .{ .library = lib }; _ = try c.add(alloc, .regular, .{ .loaded = try Face.init( @@ -516,7 +516,7 @@ test "getIndex box glyph" { var lib = try Library.init(); defer lib.deinit(); - const c = try Collection.init(alloc); + const c = Collection.init(); var r: CodepointResolver = .{ .collection = c, diff --git a/src/font/Collection.zig b/src/font/Collection.zig index 08d5c81c3..f5e1880ed 100644 --- a/src/font/Collection.zig +++ b/src/font/Collection.zig @@ -40,21 +40,18 @@ faces: StyleArray, load_options: ?LoadOptions = null, /// Initialize an empty collection. -pub fn init( - alloc: Allocator, -) !Collection { +pub fn init() Collection { // Initialize our styles array, preallocating some space that is // likely to be used. - var faces = StyleArray.initFill(.{}); - for (&faces.values) |*v| try v.ensureTotalCapacityPrecise(alloc, 2); - return .{ .faces = faces }; + return .{ .faces = StyleArray.initFill(.{}) }; } pub fn deinit(self: *Collection, alloc: Allocator) void { var it = self.faces.iterator(); - while (it.next()) |entry| { - for (entry.value.items) |*item| item.deinit(); - entry.value.deinit(alloc); + while (it.next()) |array| { + var entry_it = array.value.iterator(0); + while (entry_it.next()) |entry| entry.deinit(); + array.value.deinit(alloc); } if (self.load_options) |*v| v.deinit(alloc); @@ -85,14 +82,14 @@ pub fn add( const list = self.faces.getPtr(style); // We have some special indexes so we must never pass those. - if (list.items.len >= Index.Special.start - 1) + const idx = list.count(); + if (idx >= Index.Special.start - 1) return error.CollectionFull; // If this is deferred and we don't have load options, we can't. if (face.isDeferred() and self.load_options == null) return error.DeferredLoadingUnavailable; - const idx = list.items.len; try list.append(alloc, face); return .{ .style = style, .idx = @intCast(idx) }; } @@ -106,7 +103,7 @@ pub fn getFace(self: *Collection, index: Index) !*Face { if (index.special() != null) return error.SpecialHasNoFace; const list = self.faces.getPtr(index.style); const item: *Entry = item: { - var item = &list.items[index.idx]; + var item = list.at(index.idx); switch (item.*) { .alias => |ptr| item = ptr, @@ -159,13 +156,17 @@ pub fn getIndex( style: Style, p_mode: PresentationMode, ) ?Index { - for (self.faces.get(style).items, 0..) |elem, i| { - if (elem.hasCodepoint(cp, p_mode)) { + var i: usize = 0; + var it = self.faces.get(style).constIterator(0); + while (it.next()) |entry| { + if (entry.hasCodepoint(cp, p_mode)) { return .{ .style = style, .idx = @intCast(i), }; } + + i += 1; } // Not found @@ -183,8 +184,8 @@ pub fn hasCodepoint( p_mode: PresentationMode, ) bool { const list = self.faces.get(index.style); - if (index.idx >= list.items.len) return false; - return list.items[index.idx].hasCodepoint(cp, p_mode); + if (index.idx >= list.count()) return false; + return list.at(index.idx).hasCodepoint(cp, p_mode); } /// Ensure we have an option for all styles in the collection, such @@ -210,7 +211,7 @@ pub fn completeStyles(self: *Collection, alloc: Allocator) !void { pub fn autoItalicize(self: *Collection, alloc: Allocator) !void { // If we have an italic font, do nothing. const italic_list = self.faces.getPtr(.italic); - if (italic_list.items.len > 0) return; + if (italic_list.count() > 0) return; // Not all font backends support auto-italicization. if (comptime !@hasDecl(Face, "italicize")) { @@ -224,10 +225,10 @@ pub fn autoItalicize(self: *Collection, alloc: Allocator) !void { // Our regular font. If we have no regular font we also do nothing. const regular = regular: { const list = self.faces.get(.regular); - if (list.items.len == 0) return; + if (list.count() == 0) return; // Find our first regular face that has text glyphs. - for (0..list.items.len) |i| { + for (0..list.count()) |i| { const face = try self.getFace(.{ .style = .regular, .idx = @intCast(i), @@ -277,8 +278,9 @@ pub fn setSize(self: *Collection, size: DesiredSize) !void { // Resize all our faces that are loaded var it = self.faces.iterator(); - while (it.next()) |entry| { - for (entry.value.items) |*elem| switch (elem.*) { + while (it.next()) |array| { + var entry_it = array.value.iterator(0); + while (entry_it.next()) |entry| switch (entry.*) { .loaded, .fallback_loaded => |*f| try f.setSize( opts.faceOptions(), ), @@ -299,7 +301,12 @@ pub fn setSize(self: *Collection, size: DesiredSize) !void { /// styles are typically loaded for a terminal session. The overhead per /// style even if it is not used or barely used is minimal given the /// small style count. -const StyleArray = std.EnumArray(Style, std.ArrayListUnmanaged(Entry)); +/// +/// We use a segmented list because the entry values must be pointer-stable +/// to support the "alias" field in Entry. SegmentedList also lets us do +/// a prealloc which is great for performance since most happy path cases +/// do not use many font fallbacks. +const StyleArray = std.EnumArray(Style, std.SegmentedList(Entry, 4)); /// Load options are used to configure all the details a Collection /// needs to load deferred faces. @@ -520,7 +527,7 @@ test init { const testing = std.testing; const alloc = testing.allocator; - var c = try init(alloc); + var c = init(); defer c.deinit(alloc); } @@ -532,7 +539,7 @@ test "add full" { var lib = try Library.init(); defer lib.deinit(); - var c = try init(alloc); + var c = init(); defer c.deinit(alloc); for (0..Index.Special.start - 1) |_| { @@ -558,7 +565,7 @@ test "add deferred without loading options" { const testing = std.testing; const alloc = testing.allocator; - var c = try init(alloc); + var c = init(); defer c.deinit(alloc); try testing.expectError(error.DeferredLoadingUnavailable, c.add( @@ -578,7 +585,7 @@ test getFace { var lib = try Library.init(); defer lib.deinit(); - var c = try init(alloc); + var c = init(); defer c.deinit(alloc); const idx = try c.add(alloc, .regular, .{ .loaded = try Face.init( @@ -602,7 +609,7 @@ test getIndex { var lib = try Library.init(); defer lib.deinit(); - var c = try init(alloc); + var c = init(); defer c.deinit(alloc); _ = try c.add(alloc, .regular, .{ .loaded = try Face.init( @@ -635,7 +642,7 @@ test autoItalicize { var lib = try Library.init(); defer lib.deinit(); - var c = try init(alloc); + var c = init(); defer c.deinit(alloc); c.load_options = .{ .library = lib }; @@ -658,7 +665,7 @@ test setSize { var lib = try Library.init(); defer lib.deinit(); - var c = try init(alloc); + var c = init(); defer c.deinit(alloc); c.load_options = .{ .library = lib }; @@ -681,7 +688,7 @@ test hasCodepoint { var lib = try Library.init(); defer lib.deinit(); - var c = try init(alloc); + var c = init(); defer c.deinit(alloc); c.load_options = .{ .library = lib }; @@ -705,7 +712,7 @@ test "hasCodepoint emoji default graphical" { var lib = try Library.init(); defer lib.deinit(); - var c = try init(alloc); + var c = init(); defer c.deinit(alloc); c.load_options = .{ .library = lib }; diff --git a/src/font/SharedGrid.zig b/src/font/SharedGrid.zig index c11c8a4ae..6c5fa43f2 100644 --- a/src/font/SharedGrid.zig +++ b/src/font/SharedGrid.zig @@ -325,7 +325,7 @@ const TestMode = enum { normal }; fn testGrid(mode: TestMode, alloc: Allocator, lib: Library) !SharedGrid { const testFont = @import("test.zig").fontRegular; - var c = try Collection.init(alloc); + var c = Collection.init(); c.load_options = .{ .library = lib }; switch (mode) { diff --git a/src/font/SharedGridSet.zig b/src/font/SharedGridSet.zig index 857368a13..c576e08e1 100644 --- a/src/font/SharedGridSet.zig +++ b/src/font/SharedGridSet.zig @@ -167,7 +167,7 @@ fn collection( .metric_modifiers = key.metric_modifiers, }; - var c = try Collection.init(self.alloc); + var c = Collection.init(); errdefer c.deinit(self.alloc); c.load_options = load_options; diff --git a/src/font/discovery.zig b/src/font/discovery.zig index 70823989e..de17a3fb6 100644 --- a/src/font/discovery.zig +++ b/src/font/discovery.zig @@ -453,22 +453,22 @@ pub const CoreText = struct { // here. if (desc.bold and desc.italic) { - const items = collection.faces.get(.bold_italic).items; - if (items.len > 0) { + const entries = collection.faces.get(.bold_italic); + if (entries.count() > 0) { break :original try collection.getFace(.{ .style = .bold_italic }); } } if (desc.bold) { - const items = collection.faces.get(.bold).items; - if (items.len > 0) { + const entries = collection.faces.get(.bold); + if (entries.count() > 0) { break :original try collection.getFace(.{ .style = .bold }); } } if (desc.italic) { - const items = collection.faces.get(.italic).items; - if (items.len > 0) { + const entries = collection.faces.get(.italic); + if (entries.count() > 0) { break :original try collection.getFace(.{ .style = .italic }); } } diff --git a/src/font/shaper/coretext.zig b/src/font/shaper/coretext.zig index 27d80633c..afb41f596 100644 --- a/src/font/shaper/coretext.zig +++ b/src/font/shaper/coretext.zig @@ -1746,7 +1746,7 @@ fn testShaperWithFont(alloc: Allocator, font_req: TestFont) !TestShaper { var lib = try Library.init(); errdefer lib.deinit(); - var c = try Collection.init(alloc); + var c = Collection.init(); c.load_options = .{ .library = lib }; // Setup group