diff options
Diffstat (limited to 'src/pdf.zig')
| -rw-r--r-- | src/pdf.zig | 87 |
1 files changed, 13 insertions, 74 deletions
diff --git a/src/pdf.zig b/src/pdf.zig index de5e6d86..8ddca497 100644 --- a/src/pdf.zig +++ b/src/pdf.zig @@ -2,66 +2,30 @@ //! setjmp/longjmp; pdf_bridge.c catches every such throw before control returns //! to Zig. This module is present in native builds unless `-Dmupdf=false`. const std = @import("std"); +const c_heap = @import("c_heap"); const c = @cImport({ @cInclude("pdf_bridge.h"); }); -const allocation_header_size = 16; +/// What MuPDF allocates from, itself and the libraries it bundles (they all +/// allocate through its context): `start`'s allocator, through the shared C +/// heap. MuPDF's allocator functions carry a user pointer the heap does not +/// need. var bridge_allocator: std.mem.Allocator = undefined; +const bridge_heap = c_heap.Heap(&bridge_allocator); var bridge_started = false; -comptime { - if (allocation_header_size < @sizeOf(usize)) - @compileError("PDF allocation header is too small"); -} - -fn bridgeAlloc(user: ?*anyopaque, size_arg: usize) callconv(.c) ?*anyopaque { - const user_ptr = user orelse return null; - const allocator: *std.mem.Allocator = @ptrCast(@alignCast(user_ptr)); - const size = @max(size_arg, 1); - const total = std.math.add(usize, allocation_header_size, size) catch - return null; - const bytes = allocator.alignedAlloc(u8, .@"16", total) catch return null; - const header: *align(16) usize = @ptrCast(bytes.ptr); - header.* = total; - return @ptrCast(bytes.ptr + allocation_header_size); +fn bridgeAlloc(_: ?*anyopaque, size: usize) callconv(.c) ?*anyopaque { + return bridge_heap.malloc(size); } -fn bridgeFree(user: ?*anyopaque, pointer: ?*anyopaque) callconv(.c) void { - const data = pointer orelse return; - const user_ptr = user orelse @panic("PDF allocator callback has no allocator"); - const allocator: *std.mem.Allocator = @ptrCast(@alignCast(user_ptr)); - const data_bytes: [*]u8 = @ptrCast(data); - const base: [*]align(16) u8 = - @ptrCast(@alignCast(data_bytes - allocation_header_size)); - const header: *align(16) usize = @ptrCast(base); - allocator.free(base[0..header.*]); +fn bridgeRealloc(_: ?*anyopaque, block: ?*anyopaque, size: usize) callconv(.c) ?*anyopaque { + return bridge_heap.realloc(block, size); } -fn bridgeRealloc( - user: ?*anyopaque, - pointer: ?*anyopaque, - size_arg: usize, -) callconv(.c) ?*anyopaque { - if (pointer == null) return bridgeAlloc(user, size_arg); - if (size_arg == 0) { - bridgeFree(user, pointer); - return null; - } - - const user_ptr = user orelse return null; - const allocator: *std.mem.Allocator = @ptrCast(@alignCast(user_ptr)); - const total = std.math.add(usize, allocation_header_size, size_arg) catch - return null; - const data_bytes: [*]u8 = @ptrCast(pointer.?); - const base: [*]align(16) u8 = - @ptrCast(@alignCast(data_bytes - allocation_header_size)); - const header: *align(16) usize = @ptrCast(base); - const resized = allocator.realloc(base[0..header.*], total) catch return null; - const resized_header: *align(16) usize = @ptrCast(resized.ptr); - resized_header.* = total; - return @ptrCast(resized.ptr + allocation_header_size); +fn bridgeFree(_: ?*anyopaque, block: ?*anyopaque) callconv(.c) void { + bridge_heap.free(block); } /// Installs the allocator used by all subsequently opened PDF documents and @@ -70,7 +34,7 @@ pub fn start(allocator: std.mem.Allocator) void { if (bridge_started) @panic("PDF allocator is already started"); bridge_allocator = allocator; const descriptor = c.pardes_pdf_allocator{ - .user = &bridge_allocator, + .user = null, .alloc = bridgeAlloc, .realloc = bridgeRealloc, .free = bridgeFree, @@ -90,31 +54,6 @@ pub fn stop() void { bridge_allocator = undefined; } -test "PDF C allocator callbacks preserve and free exact allocations" { - var allocator = std.testing.allocator; - - var live: ?*anyopaque = bridgeAlloc(&allocator, 4) orelse - return error.OutOfMemory; - defer if (live) |pointer| bridgeFree(&allocator, pointer); - const original: [*]u8 = @ptrCast(live.?); - @memcpy(original[0..4], "data"); - - live = bridgeRealloc(&allocator, live, 32) orelse - return error.OutOfMemory; - const grown: [*]u8 = @ptrCast(live.?); - try std.testing.expectEqualSlices(u8, "data", grown[0..4]); - - try std.testing.expect(bridgeAlloc( - &allocator, - std.math.maxInt(usize), - ) == null); - - const zero = bridgeAlloc(&allocator, 0) orelse return error.OutOfMemory; - bridgeFree(&allocator, zero); - try std.testing.expect(bridgeRealloc(&allocator, live, 0) == null); - live = null; -} - test "custom allocator owns a complete PDF document lifecycle" { start(std.testing.allocator); defer stop(); |
