summaryrefslogtreecommitdiff
path: root/src/allocators.zig
blob: 7a8433e82c79e3dc8bb726e3188ff6eafa558f5d (plain) (blame)
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));
}