ozlrip-decode 0.2.0

OpenZL decoder for ozlrip
Documentation
use alloc::{string::ToString, vec::Vec};

use ozlrip_core::{Error, ErrorKind, Limits, Result};

use super::{OwnedStream, StreamInput, numeric_element_count};

pub(super) fn decode_node(
    input: StreamInput<'_>,
    header: &[u8],
    limits: Limits,
) -> Result<OwnedStream> {
    if !header.is_empty() {
        return Err(Error::new(ErrorKind::Malformed)
            .with_detail("parse_int transform header must be empty"));
    }
    if input.element_width != 8 {
        return Err(Error::new(ErrorKind::InvalidType)
            .with_detail("parse_int input must be width-8 numeric values"));
    }
    if input.string_lengths.is_some() {
        return Err(Error::new(ErrorKind::InvalidType)
            .with_detail("parse_int input must be numeric, not string"));
    }

    let count = numeric_element_count(input.bytes, input.element_width)?;
    let mut lengths = Vec::new();
    lengths.try_reserve_exact(count).map_err(|_| {
        Error::new(ErrorKind::LimitExceeded).with_detail("parse_int length allocation failed")
    })?;

    let mut output = Vec::new();
    for raw in input.bytes.chunks_exact(8) {
        let mut bytes = [0u8; 8];
        bytes.copy_from_slice(raw);
        let text = i64::from_le_bytes(bytes).to_string();
        let len = u32::try_from(text.len()).map_err(|_| Error::new(ErrorKind::IntegerOverflow))?;
        let new_len = output
            .len()
            .checked_add(text.len())
            .ok_or_else(|| Error::new(ErrorKind::IntegerOverflow))?;
        if new_len > limits.max_decoded_bytes || new_len > limits.max_buffer_bytes {
            return Err(
                Error::new(ErrorKind::LimitExceeded).with_detail("decoded output limit exceeded")
            );
        }
        lengths.push(len);
        output.try_reserve_exact(text.len()).map_err(|_| {
            Error::new(ErrorKind::LimitExceeded).with_detail("parse_int output allocation failed")
        })?;
        output.extend_from_slice(text.as_bytes());
    }

    Ok(OwnedStream {
        bytes: output,
        element_width: 1,
        string_lengths: Some(lengths),
        recyclable: false,
    })
}