feat(rust): add common primitive compatibility layer
Introduce the Rust static library and move the shared byte, bitstream, CPU, error, xxHash, debug, and public-common implementations into it. Thin C shims preserve the existing header-driven C build while original tests link the Rust archive. This establishes the ABI-safe foundation for later codec and CLI ports; entropy coding, runtime support, codecs, dictionaries, and the CLI remain C. The top-down migration map documents that boundary and its validation path. Test Plan: - cargo fmt --check - cargo test --all-targets - cargo clippy --all-targets -- -D warnings - cargo build --release Refs: rust/README.md
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
#![allow(non_snake_case)]
|
||||
use std::os::raw::c_char;
|
||||
|
||||
/// Matches `ZSTD_ErrorCode` / `ERR_enum` from the C API.
|
||||
#[repr(i32)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum ZstdErrorCode {
|
||||
NoError = 0,
|
||||
Generic = 1,
|
||||
PrefixUnknown = 10,
|
||||
VersionUnsupported = 12,
|
||||
FrameParameterUnsupported = 14,
|
||||
FrameParameterWindowTooLarge = 16,
|
||||
CorruptionDetected = 20,
|
||||
ChecksumWrong = 22,
|
||||
LiteralsHeaderWrong = 24,
|
||||
DictionaryCorrupted = 30,
|
||||
DictionaryWrong = 32,
|
||||
DictionaryCreationFailed = 34,
|
||||
ParameterUnsupported = 40,
|
||||
ParameterCombinationUnsupported = 41,
|
||||
ParameterOutOfBound = 42,
|
||||
TableLogTooLarge = 44,
|
||||
MaxSymbolValueTooLarge = 46,
|
||||
MaxSymbolValueTooSmall = 48,
|
||||
CannotProduceUncompressedBlock = 49,
|
||||
StabilityConditionNotRespected = 50,
|
||||
StageWrong = 60,
|
||||
InitMissing = 62,
|
||||
MemoryAllocation = 64,
|
||||
WorkSpaceTooSmall = 66,
|
||||
DstSizeTooSmall = 70,
|
||||
SrcSizeWrong = 72,
|
||||
DstBufferNull = 74,
|
||||
NoForwardProgressDestFull = 80,
|
||||
NoForwardProgressInputEmpty = 82,
|
||||
FrameIndexTooLarge = 100,
|
||||
SeekableIO = 102,
|
||||
DstBufferWrong = 104,
|
||||
SrcBufferWrong = 105,
|
||||
SequenceProducerFailed = 106,
|
||||
ExternalSequencesInvalid = 107,
|
||||
MaxCode = 120,
|
||||
}
|
||||
|
||||
/// Encode an error as a size_t-style error code: `(size_t)-(code)`.
|
||||
#[inline]
|
||||
pub fn ERROR(code: ZstdErrorCode) -> usize {
|
||||
(-(code as i32)) as usize
|
||||
}
|
||||
|
||||
/// Returns true if `code` is an error (same as C `ERR_isError`).
|
||||
#[inline]
|
||||
pub fn ERR_isError(code: usize) -> bool {
|
||||
code > ERROR(ZstdErrorCode::MaxCode)
|
||||
}
|
||||
|
||||
/// Extract the numeric `ZSTD_ErrorCode` from a size_t error value.
|
||||
///
|
||||
/// C permits error-shaped values which aren't named enum variants, and
|
||||
/// `ERR_getErrorCode()` preserves those numeric values. Returning the ABI's
|
||||
/// integer representation avoids constructing an invalid Rust enum.
|
||||
#[inline]
|
||||
pub fn ERR_getErrorCode(code: usize) -> i32 {
|
||||
if !ERR_isError(code) {
|
||||
return ZstdErrorCode::NoError as i32;
|
||||
}
|
||||
0usize.wrapping_sub(code) as i32
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ERR_getErrorName(code: usize) -> *const c_char {
|
||||
ERR_getErrorString(ERR_getErrorCode(code))
|
||||
}
|
||||
|
||||
/// C ABI: `ERR_getErrorString` (error_private.c).
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ERR_getErrorString(code: i32) -> *const c_char {
|
||||
match code {
|
||||
0 => c"No error detected".as_ptr(),
|
||||
1 => c"Error (generic)".as_ptr(),
|
||||
10 => c"Unknown frame descriptor".as_ptr(),
|
||||
12 => c"Version not supported".as_ptr(),
|
||||
14 => c"Unsupported frame parameter".as_ptr(),
|
||||
16 => c"Frame requires too much memory for decoding".as_ptr(),
|
||||
20 => c"Data corruption detected".as_ptr(),
|
||||
22 => c"Restored data doesn't match checksum".as_ptr(),
|
||||
24 => c"Header of Literals' block doesn't respect format specification".as_ptr(),
|
||||
30 => c"Dictionary is corrupted".as_ptr(),
|
||||
32 => c"Dictionary mismatch".as_ptr(),
|
||||
34 => c"Cannot create Dictionary from provided samples".as_ptr(),
|
||||
40 => c"Unsupported parameter".as_ptr(),
|
||||
41 => c"Unsupported combination of parameters".as_ptr(),
|
||||
42 => c"Parameter is out of bound".as_ptr(),
|
||||
44 => c"tableLog requires too much memory : unsupported".as_ptr(),
|
||||
46 => c"Unsupported max Symbol Value : too large".as_ptr(),
|
||||
48 => c"Specified maxSymbolValue is too small".as_ptr(),
|
||||
49 => c"This mode cannot generate an uncompressed block".as_ptr(),
|
||||
50 => c"pledged buffer stability condition is not respected".as_ptr(),
|
||||
60 => c"Operation not authorized at current processing stage".as_ptr(),
|
||||
62 => c"Context should be init first".as_ptr(),
|
||||
64 => c"Allocation error : not enough memory".as_ptr(),
|
||||
66 => c"workSpace buffer is not large enough".as_ptr(),
|
||||
70 => c"Destination buffer is too small".as_ptr(),
|
||||
72 => c"Src size is incorrect".as_ptr(),
|
||||
74 => c"Operation on NULL destination buffer".as_ptr(),
|
||||
80 => c"Operation made no progress over multiple calls, due to output buffer being full"
|
||||
.as_ptr(),
|
||||
82 => c"Operation made no progress over multiple calls, due to input being empty".as_ptr(),
|
||||
100 => c"Frame index is too large".as_ptr(),
|
||||
102 => c"An I/O error occurred when reading/seeking".as_ptr(),
|
||||
104 => c"Destination buffer is wrong".as_ptr(),
|
||||
105 => c"Source buffer is wrong".as_ptr(),
|
||||
106 => c"Block-level external sequence producer returned an error code".as_ptr(),
|
||||
107 => c"External sequences are not valid".as_ptr(),
|
||||
_ => c"Unspecified error code".as_ptr(),
|
||||
}
|
||||
}
|
||||
|
||||
/// C ABI: public `ZSTD_getErrorString` from zstd_errors.h.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ZSTD_getErrorString(code: i32) -> *const c_char {
|
||||
ERR_getErrorString(code)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::ffi::CStr;
|
||||
|
||||
#[test]
|
||||
fn size_t_error_encoding_matches_c() {
|
||||
let error = ERROR(ZstdErrorCode::DstSizeTooSmall);
|
||||
assert!(ERR_isError(error));
|
||||
assert_eq!(ERR_getErrorCode(error), 70);
|
||||
assert!(!ERR_isError(0));
|
||||
assert_eq!(ERR_getErrorCode(usize::MAX - 1), 2);
|
||||
assert_eq!(ERR_getErrorCode(ERROR(ZstdErrorCode::MaxCode)), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_names_match_the_public_api() {
|
||||
let name = unsafe { CStr::from_ptr(ERR_getErrorName(ERROR(ZstdErrorCode::ChecksumWrong))) };
|
||||
assert_eq!(name.to_bytes(), b"Restored data doesn't match checksum");
|
||||
|
||||
let unknown = unsafe { CStr::from_ptr(ERR_getErrorString(2)) };
|
||||
assert_eq!(unknown.to_bytes(), b"Unspecified error code");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user