rust_aec/lib.rs
1//! `rust-aec` is a pure Rust decoder for **CCSDS 121.0-B-3 Adaptive Entropy Coding (AEC)**.
2//!
3//! Primary goal: support **GRIB2 Data Representation Template 5.0 = 42 (CCSDS/AEC)** without
4//! requiring native `libaec`.
5//!
6//! # Quick start
7//!
8//! ```
9//! use rust_aec::{decode, flags_from_grib2_ccsds_flags, AecParams};
10//!
11//! // In a real GRIB2 pipeline, `payload` is Section 7 and `num_points` comes from Section 5.
12//! // This snippet focuses on API shape and compiles without external files.
13//! let payload: Vec<u8> = Vec::new();
14//! let num_points: usize = 0;
15//!
16//! let params = AecParams::new(12, 32, 128, flags_from_grib2_ccsds_flags(0x0e));
17//! let decoded = decode(&payload, params, num_points);
18//! assert!(decoded.is_ok());
19//! ```
20
21pub mod bitreader;
22mod decoder;
23pub mod error;
24pub mod params;
25
26pub use crate::error::AecError;
27pub use crate::params::{AecFlags, AecParams};
28
29pub use crate::decoder::{DecodeStatus, Decoder, Flush};
30
31/// Decode an AEC bitstream into packed sample bytes.
32///
33/// - `input`: CCSDS/AEC payload bitstream.
34/// - `params`: bit width, block size, RSI, and flags.
35/// - `output_samples`: number of samples expected in the output.
36///
37/// Returns a `Vec<u8>` of length `output_samples * bytes_per_sample`, where
38/// `bytes_per_sample = ceil(bits_per_sample / 8)`.
39///
40/// Note: When `AecFlags::MSB` is set, samples are written big-endian (MSB-first)
41/// per sample; otherwise little-endian.
42pub fn decode(input: &[u8], params: AecParams, output_samples: usize) -> Result<Vec<u8>, AecError> {
43 decoder::decode(input, params, output_samples)
44}
45
46/// Decode an AEC bitstream into a caller-provided output buffer.
47///
48/// This is useful when you want to reuse an allocation (e.g. decode many tiles/messages)
49/// without repeatedly allocating a `Vec<u8>`.
50///
51/// The `output` buffer length must be exactly `output_samples * bytes_per_sample`, where
52/// `bytes_per_sample = ceil(bits_per_sample / 8)` (subject to `AecFlags::DATA_3BYTE` rules).
53pub fn decode_into(
54 input: &[u8],
55 params: AecParams,
56 output_samples: usize,
57 output: &mut [u8],
58) -> Result<(), AecError> {
59 decoder::decode_into(input, params, output_samples, output)
60}
61
62/// Helper: convert GRIB2 `ccsdsFlags` (template 5.42) to `AecFlags`.
63pub fn flags_from_grib2_ccsds_flags(ccsds_flags: u8) -> AecFlags {
64 let mut flags = AecFlags::empty();
65
66 if (ccsds_flags & (1 << 0)) != 0 {
67 flags |= AecFlags::DATA_SIGNED;
68 }
69 if (ccsds_flags & (1 << 1)) != 0 {
70 flags |= AecFlags::DATA_3BYTE;
71 }
72 if (ccsds_flags & (1 << 2)) != 0 {
73 flags |= AecFlags::MSB;
74 }
75 if (ccsds_flags & (1 << 3)) != 0 {
76 flags |= AecFlags::DATA_PREPROCESS;
77 }
78 if (ccsds_flags & (1 << 4)) != 0 {
79 flags |= AecFlags::RESTRICTED;
80 }
81 if (ccsds_flags & (1 << 5)) != 0 {
82 flags |= AecFlags::PAD_RSI;
83 }
84
85 flags
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn flags_mapping_smoke() {
94 let f = flags_from_grib2_ccsds_flags(0b0011_1011);
95 assert!(f.contains(AecFlags::DATA_SIGNED));
96 assert!(f.contains(AecFlags::DATA_3BYTE));
97 assert!(!f.contains(AecFlags::MSB));
98 assert!(f.contains(AecFlags::DATA_PREPROCESS));
99 assert!(f.contains(AecFlags::RESTRICTED));
100 assert!(f.contains(AecFlags::PAD_RSI));
101 }
102}