#![warn(missing_docs)]
use std::collections::HashMap;
use crate::raw::{codec::sdmp7::decode, entry::Entry, error::SdmpError, header::Header};
pub mod raw {
pub mod entry;
pub mod error;
pub mod header;
pub mod table;
pub mod codec {
pub mod bits;
pub mod freq;
pub mod huffman;
pub mod sdmp7;
pub mod sdmp8;
pub mod sdmpc4;
}
}
#[cfg(feature = "cli")]
pub mod cli {
pub mod log;
pub mod viewer;
}
#[cfg(feature = "fs")]
mod api;
#[cfg(feature = "fs")]
pub use api::{Codec, Dump, DumpBuilder, DumpWriteBuilder};
pub fn unpack_bytes(data: &[u8]) -> Result<HashMap<String, String>, SdmpError> {
let header = Header::read(data)?;
let mut result = HashMap::new();
let mut i = Header::SIZE;
for _ in 0..header.file_count {
let (entry, consumed) = Entry::read(&data[i..])?;
i += consumed;
let end = i + entry.file_size as usize;
if end > data.len() {
return Err(SdmpError::UnexpectedEof);
}
let encoded = &data[i..end];
i = end;
let content = decode(encoded)?;
result.insert(entry.path, content);
}
Ok(result)
}
#[cfg(test)]
mod tests {
use super::raw::table::*;
#[test]
fn round_trip_tier1() {
let encode = build_tier1_encode();
for (index, slot) in TIER1_DECODE.iter().enumerate() {
if let Some(c) = slot {
assert_eq!(encode[c], index as u8);
}
}
}
#[test]
fn header_round_trip() {
use crate::raw::header::*;
let original = Header {
codec: CODEC_C4,
version: 1,
original_size: 123456,
file_count: 34,
};
let bytes = original.write();
let decoded = Header::read(&bytes).unwrap();
assert_eq!(decoded.codec, original.codec);
assert_eq!(decoded.version, original.version);
assert_eq!(decoded.original_size, original.original_size);
assert_eq!(decoded.file_count, original.file_count);
}
#[test]
fn sdmp7_decode_basic() {
use crate::raw::codec::sdmp7::decode;
let bytes = vec![0x26, 0x27, 0x6F];
let result = decode(&bytes).unwrap();
assert_eq!(result, "hi!");
}
#[test]
fn sdmp7_decode_escape() {
use crate::raw::codec::sdmp7::decode;
let bytes = vec![0x7F, 0x41];
let result = decode(&bytes).unwrap();
assert_eq!(result, "A");
}
#[test]
fn sdmp7_round_trip() {
use crate::raw::codec::sdmp7::{decode, encode};
let original = "hello, world! fn main() { let x = 42; }";
let encoded = encode(original);
let decoded = decode(&encoded).unwrap();
assert_eq!(decoded, original);
}
#[test]
fn entry_round_trip() {
use crate::raw::entry::Entry;
let original = Entry {
path: "src/main.rs".to_string(),
file_size: 932,
};
let mut bytes = Vec::new();
original.write(&mut bytes);
let (decoded, consumed) = Entry::read(&bytes).unwrap();
assert_eq!(decoded.path, original.path);
assert_eq!(decoded.file_size, original.file_size);
assert_eq!(consumed, 17);
}
#[test]
fn sdmp7_tier2_round_trip() {
use crate::raw::codec::sdmp7::{decode, encode};
let original = "fn calculate(α: f64, β: f64) -> f64 { α + β }";
let encoded = encode(original);
let decoded = decode(&encoded).unwrap();
assert_eq!(decoded, original);
}
#[test]
fn sdmp7_tier3_round_trip() {
use crate::raw::codec::sdmp7::{decode, encode};
let original = "// 这是一个注释 hello world";
let encoded = encode(original);
let decoded = decode(&encoded).unwrap();
assert_eq!(decoded, original);
}
#[test]
fn sdmpc4_round_trip_basic() {
use crate::raw::codec::sdmpc4::{decode, encode};
let original = "fn main() {\n let x = 42;\n}";
let encoded = encode(original);
let decoded = decode(&encoded).unwrap();
assert_eq!(decoded, original);
}
#[test]
fn sdmpc4_round_trip_unicode() {
use crate::raw::codec::sdmpc4::{decode, encode};
let original = "fn calculate(α: f64) -> f64 { α * 2.0 }";
let encoded = encode(original);
let decoded = decode(&encoded).unwrap();
assert_eq!(decoded, original);
}
#[test]
fn sdmpc4_smaller_than_sdmp7() {
use crate::raw::codec::sdmp7;
use crate::raw::codec::sdmpc4;
let input = include_str!("api.rs");
let c4 = sdmpc4::encode(input);
let s7 = sdmp7::encode(input);
println!("sdmp7: {} bytes", s7.len());
println!("sdmpc4: {} bytes", c4.len());
assert!(c4.len() < s7.len(), "C4 should be smaller than SDMP-7");
}
}