jubako 0.4.0

The reference implementation of the Jubako container format
Documentation
#[inline]
fn extend_sign(val: u64, nbytes: usize) -> i64 {
    let shift = (8 - nbytes) * 8;
    (val << shift) as i64 >> shift
}
#[inline]
fn unextend_sign(val: i64, nbytes: usize) -> u64 {
    let shift = (8 - nbytes) * 8;
    (val << shift) as u64 >> shift
}

#[inline]
pub fn write_u8(val: u8) -> [u8; 1] {
    val.to_le_bytes()
}

#[inline]
pub fn write_u16(val: u16) -> [u8; 2] {
    val.to_le_bytes()
}

#[inline]
pub fn write_u32(val: u32) -> [u8; 4] {
    val.to_le_bytes()
}

#[inline]
pub fn write_u64(val: u64) -> [u8; 8] {
    val.to_le_bytes()
}

#[inline]
pub fn write_from_u64(val: u64, size: usize) -> [u8; 8] {
    debug_assert!(size <= 8);
    let mut buf = val.to_le_bytes();
    buf[size..].fill(0);
    buf
}

#[inline]
pub fn write_from_i64(val: i64, size: usize) -> [u8; 8] {
    debug_assert!(size <= 8);
    write_from_u64(unextend_sign(val, size), size)
}

#[inline]
pub fn read_u8(buf: &[u8]) -> u8 {
    debug_assert!(!buf.is_empty());
    u8::from_le_bytes((buf).try_into().unwrap())
}

#[inline]
pub fn read_u16(buf: &[u8]) -> u16 {
    debug_assert!(2 <= buf.len());
    u16::from_le_bytes((buf).try_into().unwrap())
}

#[inline]
pub fn read_u32(buf: &[u8]) -> u32 {
    debug_assert!(4 <= buf.len());
    u32::from_le_bytes((buf).try_into().unwrap())
}

#[inline]
pub fn read_u64(buf: &[u8]) -> u64 {
    debug_assert!(8 <= buf.len());
    u64::from_le_bytes((buf).try_into().unwrap())
}

#[inline]
pub fn read_to_u64(size: usize, buf: &[u8]) -> u64 {
    debug_assert!(size <= 8);
    let mut out = [0; 8];
    out[..size].copy_from_slice(&buf[..size]);
    u64::from_le_bytes(out)
}

#[inline]
pub fn read_i8(buf: &[u8]) -> i8 {
    debug_assert!(!buf.is_empty());
    i8::from_le_bytes(buf[..1].try_into().unwrap())
}

#[inline]
pub fn read_i16(buf: &[u8]) -> i16 {
    debug_assert!(2 <= buf.len());
    i16::from_le_bytes(buf.try_into().unwrap())
}

#[inline]
pub fn read_i32(buf: &[u8]) -> i32 {
    debug_assert!(4 <= buf.len());
    i32::from_le_bytes(buf.try_into().unwrap())
}

#[inline]
pub fn read_i64(buf: &[u8]) -> i64 {
    debug_assert!(8 <= buf.len());
    i64::from_le_bytes(buf.try_into().unwrap())
}

#[inline]
pub fn read_to_i64(size: usize, buf: &[u8]) -> i64 {
    extend_sign(read_to_u64(size, buf), size)
}

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

    #[rustest::test(params:(&'static [u8], u8)=[
        ([0x01].as_slice(), 1_u8),
        ([0xff].as_slice(), 255_u8),
        ([0x80].as_slice(), 128_u8),
    ])]
    fn read_u8_tests(Param((input, expected)): Param) {
        assert_eq!(read_u8(input), expected)
    }

    #[rustest::test(params:(&'static [u8], u16)=[
        ([0x01, 0x00].as_slice(), 1_u16),
        ([0x00, 0x01].as_slice(), 0x0100_u16),
        ([0xff, 0x00].as_slice(), 255_u16),
        ([0x80, 0x00].as_slice(), 128_u16),
        ([0x00, 0x80].as_slice(), 0x8000_u16),
        ([0xff, 0xff].as_slice(), 0xffff_u16)
    ])]
    fn read_u16_tests(Param((input, expected)): Param) {
        assert_eq!(read_u16(input), expected)
    }

    #[rustest::test(params:(&'static [u8], u32)=[
        ([0x00, 0x00, 0x00, 0x00].as_slice(), 0_u32),
        ([0x01, 0x00, 0x00, 0x00].as_slice(), 1_u32),
        ([0xff, 0x00, 0x00, 0x00].as_slice(), 255_u32),
        ([0x80, 0x00, 0x00, 0x00].as_slice(), 128_u32),
        ([0x00, 0x80, 0x00, 0x00].as_slice(), 0x8000_u32),
        ([0x00, 0x00, 0x80, 0x00].as_slice(), 0x800000_u32),
        ([0x00, 0x00, 0x00, 0x80].as_slice(), 0x80000000_u32),
        ([0x78, 0x56, 0x34, 0x12].as_slice(), 0x12345678_u32),
        ([0xff, 0xff, 0xff, 0xff].as_slice(), 0xffffffff_u32)
    ])]
    fn read_u32_tests(Param((input, expected)): Param) {
        assert_eq!(read_u32(input), expected)
    }

    #[rustest::test(params:(&'static [u8], u64)=[
        ([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00].as_slice(), 0_u64),
        ([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00].as_slice(), 1_u64),
        ([0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00].as_slice(), 255_u64),
        ([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff].as_slice(), 0xff00000000000000_u64),
        ([0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01].as_slice(), 0x0123456789abcdef_u64)
    ])]
    fn read_u64_tests(Param((input, expected)): Param) {
        assert_eq!(read_u64(input), expected)
    }

    #[rustest::test(params:(usize, u64)=[
        (1, 1_u64),
        (2, 0x23_01_u64),
        (3, 0x45_23_01_u64),
        (4, 0x67_45_23_01_u64),
        (5, 0x8967452301_u64),
        (6, 0xab8967452301_u64),
        (7, 0xcdab8967452301_u64),
        (8, 0xefcdab8967452301_u64)
    ])]
    fn read_to_u64_test(Param((size, expected)): Param) {
        assert_eq!(
            read_to_u64(size, &[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]),
            expected
        )
    }

    #[rustest::test(params:(u8, &'static [u8])=[
        (1_u8, [0x01].as_slice()),
        (255_u8, [0xff].as_slice()),
        (128_u8, [0x80].as_slice())
    ])]
    fn write_u8_tests(Param((input, expected)): Param) {
        assert_eq!(write_u8(input), expected)
    }

    #[rustest::test(params:(u16, &'static [u8])=[
        (1_u16, [0x01, 0x00].as_slice()),
        (0x0100_u16, [0x00, 0x01].as_slice()),
        (255_u16, [0xff, 0x00].as_slice()),
        (128_u16, [0x80, 0x00].as_slice()),
        (0x8000_u16, [0x00, 0x80].as_slice()),
        (0xffff_u16, [0xff, 0xff].as_slice())
    ])]
    fn write_u16_tests(Param((input, expected)): Param) {
        assert_eq!(write_u16(input), expected)
    }

    #[rustest::test(params:(u32, &'static [u8])=[
        (0_u32, [0x00, 0x00, 0x00, 0x00].as_slice()),
        (1_u32, [0x01, 0x00, 0x00, 0x00].as_slice()),
        (255_u32, [0xff, 0x00, 0x00, 0x00].as_slice()),
        (128_u32, [0x80, 0x00, 0x00, 0x00].as_slice()),
        (0x8000_u32, [0x00, 0x80, 0x00, 0x00].as_slice()),
        (0x800000_u32, [0x00, 0x00, 0x80, 0x00].as_slice()),
        (0x80000000_u32, [0x00, 0x00, 0x00, 0x80].as_slice()),
        (0x12345678_u32, [0x78, 0x56, 0x34, 0x12].as_slice()),
        (0xffffffff_u32, [0xff, 0xff, 0xff, 0xff].as_slice()),
    ])]
    fn write_u32_tests(Param((input, expected)): Param) {
        assert_eq!(write_u32(input), expected)
    }

    #[rustest::test(params:(u64, &'static [u8])=[
        (0_u64, [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00].as_slice()),
        (1_u64, [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00].as_slice()),
        (255_u64, [0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00].as_slice()),
        (0xff00000000000000_u64, [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff].as_slice()),
        (0x0123456789abcdef_u64, [0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01].as_slice()),
    ])]
    fn write_u64_tests(Param((input, expected)): Param) {
        assert_eq!(write_u64(input), expected)
    }

    #[rustest::test(params:(usize, [u8;8])=[
        (1, [0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
        (2, [0xef, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
        (3, [0xef, 0xcd, 0xab, 0x00, 0x00, 0x00, 0x00, 0x00]),
        (4, [0xef, 0xcd, 0xab, 0x89, 0x00, 0x00, 0x00, 0x00]),
        (5, [0xef, 0xcd, 0xab, 0x89, 0x67, 0x00, 0x00, 0x00]),
        (6, [0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x00, 0x00]),
        (7, [0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x00]),
        (8, [0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01]),
    ])]
    fn write_from_u64_test(Param((size, expected)): Param) {
        assert_eq!(write_from_u64(0x0123456789abcdef_u64, size), expected)
    }

    #[rustest::test(params:(&'static[u8], i8)=[
        ([0x00].as_slice(), 0_i8),
        ([0xFF].as_slice(), -1_i8),
        ([0x01].as_slice(), 1_i8),
        ([0x80].as_slice(), i8::MIN),
        ([0x7F].as_slice(), i8::MAX),
        ([0xFE].as_slice(), -2_i8),
        ([0x7F, 0xff].as_slice(), 127_i8),
    ])]
    fn read_i8_tests(Param((input, expected)): Param) {
        assert_eq!(read_i8(input), expected)
    }

    #[rustest::test(params:(&'static[u8], i16)=[
        ([0x00, 0x00].as_slice(), 0_i16),
        ([0xFF, 0xFF].as_slice(), -1_i16),
        ([0x01, 0x00].as_slice(), 1_i16),
        ([0x80, 0xFF].as_slice(), i8::MIN as i16),
        ([0x7F, 0x00].as_slice(), i8::MAX as i16),
        ([0x00, 0x80].as_slice(), i16::MIN),
        ([0xFF, 0x7F].as_slice(), i16::MAX),
        ([0xDC, 0xFE].as_slice(), -292_i16),
    ])]
    fn read_i16_tests(Param((input, expected)): Param) {
        assert_eq!(read_i16(input), expected)
    }

    #[rustest::test(params:(&'static[u8], i32)=[
        ([0x00, 0x00, 0x00, 0x00].as_slice(), 0_i32),
        ([0xFF, 0xFF, 0xFF, 0xFF].as_slice(), -1_i32),
        ([0x01, 0x00, 0x00, 0x00].as_slice(), 1_i32),
        ([0x80, 0xFF, 0xFF, 0xFF].as_slice(), i8::MIN as i32),
        ([0x7F, 0x00, 0x00, 0x00].as_slice(), i8::MAX as i32),
        ([0x00, 0x80, 0xFF, 0xFF].as_slice(), i16::MIN as i32),
        ([0xFF, 0x7F, 0x00, 0x00].as_slice(), i16::MAX as i32),
        ([0x00, 0x00, 0x00, 0x80].as_slice(), i32::MIN),
        ([0xFF, 0xFF, 0xFF, 0x7F].as_slice(), i32::MAX),
        ([0x98, 0xBA, 0xDC, 0xFE].as_slice(), -19_088_744_i32),
    ])]
    fn read_i32_tests(Param((input, expected)): Param) {
        assert_eq!(read_i32(input), expected)
    }

    #[rustest::test(params:(&'static[u8], i64)=[
        ([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00].as_slice(), 0_i64),
        ([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF].as_slice(), -1_i64),
        ([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00].as_slice(), 1_i64),
        ([0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF].as_slice(), i8::MIN as i64),
        ([0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00].as_slice(), i8::MAX as i64),
        ([0x00, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF].as_slice(), i16::MIN as i64),
        ([0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00].as_slice(), i16::MAX as i64),
        ([0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0xFF].as_slice(), i32::MIN as i64),
        ([0xFF, 0xFF, 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00].as_slice(), i32::MAX as i64),
        ([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80].as_slice(), i64::MIN),
        ([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F].as_slice(), i64::MAX),
        ([0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE].as_slice(), -81_985_529_216_486_896_i64),
    ])]
    fn read_i64_tests(Param((input, expected)): Param) {
        assert_eq!(read_i64(input), expected)
    }

    #[rustest::test(params:(usize, i64)=[
        (1, 1_i64),
        (2, 0x23_01_i64),
        (3, 0x45_23_01_i64),
        (4, 0x67_45_23_01_i64),
        (5, 0xff_ff_ff_89_67_45_23_01_u64 as i64),
        (6, 0xff_ff_ab_89_67_45_23_01_u64 as i64),
        (7, 0xff_cd_ab_89_67_45_23_01_u64 as i64),
        (8, 0xef_cd_ab_89_67_45_23_01_u64 as i64),
    ])]
    fn read_to_i64_test(Param((size, expected)): Param) {
        assert_eq!(
            read_to_i64(size, &[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]),
            expected
        )
    }

    #[rustest::test(params:(usize, [u8; 8])=[
        (1, [0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
        (2, [0xef, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
        (3, [0xef, 0xcd, 0xab, 0x00, 0x00, 0x00, 0x00, 0x00]),
        (4, [0xef, 0xcd, 0xab, 0x89, 0x00, 0x00, 0x00, 0x00]),
        (5, [0xef, 0xcd, 0xab, 0x89, 0x67, 0x00, 0x00, 0x00]),
        (6, [0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x00, 0x00]),
        (7, [0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x00]),
        (8, [0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01]),
    ])]
    fn write_from_i64_positive_test(Param((size, expected)): Param) {
        assert_eq!(write_from_i64(0x0123456789abcdef_i64, size), expected)
    }

    #[rustest::test(params:(usize, [u8; 8])=[
        (1, [0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
        (2, [0xef, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
        (3, [0xef, 0xcd, 0xab, 0x00, 0x00, 0x00, 0x00, 0x00]),
        (4, [0xef, 0xcd, 0xab, 0x89, 0x00, 0x00, 0x00, 0x00]),
        (5, [0xef, 0xcd, 0xab, 0x89, 0x67, 0x00, 0x00, 0x00]),
        (6, [0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x00, 0x00]),
        (7, [0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x00]),
        (8, [0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x81]),
    ])]
    fn write_from_i64_negative_test(Param((size, expected)): Param) {
        assert_eq!(
            write_from_i64(0x8123456789abcdef_u64 as i64, size),
            expected
        )
    }

    #[rustest::test(params:usize=[1, 2, 3, 4, 5, 6, 7, 8])]
    fn test_i64_in_and_out(Param(size): Param) {
        let ret = read_to_i64(size, &write_from_i64(2, size));
        assert_eq!(ret, 2);

        let ret = read_to_i64(size, &write_from_i64(-2, size));
        assert_eq!(ret, -2);

        let ret = read_to_i64(size, &write_from_i64(-128, size));
        assert_eq!(ret, -128);

        let ret = read_to_i64(size, &write_from_i64(127, size));
        assert_eq!(ret, 127);

        if size > 1 {
            let ret = read_to_i64(size, &write_from_i64(-10867, size));
            assert_eq!(ret, -10867);
        }
    }

    #[rustest::test(params:usize=[1, 2, 3, 4, 5, 6, 7, 8])]
    fn read_to_i64_minus_1(Param(size): Param) {
        assert_eq!(
            -1,
            read_to_i64(size, &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])
        );
    }
}