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 }