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 }