Skip to main content

ad_core_rs/
codec.rs

1/// Codec names for compressed NDArray data (matching C++ `NDCodecName`).
2#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3pub enum CodecName {
4    None,
5    JPEG,
6    /// Zlib (deflate) compression (C++ `NDCODEC_ZLIB`).
7    Zlib,
8    LZ4,
9    Blosc,
10    BSLZ4,
11    /// LZ4 in the HDF5 framing variant (C++ `NDCODEC_LZ4HDF5`).
12    LZ4HDF5,
13}
14
15impl CodecName {
16    /// Codec name string as published in the EPICS `CODEC` parameter and the
17    /// `NDArray.codec.name` field (matching C++ `NDCodecName`).
18    pub fn as_str(self) -> &'static str {
19        match self {
20            Self::None => "",
21            Self::JPEG => "jpeg",
22            Self::Zlib => "zlib",
23            Self::LZ4 => "lz4",
24            Self::Blosc => "blosc",
25            Self::BSLZ4 => "bslz4",
26            Self::LZ4HDF5 => "lz4hdf5",
27        }
28    }
29}
30
31/// Blosc sub-compressor names (matching C++ `NDCodecBloscCompName`).
32///
33/// Indexed by `NDCodecBloscComp_t`: `BloscLZ`, `LZ4`, `LZ4HC`, `Snappy`,
34/// `ZLIB`, `ZSTD`.
35pub const BLOSC_COMP_NAMES: [&str; 6] = ["blosclz", "lz4", "lz4hc", "snappy", "zlib", "zstd"];
36
37/// Resolve a Blosc sub-compressor index to its name, matching C++
38/// `NDCodecBloscCompName[compressor]`. Out-of-range indices return `None`.
39pub fn blosc_comp_name(compressor: i32) -> Option<&'static str> {
40    usize::try_from(compressor)
41        .ok()
42        .and_then(|i| BLOSC_COMP_NAMES.get(i).copied())
43}
44
45/// Codec information attached to an NDArray (matching C++ Codec_t).
46#[derive(Debug, Clone)]
47pub struct Codec {
48    pub name: CodecName,
49    pub compressed_size: usize,
50    pub level: i32,
51    pub shuffle: i32,
52    pub compressor: i32,
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    #[test]
60    fn test_codec_clone() {
61        let c = Codec {
62            name: CodecName::LZ4,
63            compressed_size: 1024,
64            level: 0,
65            shuffle: 0,
66            compressor: 0,
67        };
68        let c2 = c.clone();
69        assert_eq!(c2.name, CodecName::LZ4);
70        assert_eq!(c2.compressed_size, 1024);
71    }
72
73    #[test]
74    fn test_codec_name_none() {
75        assert_eq!(CodecName::None, CodecName::None);
76        assert_ne!(CodecName::None, CodecName::JPEG);
77    }
78
79    #[test]
80    fn test_codec_name_strings() {
81        // G12: name strings must match C++ NDCodecName.
82        assert_eq!(CodecName::None.as_str(), "");
83        assert_eq!(CodecName::JPEG.as_str(), "jpeg");
84        assert_eq!(CodecName::Zlib.as_str(), "zlib");
85        assert_eq!(CodecName::LZ4.as_str(), "lz4");
86        assert_eq!(CodecName::Blosc.as_str(), "blosc");
87        assert_eq!(CodecName::BSLZ4.as_str(), "bslz4");
88        assert_eq!(CodecName::LZ4HDF5.as_str(), "lz4hdf5");
89    }
90
91    #[test]
92    fn test_blosc_comp_names() {
93        // G12: Blosc sub-compressor table must match C++ NDCodecBloscCompName.
94        assert_eq!(blosc_comp_name(0), Some("blosclz"));
95        assert_eq!(blosc_comp_name(1), Some("lz4"));
96        assert_eq!(blosc_comp_name(2), Some("lz4hc"));
97        assert_eq!(blosc_comp_name(3), Some("snappy"));
98        assert_eq!(blosc_comp_name(4), Some("zlib"));
99        assert_eq!(blosc_comp_name(5), Some("zstd"));
100        assert_eq!(blosc_comp_name(6), None);
101        assert_eq!(blosc_comp_name(-1), None);
102    }
103}