From c88e2dd9754faeaa834f464f9614a961a2720111 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 5 Aug 2025 15:24:18 +0200 Subject: [PATCH] fix: don't leak Buffer.file_path --- src/buffer/Buffer.zig | 22 +++++++++++++++++----- src/buffer/Manager.zig | 8 ++++---- src/tui/mainview.zig | 4 ++-- src/tui/mode/overlay/buffer_palette.zig | 2 +- src/tui/status/tabs.zig | 8 ++++---- 5 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/buffer/Buffer.zig b/src/buffer/Buffer.zig index 0a2d752..c094845 100644 --- a/src/buffer/Buffer.zig +++ b/src/buffer/Buffer.zig @@ -33,7 +33,7 @@ external_allocator: Allocator, root: Root, leaves_buf: ?[]Node = null, file_buf: ?[]const u8 = null, -file_path: []const u8 = "", +file_path_buf: std.ArrayListUnmanaged(u8) = .empty, last_save: ?Root = null, file_exists: bool = true, file_eol_mode: EolMode = .lf, @@ -1084,6 +1084,7 @@ pub fn deinit(self: *Self) void { if (self.meta) |buf| self.external_allocator.free(buf); if (self.file_buf) |buf| self.external_allocator.free(buf); if (self.leaves_buf) |buf| self.external_allocator.free(buf); + self.file_path_buf.deinit(self.external_allocator); self.arena.deinit(); self.external_allocator.destroy(self); } @@ -1098,6 +1099,17 @@ pub fn get_meta(self: *Self) ?[]const u8 { return self.meta; } +pub fn set_file_path(self: *Self, file_path: []const u8) void { + self.file_path_buf.clearRetainingCapacity(); + self.file_path_buf.appendSlice(self.external_allocator, file_path) catch |e| switch (e) { + error.OutOfMemory => @panic("OOM in Buffer.set_file_path"), + }; +} + +pub inline fn get_file_path(self: *const Self) []const u8 { + return self.file_path_buf.items; +} + pub fn update_last_used_time(self: *Self) void { self.utime = std.time.milliTimestamp(); } @@ -1177,7 +1189,7 @@ pub fn load_from_string(self: *const Self, s: []const u8, eol_mode: *EolMode, ut pub fn load_from_string_and_update(self: *Self, file_path: []const u8, s: []const u8) LoadFromStringError!void { self.root = try self.load_from_string(s, &self.file_eol_mode, &self.file_utf8_sanitized); - self.file_path = try self.allocator.dupe(u8, file_path); + self.set_file_path(file_path); self.last_save = self.root; self.last_save_eol_mode = self.file_eol_mode; self.file_exists = false; @@ -1257,7 +1269,7 @@ pub fn load_from_file_and_update(self: *Self, file_path: []const u8) LoadFromFil var eol_mode: EolMode = .lf; var utf8_sanitized: bool = false; self.root = try self.load_from_file(file_path, &file_exists, &eol_mode, &utf8_sanitized); - self.file_path = try self.allocator.dupe(u8, file_path); + self.set_file_path(file_path); self.last_save = self.root; self.file_exists = file_exists; self.file_eol_mode = eol_mode; @@ -1276,7 +1288,7 @@ pub fn reset_to_last_saved(self: *Self) void { } pub fn refresh_from_file(self: *Self) LoadFromFileError!void { - try self.load_from_file_and_update(self.file_path); + try self.load_from_file_and_update(self.get_file_path()); self.update_last_used_time(); } @@ -1366,7 +1378,7 @@ pub fn store_to_file_and_clean(self: *Self, file_path: []const u8) StoreToFileEr self.file_utf8_sanitized = false; if (self.ephemeral) { self.ephemeral = false; - self.file_path = try self.allocator.dupe(u8, file_path); + self.set_file_path(file_path); } } diff --git a/src/buffer/Manager.zig b/src/buffer/Manager.zig index 7bcd881..d2484ee 100644 --- a/src/buffer/Manager.zig +++ b/src/buffer/Manager.zig @@ -64,15 +64,15 @@ pub fn delete_buffer(self: *Self, file_path: []const u8) bool { pub fn retire(_: *Self, buffer: *Buffer, meta: ?[]const u8) void { if (meta) |buf| buffer.set_meta(buf) catch {}; - tp.trace(tp.channel.debug, .{ "buffer", "retire", buffer.file_path, "hidden", buffer.hidden, "ephemeral", buffer.ephemeral }); + tp.trace(tp.channel.debug, .{ "buffer", "retire", buffer.get_file_path(), "hidden", buffer.hidden, "ephemeral", buffer.ephemeral }); if (meta) |buf| tp.trace(tp.channel.debug, tp.message{ .buf = buf }); } pub fn close_buffer(self: *Self, buffer: *Buffer) void { buffer.hidden = true; - tp.trace(tp.channel.debug, .{ "buffer", "close", buffer.file_path, "hidden", buffer.hidden, "ephemeral", buffer.ephemeral }); + tp.trace(tp.channel.debug, .{ "buffer", "close", buffer.get_file_path(), "hidden", buffer.hidden, "ephemeral", buffer.ephemeral }); if (buffer.is_ephemeral()) { - _ = self.buffers.remove(buffer.file_path); + _ = self.buffers.remove(buffer.get_file_path()); buffer.deinit(); } } @@ -116,7 +116,7 @@ pub fn save_all(self: *const Self) Buffer.StoreToFileError!void { if (buffer.is_ephemeral()) buffer.mark_clean() else - try buffer.store_to_file_and_clean(buffer.file_path); + try buffer.store_to_file_and_clean(buffer.get_file_path()); } } diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index 0b92e31..c210882 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -1316,11 +1316,11 @@ fn get_next_mru_buffer(self: *Self) ?[]const u8 { defer self.allocator.free(buffers); const active_file_path = self.get_active_file_path(); for (buffers) |buffer| { - if (active_file_path) |fp| if (std.mem.eql(u8, fp, buffer.file_path)) + if (active_file_path) |fp| if (std.mem.eql(u8, fp, buffer.get_file_path())) continue; if (buffer.hidden) continue; - return buffer.file_path; + return buffer.get_file_path(); } return null; } diff --git a/src/tui/mode/overlay/buffer_palette.zig b/src/tui/mode/overlay/buffer_palette.zig index 3db56c1..794ca16 100644 --- a/src/tui/mode/overlay/buffer_palette.zig +++ b/src/tui/mode/overlay/buffer_palette.zig @@ -34,7 +34,7 @@ pub fn load_entries(palette: *Type) !usize { else ""; (try palette.entries.addOne()).* = .{ - .label = buffer.file_path, + .label = buffer.get_file_path(), .icon = buffer.file_type_icon orelse "", .color = buffer.file_type_color, .indicator = indicator, diff --git a/src/tui/status/tabs.zig b/src/tui/status/tabs.zig index 58ede12..c2de5b2 100644 --- a/src/tui/status/tabs.zig +++ b/src/tui/status/tabs.zig @@ -265,7 +265,7 @@ const TabBar = struct { fn navigate_to_tab(tab: *const TabBarTab) void { const buffer_manager = tui.get_buffer_manager() orelse @panic("tabs no buffer manager"); if (buffer_manager.buffer_from_ref(tab.buffer_ref)) |buffer| - tp.self_pid().send(.{ "cmd", "navigate", .{ .file = buffer.file_path } }) catch {}; + tp.self_pid().send(.{ "cmd", "navigate", .{ .file = buffer.get_file_path() } }) catch {}; } }; @@ -298,13 +298,13 @@ const Tab = struct { fn on_click(self: *@This(), _: *Button.State(@This())) void { const buffer_manager = tui.get_buffer_manager() orelse @panic("tabs no buffer manager"); if (buffer_manager.buffer_from_ref(self.buffer_ref)) |buffer| - tp.self_pid().send(.{ "cmd", "navigate", .{ .file = buffer.file_path } }) catch {}; + tp.self_pid().send(.{ "cmd", "navigate", .{ .file = buffer.get_file_path() } }) catch {}; } fn on_click2(self: *@This(), _: *Button.State(@This())) void { const buffer_manager = tui.get_buffer_manager() orelse @panic("tabs no buffer manager"); if (buffer_manager.buffer_from_ref(self.buffer_ref)) |buffer| - tp.self_pid().send(.{ "cmd", "close_buffer", .{buffer.file_path} }) catch {}; + tp.self_pid().send(.{ "cmd", "close_buffer", .{buffer.get_file_path()} }) catch {}; } fn render(self: *@This(), btn: *Button.State(@This()), theme: *const Widget.Theme) bool { @@ -450,7 +450,7 @@ const Tab = struct { } fn name_from_buffer(buffer: *Buffer) []const u8 { - const file_path = buffer.file_path; + const file_path = buffer.get_file_path(); if (file_path.len > 0 and file_path[0] == '*') return file_path; const basename_begin = std.mem.lastIndexOfScalar(u8, file_path, std.fs.path.sep);