1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
const std = @import("std");
const builtin = @import("builtin");
const config = @import("pardes_config");
const Allocator = std.mem.Allocator;
const debug_enabled = builtin.mode == .Debug;
const reduced_target = config.platform == .web or builtin.os.tag == .freestanding;
const KiB = 1024;
const MiB = 1024 * KiB;
const capacities = struct {
const pardes = if (reduced_target) 8 * MiB else 32 * MiB;
const frame = if (reduced_target) 4 * MiB else 16 * MiB;
const tree_sitter = if (reduced_target) 4 * MiB else 16 * MiB;
const image = if (reduced_target) 64 * KiB else 32 * MiB;
const pdf = if (reduced_target or !config.mupdf) 64 * KiB else 64 * MiB;
};
pub const Allocators = struct {
pardes: Allocator,
frame: Allocator,
lsp: Allocator,
tree_sitter: Allocator,
image: Allocator,
pdf: Allocator,
};
var pardes_fallback: std.heap.StackFallbackAllocator(capacities.pardes) = undefined;
var frame_fallback: std.heap.StackFallbackAllocator(capacities.frame) = undefined;
var tree_sitter_fallback: std.heap.StackFallbackAllocator(capacities.tree_sitter) = undefined;
var image_fallback: std.heap.StackFallbackAllocator(capacities.image) = undefined;
var pdf_fallback: std.heap.StackFallbackAllocator(capacities.pdf) = undefined;
const Debug = std.heap.DebugAllocator(.{});
var pardes_debug: Debug = .init;
var frame_debug: Debug = .init;
var lsp_debug: Debug = .init;
var tree_sitter_debug: Debug = .init;
var image_debug: Debug = .init;
var pdf_debug: Debug = .init;
/// Returns ordinary allocators backed by stdlib fixed-buffer fallbacks. LSP
/// keeps the caller's allocator because its detached workers are concurrent.
pub fn init(fallback: Allocator) Allocators {
pardes_fallback.fallback_allocator = fallback;
pardes_fallback.get_called = if (std.debug.runtime_safety) false else {};
frame_fallback.fallback_allocator = fallback;
frame_fallback.get_called = if (std.debug.runtime_safety) false else {};
tree_sitter_fallback.fallback_allocator = fallback;
tree_sitter_fallback.get_called = if (std.debug.runtime_safety) false else {};
image_fallback.fallback_allocator = fallback;
image_fallback.get_called = if (std.debug.runtime_safety) false else {};
pdf_fallback.fallback_allocator = fallback;
pdf_fallback.get_called = if (std.debug.runtime_safety) false else {};
const raw: Allocators = .{
.pardes = pardes_fallback.get(),
.frame = frame_fallback.get(),
.lsp = fallback,
.tree_sitter = tree_sitter_fallback.get(),
.image = image_fallback.get(),
.pdf = pdf_fallback.get(),
};
if (!debug_enabled) return raw;
pardes_debug = .{ .backing_allocator = raw.pardes };
frame_debug = .{ .backing_allocator = raw.frame };
lsp_debug = .{ .backing_allocator = raw.lsp };
tree_sitter_debug = .{ .backing_allocator = raw.tree_sitter };
image_debug = .{ .backing_allocator = raw.image };
pdf_debug = .{ .backing_allocator = raw.pdf };
return .{
.pardes = pardes_debug.allocator(),
.frame = frame_debug.allocator(),
.lsp = lsp_debug.allocator(),
.tree_sitter = tree_sitter_debug.allocator(),
.image = image_debug.allocator(),
.pdf = pdf_debug.allocator(),
};
}
pub fn deinit() void {
if (!debug_enabled) return;
var leaked = pardes_debug.deinit() == .leak;
leaked = (frame_debug.deinit() == .leak) or leaked;
leaked = (lsp_debug.deinit() == .leak) or leaked;
leaked = (tree_sitter_debug.deinit() == .leak) or leaked;
leaked = (image_debug.deinit() == .leak) or leaked;
leaked = (pdf_debug.deinit() == .leak) or leaked;
if (leaked) @panic("allocator leaks detected");
}
test "fixed allocators are separate, spill, and restart" {
var allocs = init(std.testing.allocator);
const core = try allocs.pardes.alloc(u8, 32);
const frame = try allocs.frame.alloc(u8, 32);
try std.testing.expect(pardes_fallback.fixed_buffer_allocator.ownsPtr(core.ptr));
try std.testing.expect(frame_fallback.fixed_buffer_allocator.ownsPtr(frame.ptr));
try std.testing.expect(core.ptr != frame.ptr);
const spill = try allocs.pardes.alloc(u8, capacities.pardes + 1);
try std.testing.expect(!pardes_fallback.fixed_buffer_allocator.ownsPtr(spill.ptr));
allocs.pardes.free(spill);
allocs.frame.free(frame);
allocs.pardes.free(core);
deinit();
allocs = init(std.testing.allocator);
defer deinit();
const restarted = try allocs.pardes.alloc(u8, 32);
defer allocs.pardes.free(restarted);
try std.testing.expect(pardes_fallback.fixed_buffer_allocator.ownsPtr(restarted.ptr));
}
|