font/coretext: creation functions can return null, handle OOM (#13679)

Catch NULL results from CoreFoundation/CoreText creation functions and
return error.OOM rather than null derefs later. I verified that this is
possible but didn't verify the behavior when it happens, this is just
defensive based on the report here: #13671 because it costs us nothing
really.
This commit is contained in:
Mitchell Hashimoto
2026-08-07 07:38:14 -07:00
committed by GitHub
4 changed files with 13 additions and 12 deletions

View File

@@ -21,13 +21,13 @@ pub const String = opaque {
pub fn createWithCharactersNoCopy(
unichars: []const u16,
) *String {
return @as(*String, @ptrFromInt(@intFromPtr(c.CFStringCreateWithCharactersNoCopy(
) Allocator.Error!*String {
return @ptrCast(@constCast(c.CFStringCreateWithCharactersNoCopy(
null,
@ptrCast(unichars.ptr),
@intCast(unichars.len),
foundation.c.kCFAllocatorNull,
))));
) orelse return error.OutOfMemory));
}
pub fn release(self: *String) void {

View File

@@ -27,10 +27,10 @@ pub const Typesetter = opaque {
pub fn createLine(
self: *Typesetter,
range: foundation.c.CFRange,
) *text.Line {
return @ptrFromInt(@intFromPtr(c.CTTypesetterCreateLine(
) Allocator.Error!*text.Line {
return @ptrCast(@constCast(c.CTTypesetterCreateLine(
@ptrCast(self),
range,
)));
) orelse return error.OutOfMemory));
}
};

View File

@@ -361,7 +361,7 @@ pub const Shaper = struct {
// Make room for the attributed string, CTTypesetter, and CTLine.
try self.cf_release_pool.ensureUnusedCapacity(self.alloc, 4);
const str = macos.foundation.String.createWithCharactersNoCopy(state.unichars.items);
const str = try macos.foundation.String.createWithCharactersNoCopy(state.unichars.items);
self.cf_release_pool.appendAssumeCapacity(str);
// Create an attributed string from our string
@@ -381,7 +381,7 @@ pub const Shaper = struct {
self.cf_release_pool.appendAssumeCapacity(typesetter);
// Create a line from the typesetter
const line = typesetter.createLine(.{ .location = 0, .length = 0 });
const line = try typesetter.createLine(.{ .location = 0, .length = 0 });
self.cf_release_pool.appendAssumeCapacity(line);
// This keeps track of the current x offset (sum of advance.width) and

View File

@@ -1146,6 +1146,11 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
state: *renderer.State,
cursor_blink_visible: bool,
) Allocator.Error!void {
// CoreText shaping accumulates objects for deferred release over
// the course of a frame. Always flush those objects, including
// when rebuilding the frame fails due to memory pressure.
defer self.font_shaper.endFrame();
// We fully deinit and reset the terminal state every so often
// so that a particularly large terminal state doesn't cause
// the renderer to hold on to retained memory.
@@ -1444,10 +1449,6 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
// Update custom shader uniforms that depend on terminal state.
self.updateCustomShaderUniformsFromState();
}
// Notify our shaper we're done for the frame. For some shapers,
// such as CoreText, this triggers off-thread cleanup logic.
self.font_shaper.endFrame();
}
/// Draw the frame to the screen.