1use core::mem::size_of;
4
5use super::{
6 HtOwnedCodeBlockBatchJob, J2kDirectColorPlan, J2kDirectGrayscalePlan, J2kDirectGrayscaleStep,
7 J2kDirectRgbaPlan, J2kOwnedCodeBlockBatchJob, J2kReferencedClassicPlan, J2kReferencedHtj2kPlan,
8 J2kReferencedTilePlan, DEFAULT_MAX_DECODE_BYTES,
9};
10use crate::{
11 HtCodeBlockPayloadRanges, J2kClassicCodeBlockPayload, J2kCodeBlockSegment, J2kCodestreamRange,
12};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub struct DecodePlanAllocationError;
17
18#[derive(Default)]
19struct Budget {
20 bytes: usize,
21}
22
23impl Budget {
24 fn include_capacity<T>(&mut self, capacity: usize) -> Result<(), DecodePlanAllocationError> {
25 let bytes = capacity
26 .checked_mul(size_of::<T>())
27 .ok_or(DecodePlanAllocationError)?;
28 self.bytes = self
29 .bytes
30 .checked_add(bytes)
31 .ok_or(DecodePlanAllocationError)?;
32 (self.bytes <= DEFAULT_MAX_DECODE_BYTES)
33 .then_some(())
34 .ok_or(DecodePlanAllocationError)
35 }
36}
37
38impl J2kDirectGrayscalePlan {
39 pub fn retained_allocation_bytes(&self) -> Result<usize, DecodePlanAllocationError> {
41 let mut budget = Budget::default();
42 include_grayscale(&mut budget, self)?;
43 Ok(budget.bytes)
44 }
45}
46
47impl J2kDirectColorPlan {
48 pub fn retained_allocation_bytes(&self) -> Result<usize, DecodePlanAllocationError> {
50 retained_components(&self.component_plans, self.component_plans.capacity())
51 }
52}
53
54impl J2kDirectRgbaPlan {
55 pub fn retained_allocation_bytes(&self) -> Result<usize, DecodePlanAllocationError> {
57 retained_components(&self.component_plans, self.component_plans.capacity())
58 }
59}
60
61impl J2kReferencedHtj2kPlan {
62 pub fn retained_allocation_bytes(&self) -> Result<usize, DecodePlanAllocationError> {
64 let mut budget = Budget::default();
65 match self {
66 Self::Grayscale {
67 tiles, payloads, ..
68 }
69 | Self::Color {
70 tiles, payloads, ..
71 }
72 | Self::Rgba {
73 tiles, payloads, ..
74 } => {
75 include_tiles(&mut budget, tiles, tiles.capacity())?;
76 budget.include_capacity::<HtCodeBlockPayloadRanges>(payloads.capacity())?;
77 }
78 }
79 Ok(budget.bytes)
80 }
81}
82
83impl J2kReferencedClassicPlan {
84 pub fn retained_allocation_bytes(&self) -> Result<usize, DecodePlanAllocationError> {
86 let mut budget = Budget::default();
87 match self {
88 Self::Grayscale {
89 tiles,
90 payloads,
91 ranges,
92 ..
93 }
94 | Self::Color {
95 tiles,
96 payloads,
97 ranges,
98 ..
99 }
100 | Self::Rgba {
101 tiles,
102 payloads,
103 ranges,
104 ..
105 } => {
106 include_tiles(&mut budget, tiles, tiles.capacity())?;
107 include_classic(&mut budget, payloads.capacity(), ranges.capacity())?;
108 }
109 }
110 Ok(budget.bytes)
111 }
112}
113
114fn include_tiles(
115 budget: &mut Budget,
116 tiles: &[J2kReferencedTilePlan],
117 capacity: usize,
118) -> Result<(), DecodePlanAllocationError> {
119 budget.include_capacity::<J2kReferencedTilePlan>(capacity)?;
120 for tile in tiles {
121 include_classic(
122 budget,
123 tile.classic_payloads.capacity(),
124 tile.classic_ranges.capacity(),
125 )?;
126 if let Some(plan) = tile.grayscale_geometry() {
127 include_grayscale(budget, plan)?;
128 } else if let Some(plan) = tile.color_geometry() {
129 include_components(
130 budget,
131 &plan.component_plans,
132 plan.component_plans.capacity(),
133 )?;
134 } else if let Some(plan) = tile.rgba_geometry() {
135 include_components(
136 budget,
137 &plan.component_plans,
138 plan.component_plans.capacity(),
139 )?;
140 } else {
141 return Err(DecodePlanAllocationError);
142 }
143 }
144 Ok(())
145}
146
147fn include_classic(
148 budget: &mut Budget,
149 payload_capacity: usize,
150 range_capacity: usize,
151) -> Result<(), DecodePlanAllocationError> {
152 budget.include_capacity::<J2kClassicCodeBlockPayload>(payload_capacity)?;
153 budget.include_capacity::<J2kCodestreamRange>(range_capacity)
154}
155
156fn retained_components(
157 components: &[J2kDirectGrayscalePlan],
158 capacity: usize,
159) -> Result<usize, DecodePlanAllocationError> {
160 let mut budget = Budget::default();
161 include_components(&mut budget, components, capacity)?;
162 Ok(budget.bytes)
163}
164
165fn include_components(
166 budget: &mut Budget,
167 components: &[J2kDirectGrayscalePlan],
168 capacity: usize,
169) -> Result<(), DecodePlanAllocationError> {
170 budget.include_capacity::<J2kDirectGrayscalePlan>(capacity)?;
171 for component in components {
172 include_grayscale(budget, component)?;
173 }
174 Ok(())
175}
176
177fn include_grayscale(
178 budget: &mut Budget,
179 plan: &J2kDirectGrayscalePlan,
180) -> Result<(), DecodePlanAllocationError> {
181 budget.include_capacity::<J2kDirectGrayscaleStep>(plan.steps.capacity())?;
182 for step in &plan.steps {
183 match step {
184 J2kDirectGrayscaleStep::ClassicSubBand(subband) => {
185 budget.include_capacity::<J2kOwnedCodeBlockBatchJob>(subband.jobs.capacity())?;
186 for job in &subband.jobs {
187 budget.include_capacity::<u8>(job.data.capacity())?;
188 budget.include_capacity::<J2kCodeBlockSegment>(job.segments.capacity())?;
189 }
190 }
191 J2kDirectGrayscaleStep::HtSubBand(subband) => {
192 budget.include_capacity::<HtOwnedCodeBlockBatchJob>(subband.jobs.capacity())?;
193 for job in &subband.jobs {
194 budget.include_capacity::<u8>(job.data.capacity())?;
195 }
196 }
197 J2kDirectGrayscaleStep::Idwt(_) | J2kDirectGrayscaleStep::Store(_) => {}
198 }
199 }
200 Ok(())
201}
202
203#[cfg(test)]
204mod tests {
205 use alloc::vec::Vec;
206
207 use super::*;
208 use crate::{J2kCodeBlockStyle, J2kSubBandType};
209
210 #[test]
211 fn retained_bytes_use_nested_vector_capacities() {
212 let mut jobs = Vec::new();
213 jobs.try_reserve_exact(3).expect("job capacity");
214 jobs.push(J2kOwnedCodeBlockBatchJob {
215 output_x: 0,
216 output_y: 0,
217 data: {
218 let mut data = Vec::new();
219 data.try_reserve_exact(11).expect("data capacity");
220 data
221 },
222 segments: {
223 let mut segments = Vec::new();
224 segments.try_reserve_exact(5).expect("segment capacity");
225 segments
226 },
227 width: 1,
228 height: 1,
229 output_stride: 1,
230 missing_bit_planes: 0,
231 number_of_coding_passes: 0,
232 total_bitplanes: 0,
233 roi_shift: 0,
234 sub_band_type: J2kSubBandType::LowLow,
235 style: J2kCodeBlockStyle {
236 selective_arithmetic_coding_bypass: false,
237 reset_context_probabilities: false,
238 termination_on_each_pass: false,
239 vertically_causal_context: false,
240 segmentation_symbols: false,
241 },
242 strict: false,
243 dequantization_step: 1.0,
244 });
245 let mut steps = Vec::new();
246 steps.try_reserve_exact(4).expect("step capacity");
247 steps.push(J2kDirectGrayscaleStep::ClassicSubBand(
248 super::super::J2kOwnedSubBandPlan {
249 band_id: 0,
250 rect: super::super::J2kRect {
251 x0: 0,
252 y0: 0,
253 x1: 1,
254 y1: 1,
255 },
256 width: 1,
257 height: 1,
258 irreversible_midpoint: false,
259 jobs,
260 },
261 ));
262 let plan = J2kDirectGrayscalePlan {
263 dimensions: (1, 1),
264 bit_depth: 8,
265 steps,
266 };
267
268 assert_eq!(
269 plan.retained_allocation_bytes().expect("retained bytes"),
270 4 * size_of::<J2kDirectGrayscaleStep>()
271 + 3 * size_of::<J2kOwnedCodeBlockBatchJob>()
272 + 11
273 + 5 * size_of::<J2kCodeBlockSegment>()
274 );
275 }
276
277 #[test]
278 fn direct_plan_owner_types_remain_move_only_values() {
279 fn assert_debug<T: core::fmt::Debug>() {}
280
281 assert_debug::<J2kDirectColorPlan>();
282 assert_debug::<J2kDirectGrayscalePlan>();
283 assert_debug::<super::super::J2kOwnedSubBandPlan>();
284 assert_debug::<super::super::HtOwnedSubBandPlan>();
285 assert_debug::<J2kOwnedCodeBlockBatchJob>();
286 assert_debug::<HtOwnedCodeBlockBatchJob>();
287 }
288}