zstd-pure-rs 0.2.0

Pure-Rust port of the Zstandard (zstd) compression library
Documentation
//! Translation of `lib/common/zstd_common.c`.
//!
//! These are the public, C-visible ABI accessors. Upstream defines
//! `ZSTD_VERSION_MAJOR/MINOR/RELEASE` in `lib/zstd.h`; we mirror those
//! values exactly so `ZSTD_versionNumber()` returns the same integer and
//! `ZSTD_versionString()` the same characters.

use crate::common::error::{ERR_getErrorCode, ERR_getErrorName, ERR_isError, ErrorCode};

pub const ZSTD_VERSION_MAJOR: u32 = 1;
pub const ZSTD_VERSION_MINOR: u32 = 6;
pub const ZSTD_VERSION_RELEASE: u32 = 0;
pub const ZSTD_VERSION_NUMBER: u32 =
    ZSTD_VERSION_MAJOR * 100 * 100 + ZSTD_VERSION_MINOR * 100 + ZSTD_VERSION_RELEASE;
pub const ZSTD_VERSION_STRING: &str = "1.6.0";
pub const ZSTD_IS_DETERMINISTIC_BUILD: i32 = 1;

/// Port of `ZSTD_versionNumber`. Returns the upstream version integer
/// (`MAJOR*10000 + MINOR*100 + RELEASE`).
#[inline]
pub fn ZSTD_versionNumber() -> u32 {
    ZSTD_VERSION_NUMBER
}

/// Port of `ZSTD_versionString`. Returns the `"X.Y.Z"` literal.
#[inline]
pub fn ZSTD_versionString() -> &'static str {
    ZSTD_VERSION_STRING
}

/// Port of `ZSTD_isError`. Forwards to `ERR_isError`.
#[inline]
pub fn ZSTD_isError(code: usize) -> bool {
    ERR_isError(code)
}

/// Port of `ZSTD_getErrorName`. Forwards to `ERR_getErrorName`.
#[inline]
pub fn ZSTD_getErrorName(code: usize) -> &'static str {
    ERR_getErrorName(code)
}

/// Port of `ZSTD_getErrorCode`. Forwards to `ERR_getErrorCode`.
#[inline]
pub fn ZSTD_getErrorCode(code: usize) -> ErrorCode {
    ERR_getErrorCode(code)
}

/// Port of `ZSTD_getErrorString`. Returns the static message for the
/// given `ErrorCode`.
#[inline]
pub fn ZSTD_getErrorString(code: ErrorCode) -> &'static str {
    crate::common::error::ERR_getErrorString(code)
}

#[inline]
const fn deterministic_build_value(flag: i32) -> i32 {
    if flag != 0 {
        1
    } else {
        0
    }
}

/// Port of `ZSTD_isDeterministicBuild`. Upstream's default build sets
/// `ZSTD_IS_DETERMINISTIC_BUILD` unless output-affecting compile macros are
/// enabled.
#[inline]
pub const fn ZSTD_isDeterministicBuild() -> i32 {
    deterministic_build_value(ZSTD_IS_DETERMINISTIC_BUILD)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn version_number_matches_string() {
        assert_eq!(ZSTD_versionNumber(), 1_06_00);
        assert_eq!(ZSTD_versionString(), "1.6.0");
        // Also pin the individual major/minor/release triplet so
        // a bump (e.g. to 1.6.1) updates all four constants together.
        assert_eq!(ZSTD_VERSION_MAJOR, 1);
        assert_eq!(ZSTD_VERSION_MINOR, 6);
        assert_eq!(ZSTD_VERSION_RELEASE, 0);
        // And verify the formula: MAJOR*10000 + MINOR*100 + RELEASE.
        assert_eq!(
            ZSTD_VERSION_NUMBER,
            ZSTD_VERSION_MAJOR * 10_000 + ZSTD_VERSION_MINOR * 100 + ZSTD_VERSION_RELEASE
        );
    }

    #[test]
    fn errorName_differs_across_common_codes() {
        use crate::common::error::ERROR;
        // Each major ErrorCode variant should map to a distinct,
        // non-empty human-readable string.
        let codes = [
            ErrorCode::DstSizeTooSmall,
            ErrorCode::CorruptionDetected,
            ErrorCode::SrcSizeWrong,
            ErrorCode::ParameterOutOfBound,
            ErrorCode::ParameterUnsupported,
        ];
        let mut seen = std::collections::HashSet::new();
        for c in codes {
            let name = ZSTD_getErrorName(ERROR(c));
            assert!(!name.is_empty());
            assert!(seen.insert(name), "duplicate error name: {name}");
        }
    }

    #[test]
    fn errorName_roundtrip_for_common_codes() {
        use crate::common::error::ERROR;
        let e = ERROR(ErrorCode::DstSizeTooSmall);
        assert!(ZSTD_isError(e));
        let name = ZSTD_getErrorName(e);
        assert!(name.contains("Destination") || name.contains("dst") || name.contains("small"));
    }

    #[test]
    fn errorCode_roundtrip() {
        use crate::common::error::ERROR;
        let e = ERROR(ErrorCode::ParameterOutOfBound);
        assert_eq!(ZSTD_getErrorCode(e), ErrorCode::ParameterOutOfBound);
        // And ZSTD_getErrorString routes through the ErrorCode.
        let s1 = ZSTD_getErrorString(ErrorCode::ParameterOutOfBound);
        let s2 = ZSTD_getErrorName(e);
        assert_eq!(s1, s2);
    }

    #[test]
    fn non_error_results_decode_to_no_error() {
        assert_eq!(ZSTD_getErrorCode(0), ErrorCode::NoError);
        assert_eq!(
            ZSTD_getErrorName(0),
            ZSTD_getErrorString(ErrorCode::NoError)
        );
    }

    #[test]
    fn isError_distinguishes_code_from_size() {
        // Small successful sizes must NOT be treated as errors.
        assert!(!ZSTD_isError(0));
        assert!(!ZSTD_isError(100));
        assert!(!ZSTD_isError(1_000_000));
        assert!(!ZSTD_isError(crate::common::error::ERROR(
            ErrorCode::MaxCode
        )));
        assert!(ZSTD_isError(
            crate::common::error::ERROR(ErrorCode::MaxCode) + 1
        ));
    }

    #[test]
    fn deterministic_build_matches_upstream_default() {
        assert_eq!(ZSTD_IS_DETERMINISTIC_BUILD, 1);
        assert_eq!(ZSTD_isDeterministicBuild(), 1);
    }

    #[test]
    fn deterministic_build_normalizes_macro_value() {
        assert_eq!(deterministic_build_value(0), 0);
        assert_eq!(deterministic_build_value(1), 1);
        assert_eq!(deterministic_build_value(42), 1);
    }
}