Skip to content

Roundtrip Compression Failing #1

@KirkBuah

Description

@KirkBuah

When compressing and decompressing a byte vector using this library, the round-trip works correctly when bit_depth == 8, but for other bit depths the decompressed output does not match the original input.

My reproducible example is similar to the procedure you can see in graec.c.

Minimal Example

use libaec_sys::*

struct Stream(aec_stream);

impl Stream {
    fn new(bits_per_sample: u32, block_size: u32, rsi: u32, flags: u32) -> Self {
        let mut raw: aec_stream = unsafe { std::mem::zeroed() };
        raw.bits_per_sample = bits_per_sample;
        raw.block_size = block_size;
        raw.rsi = rsi;
        raw.flags = flags;

        Self(raw)
    }

    fn total_in(&self) -> usize {
        self.0.total_in
    }

    fn total_out(&self) -> usize {
        self.0.total_out
    }

    fn encode(&mut self, input: &[u8], output: &mut [u8]) -> Result<(), &'static str> {
        self.0.next_in = input.as_ptr();
        self.0.avail_in = input.len();
        self.0.next_out = output.as_mut_ptr();
        self.0.avail_out = output.len();

        let result = unsafe { aec_encode_init(&mut self.0) };
        if result as u32 != AEC_OK {
            return Err("aec_encode_init() failed");
        }

        // Process until all input consumed and output space remains
        while self.0.avail_in > 0 || self.0.avail_out == 0 {
            let result = unsafe { aec_encode(&mut self.0, AEC_NO_FLUSH as i32) };
            if result as u32 != AEC_OK {
                return Err("aec_encode() failed");
            }

            if self.0.avail_out == 0 {
                return Err("output buffer too small");
            }
        }

        // Final flush
        let result = unsafe { aec_encode(&mut self.0, AEC_FLUSH as i32) };
        if result as u32 != AEC_OK {
            return Err("aec_encode() flush failed");
        }

        let result = unsafe { aec_encode_end(&mut self.0) };
        if result as u32 != AEC_OK {
            return Err("aec_encode_end() failed");
        }

        Ok(())
    }

    fn decode(&mut self, input: &[u8], output: &mut [u8]) -> Result<(), &'static str> {
        self.0.next_in = input.as_ptr();
        self.0.avail_in = input.len();
        self.0.next_out = output.as_mut_ptr();
        self.0.avail_out = output.len();

        let result = unsafe { aec_decode_init(&mut self.0) };
        if result as u32 != AEC_OK {
            return Err("aec_decode_init() failed");
        }

        while self.0.avail_in > 0 || self.0.avail_out == 0 {
            let result = unsafe { aec_decode(&mut self.0, AEC_NO_FLUSH as i32) };
            if result as u32 != AEC_OK {
                return Err("aec_decode() failed");
            }

            if self.0.avail_out == 0 {
                // Caller must handle reallocating or extending output buffer
                return Err("output buffer too small");
            }
        }

        let result = unsafe { aec_decode_end(&mut self.0) };
        if result as u32 != AEC_OK {
            return Err("aec_decode_end() failed");
        }

        Ok(())
    }
}

#[test]
fn roundtrip_test() {
    let n = 3000 * 4096; // number of bytes
    // Generate vector with numbers 1..=100 repeating
    let bytes: Vec<u8> = (0..n)
        .map(|i| ((i % 100) + 1) as u8)
        .collect();

    let mut codec = Stream::new(12, 32, 128, 0);

    // COMPRESS
    let mut encoded = vec![0; bytes.len() * 67 / 64 + 256]; // Maximum output size according to specifications
    let result = codec.encode(&bytes, &mut encoded);
    assert!(result.is_ok());

    let encoded = &encoded[..codec.total_out()];


    // DECOMPRESS
    let mut decoded = vec![0; bytes.len() * 4];
    let result = codec.decode(&encoded, &mut decoded);
    assert!(result.is_ok());

    let decoded = &decoded[..codec.total_out()];

    assert_eq!(bytes, decoded)
}

Environment

  • Rust version: 1.89.0
  • libaec-sys version: 0.1.2

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions