diff options
| author | Gabriel Schneider <[email protected]> | 2026-08-09 10:41:33 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-08-10 09:17:07 -0300 |
| commit | 9085cb5bfdd0b78ff3a62c0c71fc231dd7b5052a (patch) | |
| tree | 91fce278abc85e4f7491de8b5f02184c8a2d5a56 /test/image_harness.zig | |
| parent | 2b1fac6fecbf7d7cc08f9dad24687b05e9968c9a (diff) | |
| download | pardes-9085cb5bfdd0b78ff3a62c0c71fc231dd7b5052a.tar.gz pardes-9085cb5bfdd0b78ff3a62c0c71fc231dd7b5052a.zip | |
replace ArrayLists with bounded storage
Diffstat (limited to 'test/image_harness.zig')
| -rw-r--r-- | test/image_harness.zig | 133 |
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 { |
