1use thiserror::Error;
11
12use super::capacity_error::CapacityError;
13use super::transcode_contract_error::TranscodeContractError;
14use crate::Codec;
15use crate::codec::assert_unit_bounds;
16
17#[derive(Clone, Copy, Debug, Eq, Error, Hash, PartialEq)]
24#[non_exhaustive]
25pub enum TranscodeFailure {
26 #[error("invalid input index {index} for input length {input_len}")]
28 InvalidInputIndex {
29 index: usize,
31 input_len: usize,
33 },
34
35 #[error("invalid output index {index} for output length {output_len}")]
37 InvalidOutputIndex {
38 index: usize,
40 output_len: usize,
42 },
43
44 #[error("invalid output range at index {output_index} with length {range_len} for output length {output_len}")]
46 InvalidOutputRange {
47 output_index: usize,
49 range_len: usize,
51 output_len: usize,
53 },
54
55 #[error("insufficient output at index {output_index}: required {required} units, available {available}")]
57 InsufficientOutput {
58 output_index: usize,
60 required: usize,
62 available: usize,
64 },
65
66 #[error("output length arithmetic overflow")]
68 OutputLengthOverflow,
69
70 #[error("output allocation failed")]
72 AllocationFailed,
73
74 #[error(
76 "incomplete input at index {input_index}: at least {required} units required to retry, {available} available"
77 )]
78 IncompleteInput {
79 input_index: usize,
81 required: usize,
84 available: usize,
86 },
87
88 #[error("invalid transcoder progress: {source}")]
90 InvalidProgress {
91 #[source]
93 source: TranscodeContractError,
94 },
95
96 #[error("trailing input after value: consumed {consumed} units, remaining {remaining}")]
98 TrailingInput {
99 consumed: usize,
101 remaining: usize,
103 },
104
105 #[error(
107 "strict single-value decoding does not support lifecycle output: reset bound {reset_bound}, finish bound {finish_bound}"
108 )]
109 UnsupportedDecodeLifecycleOutput {
110 reset_bound: usize,
112 finish_bound: usize,
114 },
115
116 #[error("transcode called after finish without an intervening reset")]
118 TranscodeAfterFinish,
119
120 #[error("transcode called before reset")]
122 TranscodeBeforeReset,
123
124 #[error("finish called twice without an intervening reset")]
126 FinishAfterFinish,
127
128 #[error("finish called before reset")]
130 FinishBeforeReset,
131
132 #[error("transcoder lifecycle is poisoned; a successful reset is required")]
136 LifecyclePoisoned,
137}
138
139impl TranscodeFailure {
140 #[inline(always)]
142 #[must_use]
143 pub const fn invalid_input_index(index: usize, len: usize) -> Self {
144 Self::InvalidInputIndex { index, input_len: len }
145 }
146
147 #[inline(always)]
149 #[must_use]
150 pub const fn invalid_output_index(index: usize, len: usize) -> Self {
151 Self::InvalidOutputIndex { index, output_len: len }
152 }
153
154 #[inline(always)]
156 #[must_use]
157 pub const fn invalid_output_range(output_index: usize, range_len: usize, output_len: usize) -> Self {
158 Self::InvalidOutputRange {
159 output_index,
160 range_len,
161 output_len,
162 }
163 }
164
165 #[inline(always)]
167 #[must_use]
168 pub const fn insufficient_output(output_index: usize, required: usize, available: usize) -> Self {
169 Self::InsufficientOutput {
170 output_index,
171 required,
172 available,
173 }
174 }
175
176 #[inline(always)]
178 #[must_use]
179 pub const fn output_length_overflow() -> Self {
180 Self::OutputLengthOverflow
181 }
182
183 #[inline(always)]
185 #[must_use]
186 pub const fn allocation_failed() -> Self {
187 Self::AllocationFailed
188 }
189
190 #[inline(always)]
192 #[must_use]
193 pub const fn incomplete_input(input_index: usize, required: usize, available: usize) -> Self {
194 Self::IncompleteInput {
195 input_index,
196 required,
197 available,
198 }
199 }
200
201 #[inline(always)]
203 #[must_use]
204 pub const fn invalid_progress(source: TranscodeContractError) -> Self {
205 Self::InvalidProgress { source }
206 }
207
208 #[inline(always)]
210 #[must_use]
211 pub const fn trailing_input(consumed: usize, remaining: usize) -> Self {
212 Self::TrailingInput { consumed, remaining }
213 }
214
215 #[inline(always)]
217 #[must_use]
218 pub const fn unsupported_decode_lifecycle_output(reset_bound: usize, finish_bound: usize) -> Self {
219 Self::UnsupportedDecodeLifecycleOutput {
220 reset_bound,
221 finish_bound,
222 }
223 }
224
225 #[inline(always)]
240 pub(crate) fn ensure_no_decode_lifecycle_output<C>() -> Result<(), Self>
241 where
242 C: Codec,
243 {
244 assert_unit_bounds::<C>();
245 if C::MAX_DECODE_RESET_VALUES != 0 || C::MAX_DECODE_FINISH_VALUES != 0 {
246 return Err(Self::unsupported_decode_lifecycle_output(
247 C::MAX_DECODE_RESET_VALUES,
248 C::MAX_DECODE_FINISH_VALUES,
249 ));
250 }
251 Ok(())
252 }
253
254 #[inline]
256 pub fn ensure_input_index(input_len: usize, input_index: usize) -> Result<(), Self> {
257 if input_index > input_len {
258 return Err(Self::invalid_input_index(input_index, input_len));
259 }
260 Ok(())
261 }
262
263 #[inline]
265 pub fn ensure_min_input(input_len: usize, input_index: usize, min_required: usize) -> Result<(), Self> {
266 Self::ensure_input_index(input_len, input_index)?;
267 let available = input_len - input_index;
268 if available < min_required {
269 return Err(Self::incomplete_input(input_index, min_required, available));
270 }
271 Ok(())
272 }
273
274 #[inline]
291 pub fn ensure_no_trailing_input(consumed: usize, input_len: usize) -> Result<(), Self> {
292 Self::ensure_input_index(input_len, consumed)?;
293 let remaining = input_len - consumed;
294 if remaining != 0 {
295 return Err(Self::trailing_input(consumed, remaining));
296 }
297 Ok(())
298 }
299
300 #[inline]
302 pub fn ensure_output_index(output_len: usize, output_index: usize) -> Result<(), Self> {
303 if output_index > output_len {
304 return Err(Self::invalid_output_index(output_index, output_len));
305 }
306 Ok(())
307 }
308
309 #[inline]
311 pub fn ensure_transcode_indices(
312 input_len: usize,
313 input_index: usize,
314 output_len: usize,
315 output_index: usize,
316 ) -> Result<(), Self> {
317 Self::ensure_input_index(input_len, input_index)?;
318 Self::ensure_output_index(output_len, output_index)
319 }
320
321 #[inline]
323 pub fn ensure_output_capacity(output_len: usize, output_index: usize, required: usize) -> Result<(), Self> {
324 Self::ensure_output_index(output_len, output_index)?;
325 let available = output_len - output_index;
326 if available < required {
327 return Err(Self::insufficient_output(output_index, required, available));
328 }
329 Ok(())
330 }
331
332 #[inline]
334 pub fn ensure_output_range(
335 output_len: usize,
336 output_index: usize,
337 range_len: usize,
338 required: usize,
339 ) -> Result<(), Self> {
340 Self::ensure_output_index(output_len, output_index)?;
341 let available = output_len - output_index;
342 if range_len > available {
343 return Err(Self::invalid_output_range(output_index, range_len, output_len));
344 }
345 if range_len < required {
346 return Err(Self::insufficient_output(output_index, required, range_len));
347 }
348 Ok(())
349 }
350}
351
352impl From<CapacityError> for TranscodeFailure {
353 #[inline(always)]
355 fn from(error: CapacityError) -> Self {
356 match error {
357 CapacityError::OutputLengthOverflow => Self::OutputLengthOverflow,
358 }
359 }
360}
361
362impl From<TranscodeContractError> for TranscodeFailure {
363 #[inline(always)]
365 fn from(error: TranscodeContractError) -> Self {
366 Self::invalid_progress(error)
367 }
368}