jvm-class 0.1.0

Zero-copy parser for JVM class files (Java 1.1 - 8, major versions 45-52)
Documentation
use crate::{
    CpIndex,
    error::{Error, Result},
};

/// Cursor over a borrowed byte slice, reading big-endian values from the front.
#[derive(Debug, Clone, Copy)]
pub struct Cursor<'a>(&'a [u8]);

impl<'a> Cursor<'a> {
    /// Creates a cursor positioned at the start of `bytes`.
    pub fn new(bytes: &'a [u8]) -> Self {
        Self(bytes)
    }

    /// Returns the not yet consumed bytes.
    pub fn remaining(&self) -> &'a [u8] {
        self.0
    }

    /// Returns the number of not yet consumed bytes.
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Returns `true` if everything has been consumed.
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }
}

impl<'a> From<&'a [u8]> for Cursor<'a> {
    fn from(value: &'a [u8]) -> Self {
        Self::new(value)
    }
}

/// Generates big-endian readers for fixed-size integer types.
macro_rules! read_int {
    ($($name:ident -> $type:ty),* $(,)?) => {$(
        /// Reads a big-endian value, advancing past it.
        fn $name(&mut self) -> Result<$type> {
            let mut buf = [0u8; size_of::<$type>()];
            buf.copy_from_slice(self.read_bytes(size_of::<$type>())?);

            Ok(<$type>::from_be_bytes(buf))
        }
    )*};
}

/// Big-endian reading primitives shared by every parser in this crate.
pub trait ReadBytes<'a> {
    /// Consumes exactly `len` bytes, or fails with [`Error::UnexpectedEof`].
    fn read_bytes(&mut self, len: usize) -> Result<&'a [u8]>;

    /// Consumes and returns everything that is left.
    fn read_rest(&mut self) -> &'a [u8];

    read_int! {
        read_u8 -> u8,
        read_u16 -> u16,
        read_u32 -> u32,
        read_u64 -> u64,
        read_i32 -> i32,
        read_i64 -> i64,
    }

    /// Reads an IEEE 754 single-precision float.
    fn read_f32(&mut self) -> Result<f32> {
        Ok(f32::from_bits(self.read_u32()?))
    }

    /// Reads an IEEE 754 double-precision float.
    fn read_f64(&mut self) -> Result<f64> {
        Ok(f64::from_bits(self.read_u64()?))
    }

    /// Reads a `u16` count, then applies `fun` that many times.
    fn repeat_by_u16<T>(&mut self, mut fun: impl FnMut(&mut Self) -> Result<T>) -> Result<Vec<T>>
    where
        Self: Sized,
    {
        let count = self.read_u16()?;
        let mut items = Vec::with_capacity(count as usize);

        for _ in 0..count {
            items.push(fun(self)?);
        }

        Ok(items)
    }

    /// Reads a `u8` count, then applies `fun` that many times.
    fn repeat_by_u8<T>(&mut self, mut fun: impl FnMut(&mut Self) -> Result<T>) -> Result<Vec<T>>
    where
        Self: Sized,
    {
        let count = self.read_u8()?;
        let mut items = Vec::with_capacity(count as usize);

        for _ in 0..count {
            items.push(fun(self)?);
        }

        Ok(items)
    }

    /// Reads a `u16`-prefixed list of constant pool indices.
    fn read_indices(&mut self) -> Result<Vec<CpIndex>>
    where
        Self: Sized,
    {
        self.repeat_by_u16(|cursor| cursor.read_u16())
    }

    /// Reads a constant pool index where `0` means "absent".
    fn read_optional_index(&mut self) -> Result<Option<CpIndex>> {
        let index = self.read_u16()?;

        Ok((index != 0).then_some(index))
    }
}

impl<'a> ReadBytes<'a> for Cursor<'a> {
    fn read_bytes(&mut self, len: usize) -> Result<&'a [u8]> {
        match self.0.split_at_checked(len) {
            Some((head, tail)) => {
                self.0 = tail;

                Ok(head)
            }
            None => Err(Error::UnexpectedEof {
                needed: len,
                available: self.0.len(),
            }),
        }
    }

    fn read_rest(&mut self) -> &'a [u8] {
        std::mem::take(&mut self.0)
    }
}