From fdd0c9748064fa08a2011c69167bacf24669a419 Mon Sep 17 00:00:00 2001 From: Emil Lerch Date: Tue, 2 Jun 2026 15:16:11 -0700 Subject: [PATCH] switch to parse_allocator = .none (fix production segfaults) --- src/analytics/analysis.zig | 2 +- src/analytics/projections.zig | 2 +- src/cache/store.zig | 8 ++++---- src/history.zig | 2 +- src/models/classification.zig | 2 +- src/models/transaction_log.zig | 2 +- src/tui/keybinds.zig | 7 +++++++ 7 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/analytics/analysis.zig b/src/analytics/analysis.zig index 197aa3a..01abd91 100644 --- a/src/analytics/analysis.zig +++ b/src/analytics/analysis.zig @@ -212,7 +212,7 @@ pub fn parseAccountsFile(allocator: std.mem.Allocator, data: []const u8) !Accoun } var reader = std.Io.Reader.fixed(data); - var it = srf.iterator(&reader, allocator, .{}) catch return error.InvalidData; + var it = srf.iterator(&reader, allocator, .{ .parse_allocator = .none }) catch return error.InvalidData; defer it.deinit(); while (try it.next()) |fields| { diff --git a/src/analytics/projections.zig b/src/analytics/projections.zig index c9d0cff..554244b 100644 --- a/src/analytics/projections.zig +++ b/src/analytics/projections.zig @@ -494,7 +494,7 @@ pub fn parseProjectionsConfig(data: ?[]const u8) UserConfig { const scratch = fba.allocator(); var reader = std.Io.Reader.fixed(raw); - var it = srf.iterator(&reader, scratch, .{}) catch return config; + var it = srf.iterator(&reader, scratch, .{ .parse_allocator = .none }) catch return config; defer it.deinit(); var saw_horizon = false; diff --git a/src/cache/store.zig b/src/cache/store.zig index dbee8a4..4a93ccf 100644 --- a/src/cache/store.zig +++ b/src/cache/store.zig @@ -352,7 +352,7 @@ pub const Store = struct { } var reader = std.Io.Reader.fixed(data); - var it = srf.iterator(&reader, self.allocator, .{}) catch return null; + var it = srf.iterator(&reader, self.allocator, .{ .parse_allocator = .none }) catch return null; defer it.deinit(); if (freshness == .fresh_only) { @@ -1132,7 +1132,7 @@ pub const Store = struct { if (std.mem.indexOf(u8, data, "# fetch_failed")) |_| return true; var reader = std.Io.Reader.fixed(data); - const it = srf.iterator(&reader, self.allocator, .{}) catch return false; + const it = srf.iterator(&reader, self.allocator, .{ .parse_allocator = .none }) catch return false; defer it.deinit(); if (it.expires == null) return false; @@ -1609,7 +1609,7 @@ pub fn deserializePortfolio(allocator: std.mem.Allocator, data: []const u8) !Por } var reader = std.Io.Reader.fixed(data); - var it = srf.iterator(&reader, allocator, .{}) catch return error.InvalidData; + var it = srf.iterator(&reader, allocator, .{ .parse_allocator = .none }) catch return error.InvalidData; defer it.deinit(); var skipped: usize = 0; @@ -1856,7 +1856,7 @@ test "writeMerged Dividend: no-change merge still rewrites to refresh expires" { defer allocator.free(data); var reader = std.Io.Reader.fixed(data); - const it = try srf.iterator(&reader, allocator, .{}); + const it = try srf.iterator(&reader, allocator, .{ .parse_allocator = .none }); defer it.deinit(); const new_expires = it.expires orelse return error.ExpiresMissing; diff --git a/src/history.zig b/src/history.zig index 3f62518..04bd4e4 100644 --- a/src/history.zig +++ b/src/history.zig @@ -78,7 +78,7 @@ pub fn parseSnapshotBytes( // fallback arena and are freed by `it.deinit()`. Snapshot // consumers dupe what they need into the caller's allocator // before the iterator dies. - var it = srf.iterator(&reader, allocator, .{}) catch return error.InvalidSrf; + var it = srf.iterator(&reader, allocator, .{ .parse_allocator = .none }) catch return error.InvalidSrf; defer it.deinit(); var meta_opt: ?snapshot.MetaRow = null; diff --git a/src/models/classification.zig b/src/models/classification.zig index 57f866a..9c58888 100644 --- a/src/models/classification.zig +++ b/src/models/classification.zig @@ -57,7 +57,7 @@ pub fn parseClassificationFile(allocator: std.mem.Allocator, data: []const u8) ! } var reader = std.Io.Reader.fixed(data); - var it = srf.iterator(&reader, allocator, .{}) catch return error.InvalidData; + var it = srf.iterator(&reader, allocator, .{ .parse_allocator = .none }) catch return error.InvalidData; defer it.deinit(); while (try it.next()) |fields| { diff --git a/src/models/transaction_log.zig b/src/models/transaction_log.zig index 0b517ec..a738703 100644 --- a/src/models/transaction_log.zig +++ b/src/models/transaction_log.zig @@ -260,7 +260,7 @@ pub fn parseTransactionLogFile( } var reader = std.Io.Reader.fixed(data); - var it = srf.iterator(&reader, allocator, .{}) catch return error.InvalidData; + var it = srf.iterator(&reader, allocator, .{ .parse_allocator = .none }) catch return error.InvalidData; defer it.deinit(); while (try it.next()) |fields| { diff --git a/src/tui/keybinds.zig b/src/tui/keybinds.zig index 9fa808d..733ab48 100644 --- a/src/tui/keybinds.zig +++ b/src/tui/keybinds.zig @@ -452,6 +452,13 @@ pub fn loadFromData(allocator: std.mem.Allocator, data: []const u8) ?KeyMap { pub fn loadFromDataChecked(allocator: std.mem.Allocator, data: []const u8) LoadOutcome { var reader = std.Io.Reader.fixed(data); + // `.{}` here defaults to `.parse_allocator = .parse_arena`, which + // is the lifetime model this function depends on: srf copies every + // parsed string into its internal arena, and we transfer ownership + // of that arena to the returned `KeyMap` below (rather than calling + // `ri.deinit()`). All other srf.iterator call sites in zfin use + // `.parse_allocator = .none` so strings slice into the input + // buffer; this is the one exception. var ri = srf.iterator(&reader, allocator, .{}) catch return .fallback; // Don't deinit `ri` until the end — its arena owns the // string slices we'll borrow into the returned KeyMap. We