summaryrefslogtreecommitdiff
path: root/test/image_harness.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-08-09 10:41:33 -0300
committerGabriel Schneider <[email protected]>2026-08-10 09:17:07 -0300
commit9085cb5bfdd0b78ff3a62c0c71fc231dd7b5052a (patch)
tree91fce278abc85e4f7491de8b5f02184c8a2d5a56 /test/image_harness.zig
parent2b1fac6fecbf7d7cc08f9dad24687b05e9968c9a (diff)
downloadpardes-9085cb5bfdd0b78ff3a62c0c71fc231dd7b5052a.tar.gz
pardes-9085cb5bfdd0b78ff3a62c0c71fc231dd7b5052a.zip
replace ArrayLists with bounded storage
Diffstat (limited to 'test/image_harness.zig')
-rw-r--r--test/image_harness.zig133
1 files changed, 82 insertions, 51 deletions
diff --git a/test/image_harness.zig b/test/image_harness.zig
index d4cb8955..601bce49 100644
--- a/test/image_harness.zig
+++ b/test/image_harness.zig
@@ -73,8 +73,27 @@ fn cleanupBase(arena: std.mem.Allocator, base: [:0]const u8) void {
_ = libc.rmdir(base);
}
-fn appendFmt(arena: std.mem.Allocator, out: *std.ArrayList(u8), comptime fmt: []const u8, args: anytype) !void {
- try out.appendSlice(arena, try std.fmt.allocPrint(arena, fmt, args));
+const pdf_fixture_cap = 4096;
+
+const BoundedPdf = struct {
+ bytes: [pdf_fixture_cap]u8 = undefined,
+ len: usize = 0,
+
+ fn appendSlice(out: *BoundedPdf, bytes: []const u8) !void {
+ if (bytes.len > out.bytes.len - out.len) return error.FixturePdfTooLarge;
+ @memcpy(out.bytes[out.len..][0..bytes.len], bytes);
+ out.len += bytes.len;
+ }
+
+ fn written(out: *const BoundedPdf) []const u8 {
+ return out.bytes[0..out.len];
+ }
+};
+
+fn appendFmt(out: *BoundedPdf, comptime fmt: []const u8, args: anytype) !void {
+ const bytes = std.fmt.bufPrint(out.bytes[out.len..], fmt, args) catch
+ return error.FixturePdfTooLarge;
+ out.len += bytes.len;
}
/// A self-contained, valid PDF fixture. The first two pages retain the red /
@@ -83,6 +102,7 @@ fn appendFmt(arena: std.mem.Allocator, out: *std.ArrayList(u8), comptime fmt: []
/// headers. Building it here keeps the backend test independent of mutool,
/// fonts, and checked-in binary fixtures while the xref offsets remain exact.
fn writePdf(arena: std.mem.Allocator, path: [:0]const u8) !void {
+ _ = arena;
const first_stream =
\\1 0 0 rg
\\0 0 72 48 re
@@ -128,54 +148,63 @@ fn writePdf(arena: std.mem.Allocator, path: [:0]const u8) !void {
\\ET
;
- var out: std.ArrayList(u8) = .empty;
+ var out: BoundedPdf = .{};
var offsets: [16]usize = @splat(0);
- try out.appendSlice(arena, "%PDF-1.4\n%\xE2\xE3\xCF\xD3\n");
+ try out.appendSlice("%PDF-1.4\n%\xE2\xE3\xCF\xD3\n");
- offsets[1] = out.items.len;
- try out.appendSlice(arena, "1 0 obj\n<< /Type /Catalog /Pages 2 0 R /Outlines 12 0 R /PageMode /UseOutlines >>\nendobj\n");
- offsets[2] = out.items.len;
- try out.appendSlice(arena, "2 0 obj\n<< /Type /Pages /Count 4 /Kids [3 0 R 5 0 R 7 0 R 9 0 R] >>\nendobj\n");
- offsets[3] = out.items.len;
- try out.appendSlice(arena, "3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 72 48] /Resources << /Font << /F1 11 0 R >> >> /Contents 4 0 R >>\nendobj\n");
- offsets[4] = out.items.len;
- try appendFmt(arena, &out, "4 0 obj\n<< /Length {d} >>\nstream\n{s}endstream\nendobj\n", .{ first_stream.len, first_stream });
- offsets[5] = out.items.len;
- try out.appendSlice(arena, "5 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 72 48] /Resources << /Font << /F1 11 0 R >> >> /Contents 6 0 R >>\nendobj\n");
- offsets[6] = out.items.len;
- try appendFmt(arena, &out, "6 0 obj\n<< /Length {d} >>\nstream\n{s}endstream\nendobj\n", .{ second_stream.len, second_stream });
- offsets[7] = out.items.len;
- try out.appendSlice(arena, "7 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 72 50] /Resources << /Font << /F1 11 0 R >> >> /Contents 8 0 R >>\nendobj\n");
- offsets[8] = out.items.len;
- try appendFmt(arena, &out, "8 0 obj\n<< /Length {d} >>\nstream\n{s}endstream\nendobj\n", .{ third_stream.len, third_stream });
- offsets[9] = out.items.len;
- try out.appendSlice(arena, "9 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 72 52] /Resources << /Font << /F1 11 0 R >> >> /Contents 10 0 R >>\nendobj\n");
- offsets[10] = out.items.len;
- try appendFmt(arena, &out, "10 0 obj\n<< /Length {d} >>\nstream\n{s}endstream\nendobj\n", .{ fourth_stream.len, fourth_stream });
- offsets[11] = out.items.len;
- try out.appendSlice(arena, "11 0 obj\n<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>\nendobj\n");
- offsets[12] = out.items.len;
- try out.appendSlice(arena, "12 0 obj\n<< /Type /Outlines /First 13 0 R /Last 15 0 R /Count 3 >>\nendobj\n");
- offsets[13] = out.items.len;
- try out.appendSlice(arena, "13 0 obj\n<< /Title (Upper) /Parent 12 0 R /First 14 0 R /Last 14 0 R /Next 15 0 R /Count 1 /Dest [3 0 R /XYZ 4 8 null] >>\nendobj\n");
- offsets[14] = out.items.len;
- try out.appendSlice(arena, "14 0 obj\n<< /Title (Lower) /Parent 13 0 R /Dest [3 0 R /XYZ 4 28 null] >>\nendobj\n");
- offsets[15] = out.items.len;
- try out.appendSlice(arena, "15 0 obj\n<< /Title (Page two) /Parent 12 0 R /Prev 13 0 R /Dest [5 0 R /XYZ 4 38 null] >>\nendobj\n");
+ offsets[1] = out.len;
+ try out.appendSlice("1 0 obj\n<< /Type /Catalog /Pages 2 0 R /Outlines 12 0 R /PageMode /UseOutlines >>\nendobj\n");
+ offsets[2] = out.len;
+ try out.appendSlice("2 0 obj\n<< /Type /Pages /Count 4 /Kids [3 0 R 5 0 R 7 0 R 9 0 R] >>\nendobj\n");
+ offsets[3] = out.len;
+ try out.appendSlice("3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 72 48] /Resources << /Font << /F1 11 0 R >> >> /Contents 4 0 R >>\nendobj\n");
+ offsets[4] = out.len;
+ try appendFmt(&out, "4 0 obj\n<< /Length {d} >>\nstream\n{s}endstream\nendobj\n", .{ first_stream.len, first_stream });
+ offsets[5] = out.len;
+ try out.appendSlice("5 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 72 48] /Resources << /Font << /F1 11 0 R >> >> /Contents 6 0 R >>\nendobj\n");
+ offsets[6] = out.len;
+ try appendFmt(&out, "6 0 obj\n<< /Length {d} >>\nstream\n{s}endstream\nendobj\n", .{ second_stream.len, second_stream });
+ offsets[7] = out.len;
+ try out.appendSlice("7 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 72 50] /Resources << /Font << /F1 11 0 R >> >> /Contents 8 0 R >>\nendobj\n");
+ offsets[8] = out.len;
+ try appendFmt(&out, "8 0 obj\n<< /Length {d} >>\nstream\n{s}endstream\nendobj\n", .{ third_stream.len, third_stream });
+ offsets[9] = out.len;
+ try out.appendSlice("9 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 72 52] /Resources << /Font << /F1 11 0 R >> >> /Contents 10 0 R >>\nendobj\n");
+ offsets[10] = out.len;
+ try appendFmt(&out, "10 0 obj\n<< /Length {d} >>\nstream\n{s}endstream\nendobj\n", .{ fourth_stream.len, fourth_stream });
+ offsets[11] = out.len;
+ try out.appendSlice("11 0 obj\n<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>\nendobj\n");
+ offsets[12] = out.len;
+ try out.appendSlice("12 0 obj\n<< /Type /Outlines /First 13 0 R /Last 15 0 R /Count 3 >>\nendobj\n");
+ offsets[13] = out.len;
+ try out.appendSlice("13 0 obj\n<< /Title (Upper) /Parent 12 0 R /First 14 0 R /Last 14 0 R /Next 15 0 R /Count 1 /Dest [3 0 R /XYZ 4 8 null] >>\nendobj\n");
+ offsets[14] = out.len;
+ try out.appendSlice("14 0 obj\n<< /Title (Lower) /Parent 13 0 R /Dest [3 0 R /XYZ 4 28 null] >>\nendobj\n");
+ offsets[15] = out.len;
+ try out.appendSlice("15 0 obj\n<< /Title (Page two) /Parent 12 0 R /Prev 13 0 R /Dest [5 0 R /XYZ 4 38 null] >>\nendobj\n");
- const xref = out.items.len;
- try out.appendSlice(arena, "xref\n0 16\n0000000000 65535 f \n");
+ const xref = out.len;
+ try out.appendSlice("xref\n0 16\n0000000000 65535 f \n");
for (offsets[1..]) |offset|
- try appendFmt(arena, &out, "{d:0>10} 00000 n \n", .{offset});
- try appendFmt(arena, &out, "trailer\n<< /Size 16 /Root 1 0 R >>\nstartxref\n{d}\n%%EOF\n", .{xref});
- try eh.writeFile(path, out.items);
+ try appendFmt(&out, "{d:0>10} 00000 n \n", .{offset});
+ try appendFmt(&out, "trailer\n<< /Size 16 /Root 1 0 R >>\nstartxref\n{d}\n%%EOF\n", .{xref});
+ try eh.writeFile(path, out.written());
}
fn writePpm(arena: std.mem.Allocator, path: [:0]const u8, width: usize, height: usize) !void {
- var out: std.ArrayList(u8) = .empty;
- try out.appendSlice(arena, try std.fmt.allocPrint(arena, "P6\n{d} {d}\n255\n", .{ width, height }));
- for (0..width * height) |_| try out.appendSlice(arena, &fixture_rgb);
- try eh.writeFile(path, out.items);
+ var header_buf: [64]u8 = undefined;
+ const header = std.fmt.bufPrint(&header_buf, "P6\n{d} {d}\n255\n", .{ width, height }) catch
+ return error.FixturePpmHeaderTooLarge;
+ const pixel_count = try std.math.mul(usize, width, height);
+ const pixel_bytes = try std.math.mul(usize, pixel_count, fixture_rgb.len);
+ const out = try arena.alloc(u8, try std.math.add(usize, header.len, pixel_bytes));
+ @memcpy(out[0..header.len], header);
+ var offset = header.len;
+ for (0..pixel_count) |_| {
+ @memcpy(out[offset..][0..fixture_rgb.len], &fixture_rgb);
+ offset += fixture_rgb.len;
+ }
+ try eh.writeFile(path, out);
}
fn runKitty(arena: std.mem.Allocator, exe: [:0]const u8, base: [:0]const u8) !void {
@@ -656,16 +685,18 @@ fn readFile(allocator: std.mem.Allocator, path: [:0]const u8) ![]u8 {
const fd = libc.open(path, .{ .ACCMODE = .RDONLY }, @as(libc.mode_t, 0));
if (fd < 0) return error.OpenFailed;
defer _ = libc.close(fd);
- var out: std.ArrayList(u8) = .empty;
- errdefer out.deinit(allocator);
- var buf: [8192]u8 = undefined;
- while (true) {
- const n = libc.read(fd, &buf, buf.len);
+ const end = libc.lseek(fd, 0, libc.SEEK.END);
+ if (end < 0 or libc.lseek(fd, 0, libc.SEEK.SET) < 0) return error.ReadFailed;
+ const out = try allocator.alloc(u8, @intCast(end));
+ errdefer allocator.free(out);
+ var filled: usize = 0;
+ while (filled < out.len) {
+ const n = libc.read(fd, out[filled..].ptr, out.len - filled);
if (n < 0) return error.ReadFailed;
- if (n == 0) break;
- try out.appendSlice(allocator, buf[0..@intCast(n)]);
+ if (n == 0) return error.UnexpectedEof;
+ filled += @intCast(n);
}
- return out.toOwnedSlice(allocator);
+ return out;
}
fn ppmPixels(bytes: []const u8) ?[]const u8 {