1#[cfg(feature = "cuda-runtime")]
6use super::Arc;
7use super::{
8 BatchGroupInfo, BatchInfrastructureError, Error, IndexedBatchError, J2kDecodeWarning,
9 PreparedBatchGroup, Rect, Surface,
10};
11
12#[derive(Debug, thiserror::Error)]
14#[non_exhaustive]
15pub enum CudaBatchError {
16 #[error(transparent)]
18 Infrastructure(#[from] BatchInfrastructureError),
19 #[error("CUDA batch group containing source indices {source_indices:?} failed: {source}")]
23 GroupExecution {
24 source_indices: Vec<usize>,
26 #[source]
28 source: Box<Error>,
29 },
30}
31
32impl CudaBatchError {
33 #[allow(
34 clippy::disallowed_methods,
35 reason = "error construction preserves its infallible public signature while retaining affected source indices"
36 )]
37 pub(super) fn group(group: &PreparedBatchGroup, source: Error) -> Self {
38 Self::GroupExecution {
39 source_indices: group.source_indices().to_vec(),
40 source: Box::new(source),
41 }
42 }
43
44 #[cfg(feature = "cuda-runtime")]
47 #[doc(hidden)]
48 pub fn completion_is_uncertain(&self) -> bool {
49 match self {
50 Self::GroupExecution { source, .. } => source.completion_is_uncertain(),
51 Self::Infrastructure(_) => false,
52 }
53 }
54
55 #[doc(hidden)]
58 #[must_use]
59 pub fn session_is_unusable(&self) -> bool {
60 match self {
61 Self::Infrastructure(_) => true,
62 Self::GroupExecution { source, .. } => source.session_is_unusable(),
63 }
64 }
65}
66
67#[cfg(test)]
68mod classification_tests {
69 use j2k_core::BatchInfrastructureError;
70
71 use super::CudaBatchError;
72 use crate::Error;
73
74 fn group_error(source: Error) -> CudaBatchError {
75 CudaBatchError::GroupExecution {
76 source_indices: vec![3],
77 source: Box::new(source),
78 }
79 }
80
81 #[test]
82 fn cuda_batch_session_classification_is_owned_by_codec_errors() {
83 assert!(
84 CudaBatchError::Infrastructure(BatchInfrastructureError::EmptyBatchPlan)
85 .session_is_unusable()
86 );
87 assert!(group_error(Error::CudaUnavailable).session_is_unusable());
88 assert!(!group_error(Error::UnsupportedCudaRequest {
89 reason: "test contract rejection",
90 })
91 .session_is_unusable());
92 }
93}
94
95#[derive(Debug, thiserror::Error)]
101#[error("CUDA batch group containing source indices {source_indices:?} failed: {source}")]
102pub struct CudaBatchGroupError {
103 source_indices: Vec<usize>,
104 #[source]
105 source: Box<Error>,
106}
107
108impl CudaBatchGroupError {
109 #[cfg(feature = "cuda-runtime")]
110 #[allow(
111 clippy::disallowed_methods,
112 reason = "error construction preserves its infallible public signature while retaining affected source indices"
113 )]
114 pub(super) fn new(group: &PreparedBatchGroup, source: Error) -> Self {
115 Self {
116 source_indices: group.source_indices().to_vec(),
117 source: Box::new(source),
118 }
119 }
120
121 #[cfg(feature = "cuda-runtime")]
122 pub(super) fn from_parts(source_indices: Vec<usize>, source: Error) -> Self {
123 Self {
124 source_indices,
125 source: Box::new(source),
126 }
127 }
128
129 #[must_use]
131 pub fn source_indices(&self) -> &[usize] {
132 &self.source_indices
133 }
134
135 #[must_use]
137 pub fn source(&self) -> &Error {
138 &self.source
139 }
140
141 #[must_use]
143 pub fn into_parts(self) -> (Vec<usize>, Error) {
144 (self.source_indices, *self.source)
145 }
146}
147
148#[derive(Debug)]
150pub struct CudaBatchGroup {
151 pub(super) info: BatchGroupInfo,
152 pub(super) source_indices: Vec<usize>,
153 pub(super) decoded_rects: Vec<Rect>,
154 pub(super) warnings: Vec<Vec<J2kDecodeWarning>>,
155 pub(super) surfaces: Vec<Surface>,
156 #[cfg(feature = "cuda-runtime")]
157 pub(super) dense_output: CudaResidentBatchBuffer,
158}
159
160#[cfg(feature = "cuda-runtime")]
166#[derive(Debug)]
167pub struct CudaResidentBatchBuffer {
168 pub(super) buffer: Arc<j2k_cuda_runtime::CudaDeviceBuffer>,
169 pub(super) ranges: Vec<j2k_cuda_runtime::CudaDeviceBufferRange>,
170}
171
172#[cfg(feature = "cuda-runtime")]
173impl CudaResidentBatchBuffer {
174 #[must_use]
176 pub fn buffer(&self) -> &j2k_cuda_runtime::CudaDeviceBuffer {
177 &self.buffer
178 }
179
180 #[must_use]
182 pub fn ranges(&self) -> &[j2k_cuda_runtime::CudaDeviceBufferRange] {
183 &self.ranges
184 }
185}
186
187impl CudaBatchGroup {
188 #[must_use]
190 pub const fn info(&self) -> &BatchGroupInfo {
191 &self.info
192 }
193
194 #[must_use]
196 pub fn source_indices(&self) -> &[usize] {
197 &self.source_indices
198 }
199
200 #[must_use]
202 pub fn decoded_rects(&self) -> &[Rect] {
203 &self.decoded_rects
204 }
205
206 #[must_use]
208 pub fn warnings(&self) -> &[Vec<J2kDecodeWarning>] {
209 &self.warnings
210 }
211
212 #[must_use]
218 pub fn surfaces(&self) -> &[Surface] {
219 &self.surfaces
220 }
221
222 #[cfg(feature = "cuda-runtime")]
228 #[must_use]
229 pub const fn dense_output(&self) -> &CudaResidentBatchBuffer {
230 &self.dense_output
231 }
232
233 #[must_use]
235 #[expect(
236 clippy::type_complexity,
237 reason = "the tuple mirrors the group's five explicitly documented owners"
238 )]
239 #[cfg(not(feature = "cuda-runtime"))]
240 pub fn into_parts(
241 self,
242 ) -> (
243 BatchGroupInfo,
244 Vec<usize>,
245 Vec<Rect>,
246 Vec<Vec<J2kDecodeWarning>>,
247 Vec<Surface>,
248 ) {
249 (
250 self.info,
251 self.source_indices,
252 self.decoded_rects,
253 self.warnings,
254 self.surfaces,
255 )
256 }
257
258 #[cfg(feature = "cuda-runtime")]
261 #[must_use]
262 #[expect(
263 clippy::type_complexity,
264 reason = "the tuple mirrors the group's explicitly documented owners"
265 )]
266 pub fn into_parts(
267 self,
268 ) -> (
269 BatchGroupInfo,
270 Vec<usize>,
271 Vec<Rect>,
272 Vec<Vec<J2kDecodeWarning>>,
273 Vec<Surface>,
274 CudaResidentBatchBuffer,
275 ) {
276 (
277 self.info,
278 self.source_indices,
279 self.decoded_rects,
280 self.warnings,
281 self.surfaces,
282 self.dense_output,
283 )
284 }
285}
286
287#[derive(Debug)]
289pub struct CudaBatchDecodeResult {
290 pub(super) groups: Vec<CudaBatchGroup>,
291 pub(super) errors: Vec<IndexedBatchError>,
292 pub(super) group_errors: Vec<CudaBatchGroupError>,
293}
294
295impl CudaBatchDecodeResult {
296 #[must_use]
298 pub fn groups(&self) -> &[CudaBatchGroup] {
299 &self.groups
300 }
301
302 #[must_use]
304 pub fn errors(&self) -> &[IndexedBatchError] {
305 &self.errors
306 }
307
308 #[must_use]
310 pub fn group_errors(&self) -> &[CudaBatchGroupError] {
311 &self.group_errors
312 }
313
314 #[must_use]
317 pub fn into_parts(
318 self,
319 ) -> (
320 Vec<CudaBatchGroup>,
321 Vec<IndexedBatchError>,
322 Vec<CudaBatchGroupError>,
323 ) {
324 (self.groups, self.errors, self.group_errors)
325 }
326}