// Copyright (c) the JPEG XL Project Authors. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. use crate::error::{Error, Result}; use crate::api::{ JxlBitstreamInput, JxlSignatureType, check_signature_internal, inner::process::SmallBuffer, }; #[derive(Clone)] enum ParseState { SignatureNeeded, BoxNeeded, CodestreamBox(u64), SkippableBox(u64), } enum CodestreamBoxType { None, Jxlc, Jxlp(u32), LastJxlp, } pub(super) struct BoxParser { pub(super) box_buffer: SmallBuffer, state: ParseState, box_type: CodestreamBoxType, } impl BoxParser { pub(super) fn new() -> Self { BoxParser { box_buffer: SmallBuffer::new(128), state: ParseState::SignatureNeeded, box_type: CodestreamBoxType::None, } } // Reads input until the next byte of codestream is available. // This function might over-read bytes. Thus, the contents of self.box_buffer should always be // read after this function call. // Returns the number of codestream bytes that will be available to be read after this call, // including any bytes in self.box_buffer. // Might return `u64::MAX`, indicating that the rest of the file is codestream. pub(super) fn get_more_codestream(&mut self, input: &mut dyn JxlBitstreamInput) -> Result { loop { match self.state.clone() { ParseState::SignatureNeeded => { self.box_buffer.refill(|b| input.read(b), None)?; match check_signature_internal(&self.box_buffer)? { None => return Err(Error::InvalidSignature), Some(JxlSignatureType::Codestream) => { self.state = ParseState::CodestreamBox(u64::MAX); return Ok(u64::MAX); } Some(JxlSignatureType::Container) => { self.box_buffer .consume(JxlSignatureType::Container.signature().len()); self.state = ParseState::BoxNeeded; } } } ParseState::CodestreamBox(b) => { return Ok(b); } ParseState::SkippableBox(mut s) => { let num = s.min(usize::MAX as u64) as usize; let skipped = if !self.box_buffer.is_empty() { self.box_buffer.consume(num) } else { input.skip(num)? }; if skipped == 0 { return Err(Error::OutOfBounds(num)); } s -= skipped as u64; if s == 0 { self.state = ParseState::BoxNeeded; } else { self.state = ParseState::SkippableBox(s); } } ParseState::BoxNeeded => { self.box_buffer.refill(|b| input.read(b), None)?; let min_len = match &self.box_buffer[..] { [0, 0, 0, 1, ..] => 16, _ => 8, }; if self.box_buffer.len() <= min_len { return Err(Error::OutOfBounds(min_len - self.box_buffer.len())); } let ty: [_; 4] = self.box_buffer[4..8].try_into().unwrap(); let extra_len = if &ty == b"jxlp" { 4 } else { 0 }; if self.box_buffer.len() <= min_len + extra_len { return Err(Error::OutOfBounds( min_len + extra_len - self.box_buffer.len(), )); } let box_len = match &self.box_buffer[..] { [0, 0, 0, 1, ..] => { u64::from_be_bytes(self.box_buffer[8..16].try_into().unwrap()) } _ => u32::from_be_bytes(self.box_buffer[0..4].try_into().unwrap()) as u64, }; // Per JXL spec: jxlc box with length 0 has special meaning "extends to EOF" let content_len = if box_len == 0 && (&ty == b"jxlp" || &ty == b"jxlc") { u64::MAX } else { if box_len <= (min_len + extra_len) as u64 { return Err(Error::InvalidBox); } box_len - min_len as u64 - extra_len as u64 }; match &ty { b"jxlc" => { if matches!( self.box_type, CodestreamBoxType::Jxlp(..) | CodestreamBoxType::LastJxlp ) { return Err(Error::InvalidBox); } self.box_type = CodestreamBoxType::Jxlc; self.state = ParseState::CodestreamBox(content_len); } b"jxlp" => { let index = u32::from_be_bytes( self.box_buffer[min_len..min_len + 4].try_into().unwrap(), ); let wanted_idx = match self.box_type { CodestreamBoxType::Jxlc | CodestreamBoxType::LastJxlp => { return Err(Error::InvalidBox); } CodestreamBoxType::None => 0, CodestreamBoxType::Jxlp(i) => i + 1, }; let last = index & 0x80000000 != 0; let idx = index & 0x7fffffff; if idx != wanted_idx { return Err(Error::InvalidBox); } self.box_type = if last { CodestreamBoxType::LastJxlp } else { CodestreamBoxType::Jxlp(idx) }; self.state = ParseState::CodestreamBox(content_len); } _ => { self.state = ParseState::SkippableBox(content_len); } } self.box_buffer.consume(min_len + extra_len); } } } } pub(super) fn consume_codestream(&mut self, amount: u64) { if let ParseState::CodestreamBox(cb) = &mut self.state { *cb = cb.checked_sub(amount).unwrap(); if *cb == 0 { self.state = ParseState::BoxNeeded; } } else if amount != 0 { unreachable!() } } }