vcdiff 0.1.0

High-level bindings to open-vcdiff, a codec for the VCDIFF binary delta format.
Documentation
#[macro_use]
extern crate bitflags;
extern crate open_vcdiff_sys;

use std::os;
use std::ptr;

bitflags! {
    pub flags FormatExtension: u32 {
        const FORMAT_STANDARD    = 0b000,
        const FORMAT_INTERLEAVED = 0b001,
        const FORMAT_CHECKSUM    = 0b010,
        const FORMAT_JSON        = 0b100,
    }
}


pub fn encode(dictionary: &[u8], target: &[u8], format_extensions: FormatExtension, look_for_target_matches: bool) -> Vec<u8> {
    use open_vcdiff_sys::VCDiffFormatExtensionFlagValues::*;

    let mut encoded_data = ptr::null_mut();
    let mut encoded_len = 0;

    let mut flags = VCD_STANDARD_FORMAT as os::raw::c_int;

    if format_extensions.contains(FORMAT_INTERLEAVED) {
        flags = flags | VCD_FORMAT_INTERLEAVED as os::raw::c_int;
    }

    if format_extensions.contains(FORMAT_CHECKSUM) {
        flags = flags | VCD_FORMAT_CHECKSUM as os::raw::c_int;
    }

    if format_extensions.contains(FORMAT_JSON) {
        flags = flags | VCD_FORMAT_JSON as os::raw::c_int;
    }

    unsafe {
        open_vcdiff_sys::encode(dictionary.as_ptr(),
                                dictionary.len(),
                                target.as_ptr(),
                                target.len(),
                                &mut encoded_data,
                                &mut encoded_len,
                                flags,
                                if look_for_target_matches { 1 } else { 0 });
    }

    let mut result = Vec::with_capacity(encoded_len);

    unsafe {
        ptr::copy_nonoverlapping(encoded_data, result.as_mut_ptr(), encoded_len);
        result.set_len(encoded_len);
        open_vcdiff_sys::free_data(encoded_data);
    }

    result
}

pub fn decode(dictionary: &[u8], encoded: &[u8]) -> Vec<u8> {
    let mut target_data = ptr::null_mut();
    let mut target_len = 0;

    unsafe {
        open_vcdiff_sys::decode(dictionary.as_ptr(),
                                dictionary.len(),
                                encoded.as_ptr(),
                                encoded.len(),
                                &mut target_data,
                                &mut target_len);
    }

    let mut result = Vec::with_capacity(target_len);

    unsafe {
        ptr::copy_nonoverlapping(target_data, result.as_mut_ptr(), target_len);
        result.set_len(target_len);
        open_vcdiff_sys::free_data(target_data);
    }

    result
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn roundtrip_standard() {
        let dict: &[u8] = &[1, 2, 3];
        let target: &[u8] = &[4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 4];
        let encoded = encode(dict, target, FORMAT_STANDARD, false);
        let decoded = decode(dict, &encoded);

        assert_eq!(target, decoded.as_slice());
    }

    #[test]
    fn roundtrip_standard_target_matches() {
        let dict: &[u8] = &[1, 2, 3];
        let target: &[u8] = &[4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 4];
        let encoded = encode(dict, target, FORMAT_STANDARD, true);
        let decoded = decode(dict, &encoded);

        assert_eq!(target, decoded.as_slice());
    }

    #[test]
    fn roundtrip_interleaved() {
        let dict: &[u8] = &[1, 2, 3];
        let target: &[u8] = &[4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 4];
        let encoded = encode(dict, target, FORMAT_INTERLEAVED, false);
        let decoded = decode(dict, &encoded);

        assert_eq!(target, decoded.as_slice());
    }

    #[test]
    fn roundtrip_interleaved_target_matches() {
        let dict: &[u8] = &[1, 2, 3];
        let target: &[u8] = &[4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 4];
        let encoded = encode(dict, target, FORMAT_INTERLEAVED, true);
        let decoded = decode(dict, &encoded);

        assert_eq!(target, decoded.as_slice());
    }

    #[test]
    fn roundtrip_checksum() {
        let dict: &[u8] = &[1, 2, 3];
        let target: &[u8] = &[4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 4];
        let encoded = encode(dict, target, FORMAT_CHECKSUM, false);
        let decoded = decode(dict, &encoded);

        assert_eq!(target, decoded.as_slice());
    }

    #[test]
    fn roundtrip_checksum_target_matches() {
        let dict: &[u8] = &[1, 2, 3];
        let target: &[u8] = &[4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 4];
        let encoded = encode(dict, target, FORMAT_CHECKSUM, true);
        let decoded = decode(dict, &encoded);

        assert_eq!(target, decoded.as_slice());
    }

    #[test]
    fn roundtrip_interleaved_checksum() {
        let dict: &[u8] = &[1, 2, 3];
        let target: &[u8] = &[4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 4];
        let encoded = encode(dict, target, FORMAT_INTERLEAVED | FORMAT_CHECKSUM, false);
        let decoded = decode(dict, &encoded);

        assert_eq!(target, decoded.as_slice());
    }

    #[test]
    fn roundtrip_interleaved_checksum_target_matches() {
        let dict: &[u8] = &[1, 2, 3];
        let target: &[u8] = &[4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 4];
        let encoded = encode(dict, target, FORMAT_INTERLEAVED | FORMAT_CHECKSUM, true);
        let decoded = decode(dict, &encoded);

        assert_eq!(target, decoded.as_slice());
    }
}