lib/png/src/decode/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     bytes: []const u8,
10     bounds: plan_mod.Bounds,
11 };
12 
13 pub const DeriveError = plan_mod.Error;
14 
15 pub const Capacity = struct {
16     plan: plan_mod.Plan,
17     window_offset: usize,
18     prior_offset: usize,
19     current_offset: usize,
20     output_offset: usize,
21     storage_bytes: usize,
22 
23     pub fn derive(limits: Limits) DeriveError!Capacity {
24         const plan = try plan_mod.Plan.inspect(limits.bytes, limits.bounds);
25         const prior_offset = flate.max_window_len;
26         const current_offset = try model.added(prior_offset, plan.source_row_bytes);
27         const output_offset = try model.added(current_offset, plan.source_row_bytes);
28         return .{
29             .plan = plan,
30             .window_offset = 0,
31             .prior_offset = prior_offset,
32             .current_offset = current_offset,
33             .output_offset = output_offset,
34             .storage_bytes = try model.added(output_offset, plan.rgba8_bytes),
35         };
36     }
37 };
38 
39 fn modelCapacity(limits: Limits) DeriveError!Capacity {
40     const plan = try plan_mod.Plan.inspect(limits.bytes, limits.bounds);
41     const prior_offset: u128 = flate.max_window_len;
42     const current_offset = prior_offset + plan.source_row_bytes;
43     const output_offset = current_offset + plan.source_row_bytes;
44     const storage_bytes = output_offset + plan.rgba8_bytes;
45     if (storage_bytes > std.math.maxInt(usize)) return error.CapacityOverflow;
46     return .{
47         .plan = plan,
48         .window_offset = 0,
49         .prior_offset = @intCast(prior_offset),
50         .current_offset = @intCast(current_offset),
51         .output_offset = @intCast(output_offset),
52         .storage_bytes = @intCast(storage_bytes),
53     };
54 }
55 
56 test "PNG decode capacity matches an independent byte model" {
57     comptime {
58         @stardustClaim(
59             @import("alloc_phase").capacity.witness(@import("./root.zig").Storage, "png_decode_capacity"),
60             null,
61             null,
62             null,
63             null,
64             null,
65             null,
66         );
67     }
68 
69     const witness = @import("fixture.zig").indexed;
70     const limits = Limits{ .bytes = &witness, .bounds = try plan_mod.Bounds.exact(&witness) };
71     const capacity = try Capacity.derive(limits);
72     try std.testing.expectEqual(try modelCapacity(limits), capacity);
73     try std.testing.expectEqual(
74         capacity.output_offset + capacity.plan.rgba8_bytes,
75         capacity.storage_bytes,
76     );
77 }