1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub enum ErrorCode {
16 NoError,
17 Generic,
18 PrefixUnknown,
19 VersionUnsupported,
20 FrameParameterUnsupported,
21 FrameParameterWindowTooLarge,
22 CorruptionDetected,
23 ChecksumWrong,
24 LiteralsHeaderWrong,
25 DictionaryCorrupted,
26 DictionaryWrong,
27 DictionaryCreationFailed,
28 ParameterUnsupported,
29 ParameterCombinationUnsupported,
30 ParameterOutOfBound,
31 TableLogTooLarge,
32 MaxSymbolValueTooLarge,
33 MaxSymbolValueTooSmall,
34 CannotProduceUncompressedBlock,
35 StabilityConditionNotRespected,
36 StageWrong,
37 InitMissing,
38 MemoryAllocation,
39 WorkSpaceTooSmall,
40 DstSizeTooSmall,
41 SrcSizeWrong,
42 DstBufferNull,
43 NoForwardProgressDestFull,
44 NoForwardProgressInputEmpty,
45 FrameIndexTooLarge,
46 SeekableIO,
47 DstBufferWrong,
48 SrcBufferWrong,
49 SequenceProducerFailed,
50 ExternalSequencesInvalid,
51 MaxCode,
52 Unknown(i32),
53}
54
55impl ErrorCode {
56 pub fn from_raw(code: i32) -> Self {
58 match code {
59 0 => Self::NoError,
60 1 => Self::Generic,
61 10 => Self::PrefixUnknown,
62 12 => Self::VersionUnsupported,
63 14 => Self::FrameParameterUnsupported,
64 16 => Self::FrameParameterWindowTooLarge,
65 20 => Self::CorruptionDetected,
66 22 => Self::ChecksumWrong,
67 24 => Self::LiteralsHeaderWrong,
68 30 => Self::DictionaryCorrupted,
69 32 => Self::DictionaryWrong,
70 34 => Self::DictionaryCreationFailed,
71 40 => Self::ParameterUnsupported,
72 41 => Self::ParameterCombinationUnsupported,
73 42 => Self::ParameterOutOfBound,
74 44 => Self::TableLogTooLarge,
75 46 => Self::MaxSymbolValueTooLarge,
76 48 => Self::MaxSymbolValueTooSmall,
77 49 => Self::CannotProduceUncompressedBlock,
78 50 => Self::StabilityConditionNotRespected,
79 60 => Self::StageWrong,
80 62 => Self::InitMissing,
81 64 => Self::MemoryAllocation,
82 66 => Self::WorkSpaceTooSmall,
83 70 => Self::DstSizeTooSmall,
84 72 => Self::SrcSizeWrong,
85 74 => Self::DstBufferNull,
86 80 => Self::NoForwardProgressDestFull,
87 82 => Self::NoForwardProgressInputEmpty,
88 100 => Self::FrameIndexTooLarge,
89 102 => Self::SeekableIO,
90 104 => Self::DstBufferWrong,
91 105 => Self::SrcBufferWrong,
92 106 => Self::SequenceProducerFailed,
93 107 => Self::ExternalSequencesInvalid,
94 120 => Self::MaxCode,
95 raw => Self::Unknown(raw),
96 }
97 }
98
99 pub fn as_i32(self) -> i32 {
100 match self {
101 Self::NoError => 0,
102 Self::Generic => 1,
103 Self::PrefixUnknown => 10,
104 Self::VersionUnsupported => 12,
105 Self::FrameParameterUnsupported => 14,
106 Self::FrameParameterWindowTooLarge => 16,
107 Self::CorruptionDetected => 20,
108 Self::ChecksumWrong => 22,
109 Self::LiteralsHeaderWrong => 24,
110 Self::DictionaryCorrupted => 30,
111 Self::DictionaryWrong => 32,
112 Self::DictionaryCreationFailed => 34,
113 Self::ParameterUnsupported => 40,
114 Self::ParameterCombinationUnsupported => 41,
115 Self::ParameterOutOfBound => 42,
116 Self::TableLogTooLarge => 44,
117 Self::MaxSymbolValueTooLarge => 46,
118 Self::MaxSymbolValueTooSmall => 48,
119 Self::CannotProduceUncompressedBlock => 49,
120 Self::StabilityConditionNotRespected => 50,
121 Self::StageWrong => 60,
122 Self::InitMissing => 62,
123 Self::MemoryAllocation => 64,
124 Self::WorkSpaceTooSmall => 66,
125 Self::DstSizeTooSmall => 70,
126 Self::SrcSizeWrong => 72,
127 Self::DstBufferNull => 74,
128 Self::NoForwardProgressDestFull => 80,
129 Self::NoForwardProgressInputEmpty => 82,
130 Self::FrameIndexTooLarge => 100,
131 Self::SeekableIO => 102,
132 Self::DstBufferWrong => 104,
133 Self::SrcBufferWrong => 105,
134 Self::SequenceProducerFailed => 106,
135 Self::ExternalSequencesInvalid => 107,
136 Self::MaxCode => 120,
137 Self::Unknown(raw) => raw,
138 }
139 }
140}
141
142#[derive(Debug, Clone, Copy, PartialEq, Eq)]
145pub struct ZstdError(pub ErrorCode);
146
147impl core::fmt::Display for ZstdError {
148 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
149 f.write_str(ERR_getErrorString(self.0))
150 }
151}
152
153#[cfg(feature = "std")]
154impl std::error::Error for ZstdError {}
155
156#[inline]
160pub fn ERROR(code: ErrorCode) -> usize {
161 (0usize).wrapping_sub(code.as_i32() as usize)
162}
163
164#[inline]
167pub fn ERR_isError(code: usize) -> bool {
168 code > ERROR(ErrorCode::MaxCode)
169}
170
171#[inline]
174pub fn ERR_getErrorCode(code: usize) -> ErrorCode {
175 if !ERR_isError(code) {
176 return ErrorCode::NoError;
177 }
178 let raw = 0i32.wrapping_sub(code as i32);
180 ErrorCode::from_raw(raw)
181}
182
183#[inline]
186pub fn ERR_getErrorName(code: usize) -> &'static str {
187 ERR_getErrorString(ERR_getErrorCode(code))
188}
189
190pub fn ERR_getErrorString(code: ErrorCode) -> &'static str {
194 match code {
195 ErrorCode::NoError => "No error detected",
196 ErrorCode::Generic => "Error (generic)",
197 ErrorCode::PrefixUnknown => "Unknown frame descriptor",
198 ErrorCode::VersionUnsupported => "Version not supported",
199 ErrorCode::FrameParameterUnsupported => "Unsupported frame parameter",
200 ErrorCode::FrameParameterWindowTooLarge => "Frame requires too much memory for decoding",
201 ErrorCode::CorruptionDetected => "Data corruption detected",
202 ErrorCode::ChecksumWrong => "Restored data doesn't match checksum",
203 ErrorCode::LiteralsHeaderWrong => {
204 "Header of Literals' block doesn't respect format specification"
205 }
206 ErrorCode::ParameterUnsupported => "Unsupported parameter",
207 ErrorCode::ParameterCombinationUnsupported => "Unsupported combination of parameters",
208 ErrorCode::ParameterOutOfBound => "Parameter is out of bound",
209 ErrorCode::InitMissing => "Context should be init first",
210 ErrorCode::MemoryAllocation => "Allocation error : not enough memory",
211 ErrorCode::WorkSpaceTooSmall => "workSpace buffer is not large enough",
212 ErrorCode::StageWrong => "Operation not authorized at current processing stage",
213 ErrorCode::TableLogTooLarge => "tableLog requires too much memory : unsupported",
214 ErrorCode::MaxSymbolValueTooLarge => "Unsupported max Symbol Value : too large",
215 ErrorCode::MaxSymbolValueTooSmall => "Specified maxSymbolValue is too small",
216 ErrorCode::CannotProduceUncompressedBlock => {
217 "This mode cannot generate an uncompressed block"
218 }
219 ErrorCode::StabilityConditionNotRespected => {
220 "pledged buffer stability condition is not respected"
221 }
222 ErrorCode::DictionaryCorrupted => "Dictionary is corrupted",
223 ErrorCode::DictionaryWrong => "Dictionary mismatch",
224 ErrorCode::DictionaryCreationFailed => "Cannot create Dictionary from provided samples",
225 ErrorCode::DstSizeTooSmall => "Destination buffer is too small",
226 ErrorCode::SrcSizeWrong => "Src size is incorrect",
227 ErrorCode::DstBufferNull => "Operation on NULL destination buffer",
228 ErrorCode::NoForwardProgressDestFull => {
229 "Operation made no progress over multiple calls, due to output buffer being full"
230 }
231 ErrorCode::NoForwardProgressInputEmpty => {
232 "Operation made no progress over multiple calls, due to input being empty"
233 }
234 ErrorCode::FrameIndexTooLarge => "Frame index is too large",
235 ErrorCode::SeekableIO => "An I/O error occurred when reading/seeking",
236 ErrorCode::DstBufferWrong => "Destination buffer is wrong",
237 ErrorCode::SrcBufferWrong => "Source buffer is wrong",
238 ErrorCode::SequenceProducerFailed => {
239 "Block-level external sequence producer returned an error code"
240 }
241 ErrorCode::ExternalSequencesInvalid => "External sequences are not valid",
242 _ => "Unspecified error code",
243 }
244}
245
246#[cfg(test)]
247mod tests {
248 use super::*;
249
250 const UPSTREAM_ERRORS: &[(ErrorCode, i32, &str)] = &[
251 (ErrorCode::NoError, 0, "No error detected"),
252 (ErrorCode::Generic, 1, "Error (generic)"),
253 (ErrorCode::PrefixUnknown, 10, "Unknown frame descriptor"),
254 (ErrorCode::VersionUnsupported, 12, "Version not supported"),
255 (
256 ErrorCode::FrameParameterUnsupported,
257 14,
258 "Unsupported frame parameter",
259 ),
260 (
261 ErrorCode::FrameParameterWindowTooLarge,
262 16,
263 "Frame requires too much memory for decoding",
264 ),
265 (
266 ErrorCode::CorruptionDetected,
267 20,
268 "Data corruption detected",
269 ),
270 (
271 ErrorCode::ChecksumWrong,
272 22,
273 "Restored data doesn't match checksum",
274 ),
275 (
276 ErrorCode::LiteralsHeaderWrong,
277 24,
278 "Header of Literals' block doesn't respect format specification",
279 ),
280 (
281 ErrorCode::DictionaryCorrupted,
282 30,
283 "Dictionary is corrupted",
284 ),
285 (ErrorCode::DictionaryWrong, 32, "Dictionary mismatch"),
286 (
287 ErrorCode::DictionaryCreationFailed,
288 34,
289 "Cannot create Dictionary from provided samples",
290 ),
291 (ErrorCode::ParameterUnsupported, 40, "Unsupported parameter"),
292 (
293 ErrorCode::ParameterCombinationUnsupported,
294 41,
295 "Unsupported combination of parameters",
296 ),
297 (
298 ErrorCode::ParameterOutOfBound,
299 42,
300 "Parameter is out of bound",
301 ),
302 (
303 ErrorCode::TableLogTooLarge,
304 44,
305 "tableLog requires too much memory : unsupported",
306 ),
307 (
308 ErrorCode::MaxSymbolValueTooLarge,
309 46,
310 "Unsupported max Symbol Value : too large",
311 ),
312 (
313 ErrorCode::MaxSymbolValueTooSmall,
314 48,
315 "Specified maxSymbolValue is too small",
316 ),
317 (
318 ErrorCode::CannotProduceUncompressedBlock,
319 49,
320 "This mode cannot generate an uncompressed block",
321 ),
322 (
323 ErrorCode::StabilityConditionNotRespected,
324 50,
325 "pledged buffer stability condition is not respected",
326 ),
327 (
328 ErrorCode::StageWrong,
329 60,
330 "Operation not authorized at current processing stage",
331 ),
332 (ErrorCode::InitMissing, 62, "Context should be init first"),
333 (
334 ErrorCode::MemoryAllocation,
335 64,
336 "Allocation error : not enough memory",
337 ),
338 (
339 ErrorCode::WorkSpaceTooSmall,
340 66,
341 "workSpace buffer is not large enough",
342 ),
343 (
344 ErrorCode::DstSizeTooSmall,
345 70,
346 "Destination buffer is too small",
347 ),
348 (ErrorCode::SrcSizeWrong, 72, "Src size is incorrect"),
349 (
350 ErrorCode::DstBufferNull,
351 74,
352 "Operation on NULL destination buffer",
353 ),
354 (
355 ErrorCode::NoForwardProgressDestFull,
356 80,
357 "Operation made no progress over multiple calls, due to output buffer being full",
358 ),
359 (
360 ErrorCode::NoForwardProgressInputEmpty,
361 82,
362 "Operation made no progress over multiple calls, due to input being empty",
363 ),
364 (
365 ErrorCode::FrameIndexTooLarge,
366 100,
367 "Frame index is too large",
368 ),
369 (
370 ErrorCode::SeekableIO,
371 102,
372 "An I/O error occurred when reading/seeking",
373 ),
374 (
375 ErrorCode::DstBufferWrong,
376 104,
377 "Destination buffer is wrong",
378 ),
379 (ErrorCode::SrcBufferWrong, 105, "Source buffer is wrong"),
380 (
381 ErrorCode::SequenceProducerFailed,
382 106,
383 "Block-level external sequence producer returned an error code",
384 ),
385 (
386 ErrorCode::ExternalSequencesInvalid,
387 107,
388 "External sequences are not valid",
389 ),
390 (ErrorCode::MaxCode, 120, "Unspecified error code"),
391 ];
392
393 #[test]
394 fn error_encoding_roundtrip() {
395 let code = ErrorCode::CorruptionDetected;
396 let enc = ERROR(code);
397 assert!(ERR_isError(enc));
398 assert_eq!(ERR_getErrorCode(enc), code);
399 }
400
401 #[test]
402 fn zero_and_small_values_are_not_errors() {
403 assert!(!ERR_isError(0));
404 assert!(!ERR_isError(1));
405 assert!(!ERR_isError(1 << 20));
406 }
407
408 #[test]
409 fn boundary_maxcode_is_not_error() {
410 assert!(!ERR_isError(ERROR(ErrorCode::MaxCode)));
412 assert!(ERR_isError(ERROR(ErrorCode::CorruptionDetected)));
415 }
416
417 #[test]
418 fn error_strings_match_upstream_literals() {
419 for &(code, _raw, name) in UPSTREAM_ERRORS {
420 assert_eq!(ERR_getErrorString(code), name, "{code:?}");
421 }
422 }
423
424 #[test]
425 fn error_name_via_size_t() {
426 let enc = ERROR(ErrorCode::ChecksumWrong);
427 assert_eq!(
428 ERR_getErrorName(enc),
429 "Restored data doesn't match checksum"
430 );
431 }
432
433 #[test]
434 fn from_raw_preserves_extreme_and_negative_inputs() {
435 assert_eq!(ErrorCode::from_raw(i32::MIN).as_i32(), i32::MIN);
438 assert_eq!(ErrorCode::from_raw(-1).as_i32(), -1);
439 assert_eq!(ErrorCode::from_raw(-42).as_i32(), -42);
440 assert_eq!(ErrorCode::from_raw(i32::MAX).as_i32(), i32::MAX);
441 assert_eq!(ErrorCode::from_raw(999_999).as_i32(), 999_999);
442 assert_eq!(ErrorCode::from_raw(22), ErrorCode::ChecksumWrong);
444 }
445
446 #[test]
447 fn getErrorName_on_non_error_returns_no_error_string() {
448 assert_eq!(ERR_getErrorName(0), "No error detected");
454 assert_eq!(ERR_getErrorName(100), "No error detected");
455 assert_eq!(ERR_getErrorName(1_000_000), "No error detected");
456 assert_eq!(ERR_getErrorCode(0), ErrorCode::NoError);
458 assert_eq!(ERR_getErrorCode(1_000_000), ErrorCode::NoError);
459 }
460
461 #[test]
462 fn from_raw_roundtrips_every_named_variant() {
463 for &(code, raw, _name) in UPSTREAM_ERRORS {
466 assert_eq!(code.as_i32(), raw, "raw value drifted for {code:?}");
467 assert_eq!(
468 ErrorCode::from_raw(raw),
469 code,
470 "from_raw({raw}) did not round-trip for {code:?}"
471 );
472 }
473 assert_eq!(ErrorCode::from_raw(99_999).as_i32(), 99_999);
475 }
476
477 #[test]
478 fn unknown_encoded_error_name_matches_upstream_default() {
479 let encoded_unknown_error = (0usize).wrapping_sub(2);
480 assert!(ERR_isError(encoded_unknown_error));
481 assert_eq!(ERR_getErrorCode(encoded_unknown_error).as_i32(), 2);
482 assert_eq!(
483 ERR_getErrorString(ErrorCode::Unknown(2)),
484 "Unspecified error code"
485 );
486 assert_eq!(
487 ERR_getErrorName(encoded_unknown_error),
488 "Unspecified error code"
489 );
490 }
491
492 #[test]
493 fn size_t_error_threshold_matches_upstream() {
494 assert!(ERR_isError((0usize).wrapping_sub(1)));
495 assert!(ERR_isError((0usize).wrapping_sub(119)));
496 assert!(!ERR_isError(ERROR(ErrorCode::MaxCode)));
497 assert!(!ERR_isError((0usize).wrapping_sub(121)));
498 }
499}