Skip to content

Roundtrip Compression Failing #1

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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