lib/png/src/encode/capacity.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const flate = std.compress.flate;
3 const model = @import("model.zig");
4 const plan_mod = @import("plan.zig");
5
6 pub const storage_alignment: usize = @alignOf(usize);
7
8 pub const Limits = struct {
9 image: model.ImageView,
10 bounds: plan_mod.Bounds,
11 scratch: *plan_mod.Scratch,
12 };
13
14 pub const DeriveError = plan_mod.Error;
15
16 pub const Capacity = struct {
17 plan: plan_mod.Plan,
18 window_offset: usize,
19 raw_offset: usize,
20 count_offset: usize,
21 output_offset: usize,
22 storage_bytes: usize,
23
24 pub fn derive(limits: Limits) DeriveError!Capacity {
25 const plan = try plan_mod.Plan.inspect(limits.image, limits.bounds, limits.scratch);
26 std.debug.assert(plan.output_bytes > 0);
27 const window_offset: usize = 0;
28 const raw_offset = try added(window_offset, flate.max_window_len);
29 const count_offset = try added(raw_offset, plan_mod.stream_bytes);
30 const output_offset = try added(count_offset, plan_mod.stream_bytes);
31 return .{
32 .plan = plan,
33 .window_offset = window_offset,
34 .raw_offset = raw_offset,
35 .count_offset = count_offset,
36 .output_offset = output_offset,
37 .storage_bytes = try added(output_offset, plan.output_bytes),
38 };
39 }
40 };
41
42 fn added(left: usize, right: usize) DeriveError!usize {
43 return std.math.add(usize, left, right) catch error.CapacityOverflow;
44 }
45
46 fn modelCapacity(limits: Limits) DeriveError!Capacity {
47 const plan = try plan_mod.Plan.inspect(limits.image, limits.bounds, limits.scratch);
48 const raw_offset: u128 = flate.max_window_len;
49 const count_offset = raw_offset + plan_mod.stream_bytes;
50 const output_offset = count_offset + plan_mod.stream_bytes;
51 const storage_bytes = output_offset + plan.output_bytes;
52 if (storage_bytes > std.math.maxInt(usize)) return error.CapacityOverflow;
53 return .{
54 .plan = plan,
55 .window_offset = 0,
56 .raw_offset = @intCast(raw_offset),
57 .count_offset = @intCast(count_offset),
58 .output_offset = @intCast(output_offset),
59 .storage_bytes = @intCast(storage_bytes),
60 };
61 }
62
63 test "PNG encode capacity matches an independent byte model" {
64 comptime {
65 @stardustClaim(
66 @import("alloc_phase").capacity.witness(@import("./root.zig").Storage, "png_encode_capacity"),
67 null,
68 null,
69 null,
70 null,
71 null,
72 null,
73 );
74 }
75
76 const pixels = [_]u8{ 1, 2, 3, 255, 4, 5, 6, 255 };
77 var scratch: plan_mod.Scratch = undefined;
78 const limits = Limits{
79 .image = .{ .rgba8 = &pixels, .width = 2, .height = 1 },
80 .bounds = .{ .image_pixels = 2 },
81 .scratch = &scratch,
82 };
83 const capacity = try Capacity.derive(limits);
84 try std.testing.expectEqual(try modelCapacity(limits), capacity);
85 try std.testing.expectEqual(plan_mod.Scratch.bytes, capacity.output_offset);
86 try std.testing.expectEqual(
87 capacity.output_offset + capacity.plan.output_bytes,
88 capacity.storage_bytes,
89 );
90 }