summaryrefslogtreecommitdiff
path: root/src/pdf.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/pdf.zig')
-rw-r--r--src/pdf.zig87
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();