jimage-rs 0.0.4

A fast and efficient Rust library for working with jimage files used by the Java Platform Module System.
Documentation
use crate::error::{JImageError, Result};
use crate::jimage::Endianness;
use byteorder::ByteOrder;

pub(crate) fn read_integer<T: ReadFromBytes>(
    bytes: &[u8],
    offset: usize,
    endianness: &Endianness,
) -> Result<T> {
    read_integer_internal(bytes, offset, endianness, None)
}

pub(crate) fn read_integer_mut<T: ReadFromBytes>(
    bytes: &[u8],
    offset: &mut usize,
    endianness: &Endianness,
) -> Result<T> {
    read_integer_internal(bytes, *offset, endianness, Some(offset))
}

fn read_integer_internal<T: ReadFromBytes>(
    bytes: &[u8],
    from: usize,
    endianness: &Endianness,
    offset_out: Option<&mut usize>,
) -> Result<T> {
    let size = T::SIZE;
    let to = from + size;
    let slice = bytes
        .get(from..to)
        .ok_or(JImageError::RawRead { from, to })?;

    if let Some(offset) = offset_out {
        *offset = to;
    }

    T::read(slice, endianness)
}

pub(crate) trait ReadFromBytes: Sized {
    const SIZE: usize = std::mem::size_of::<Self>();
    fn read(bytes: &[u8], endianness: &Endianness) -> Result<Self>;
}

macro_rules! define_detect_endianness {
    ($fn_name:ident, $magic:expr, $label:expr) => {
        pub fn $fn_name(bytes: &[u8]) -> Result<Endianness> {
            use byteorder::{BigEndian, LittleEndian};
            const LEN: usize = 4;
            let magic_bytes = bytes
                .get(..LEN)
                .ok_or(JImageError::RawRead { from: 0, to: LEN })?;
            if LittleEndian::read_u32(magic_bytes) == $magic {
                Ok(Endianness::Little)
            } else if BigEndian::read_u32(magic_bytes) == $magic {
                Ok(Endianness::Big)
            } else {
                Err(JImageError::Magic {
                    magic: magic_bytes.try_into().unwrap_or([0; LEN]),
                    context: $label,
                })
            }
        }
    };
}
define_detect_endianness!(detect_endianness_header, 0xCAFEDADA, "header");
define_detect_endianness!(detect_endianness_resource, 0xCAFEFAFA, "resource");

macro_rules! impl_read_from_bytes {
    ($t:ty, $read_fn:ident, $size:expr) => {
        impl ReadFromBytes for $t {
            fn read(bytes: &[u8], endianness: &Endianness) -> Result<Self> {
                use byteorder::{BigEndian, LittleEndian};
                if bytes.len() < $size {
                    return Err(JImageError::Internal {
                        value: format!("not enough bytes for {}", stringify!($t)),
                    });
                }
                Ok(match endianness {
                    Endianness::Little => LittleEndian::$read_fn(bytes),
                    Endianness::Big => BigEndian::$read_fn(bytes),
                })
            }
        }
    };
}
impl_read_from_bytes!(i32, read_i32, 4);
impl_read_from_bytes!(u32, read_u32, 4);
impl_read_from_bytes!(u64, read_u64, 8);