srcdmp 0.1.0-alpha.1

Amazing code snapshot tool and library
Documentation
use crate::raw::codec::bits::{BitReader, BitWriter};
use crate::raw::codec::huffman::{HUFFMAN_CODES, HUFFMAN_TREE, HuffmanNode};
use crate::raw::error::SdmpError;

/// Encode a string using the SDMP-C4 Huffman-based codec.
///
/// Each character is encoded using its Huffman code from the
/// empirically-derived frequency table. Characters not present in
/// the frequency table are emitted as a literal escape sequence:
/// an 8-bit `0xFF` marker followed by the 24-bit Unicode code point.
///
/// The output begins with a single byte indicating how many padding
/// bits were added to the final byte.
///
/// # Examples
///
/// ```rust
/// use srcdmp::raw::codec::sdmpc4::{encode, decode};
///
/// let original = "fn main() {\n    let x = 42;\n}";
/// let encoded = encode(original);
/// let decoded = decode(&encoded).unwrap();
/// assert_eq!(decoded, original);
/// ```
#[must_use]
pub fn encode(input: &str) -> Vec<u8> {
    let codes = &*HUFFMAN_CODES;
    let mut writer = BitWriter::new();

    for c in input.chars() {
        if let Some(&(code, length)) = codes.get(&c) {
            writer.write_bits(code, length);
        } else {
            writer.write_bits(0xFF, 8);
            let codepoint = c as u32;
            writer.write_bits(u64::from(codepoint), 24);
        }
    }

    let padding = writer.padding_bits();
    let mut output = vec![padding];
    output.extend(writer.finish());
    output
}

/// Decode SDMP-C4 encoded bytes back into a string.
///
/// Walks the Huffman tree bit by bit. When a leaf node is reached,
/// the corresponding character is emitted. If the leaf's character
/// is `\u{FF}` (the escape sentinel), the next 24 bits are read as
/// a raw Unicode code point.
///
/// # Errors
///
/// Returns [`SdmpError::UnexpectedEof`] if the bit stream ends
/// unexpectedly (e.g., during a literal escape code point read).
/// Returns [`SdmpError::InvalidUtf8`] if a literal code point is
/// not a valid Unicode scalar value.
///
/// # Examples
///
/// ```rust
/// use srcdmp::raw::codec::sdmpc4::{encode, decode};
///
/// let original = "hello, world!";
/// let encoded = encode(original);
/// let decoded = decode(&encoded).unwrap();
/// assert_eq!(decoded, original);
/// ```
pub fn decode(input: &[u8]) -> Result<String, SdmpError> {
    if input.is_empty() {
        return Ok(String::new());
    }

    let padding = input[0];
    let data = &input[1..];

    if data.is_empty() {
        return Ok(String::new());
    }

    let mut reader = BitReader::new(data, padding);
    let mut output = String::new();
    let tree = &*HUFFMAN_TREE;

    let mut node = tree;

    loop {
        match reader.read_bit() {
            None => break,
            Some(bit) => {
                match node {
                    HuffmanNode::Internal { left, right, .. } => {
                        node = if bit == 0 { left } else { right };
                    }
                    HuffmanNode::Leaf { .. } => unreachable!(),
                }

                if let HuffmanNode::Leaf { ch, .. } = node {
                    if *ch as u32 == 0xFF {
                        let mut codepoint = 0u32;
                        for _ in 0..24 {
                            match reader.read_bit() {
                                Some(b) => codepoint = (codepoint << 1) | u32::from(b),
                                None => return Err(SdmpError::UnexpectedEof),
                            }
                        }
                        match char::from_u32(codepoint) {
                            Some(c) => output.push(c),
                            None => return Err(SdmpError::InvalidUtf8),
                        }
                    } else {
                        output.push(*ch);
                    }
                    node = tree;
                }
            }
        }
    }

    Ok(output)
}