use anyhow::{bail, Result}; use log::warn; use std::{ collections::VecDeque, io::{Read, Seek}, }; use crate::matroska::MatroskaTag; trait ReadAndSeek: Read + Seek {} impl ReadAndSeek for T {} #[derive(Debug, Clone, Copy)] pub struct StackTag { end: Option, id: u64, } pub struct EbmlReader { inner: Box, stack: Vec, queue: VecDeque, pub position: usize, } pub trait EbmlRead: Sized { fn read(r: &mut EbmlReader) -> Result; } impl EbmlReader { pub fn new(inner: T) -> Self { Self { queue: VecDeque::new(), inner: Box::new(inner), stack: vec![], position: 0, } } pub fn read_byte(&mut self) -> Result { let mut b = [0u8]; self.inner.read_exact(&mut b)?; self.position += 1; Ok(b[0]) } pub fn read_buf(&mut self, size: impl Into) -> Result> { let size = size.into(); let mut b = vec![0u8; size]; self.inner.read_exact(&mut b)?; self.position += size; Ok(b) } pub fn read_vint_len(&mut self) -> Result<(u64, usize)> { let s = self.read_byte()?; let len = s.leading_zeros() + 1; if len > 8 { bail!("varint too long"); } let mut value = s as u64; value -= 1 << (8 - len); for _ in 1..len { value <<= 8; value += self.read_byte()? as u64; } Ok((value, len as usize)) } pub fn read_vint(&mut self) -> Result { Ok(self.read_vint_len()?.0) } pub fn read_utf8(&mut self, size: impl Into) -> Result { let b = self.read_buf(size)?; Ok(String::from_utf8(b)?) } pub fn read_tag_id(&mut self) -> Result { let (value, len) = self.read_vint_len()?; Ok(value + (1 << (7 * len))) } pub fn read_tag_size(&mut self) -> Result { Ok(EbmlSize::from_vint(self.read_vint_len()?)) } pub fn read_stuff(&mut self) -> Result<()> { if let Some(e) = self.stack.last().map(|e| *e) { if let Some(end) = e.end { if self.position >= end { if self.position != end { warn!("we missed the end") } self.stack.pop(); self.queue.push_back(MatroskaTag::parse(e.id, &[0])?); } } } let id = self.read_tag_id()?; let size = self.read_tag_size()?; let is_master = MatroskaTag::is_master(id)?; let tag = if is_master { MatroskaTag::parse(id, &[])? } else { let data = self.read_buf(size)?; MatroskaTag::parse(id, &data)? }; if let Some(path) = tag.path() { // we have slightly different rules for closing tags implicitly // this closes as many tags as needed to make the next tag a valid child while let Some(tag @ StackTag { end: None, .. }) = self.stack.last() { let mut valid_child = false; for p in path { if *p == tag.id { valid_child = true; } } if valid_child { break; } else { self.queue .push_back(MatroskaTag::parse(self.stack.pop().unwrap().id, &[0])?); } } } if is_master { self.stack.push(StackTag { end: size.some().map(|s| s + self.position), id, }); } self.queue.push_back(tag); Ok(()) } } impl Iterator for EbmlReader { type Item = Result; fn next(&mut self) -> Option { if let Some(t) = self.queue.pop_front() { Some(Ok(t)) } else { match self.read_stuff() { Ok(()) => self.next(), Err(e) => Some(Err(e)), } } } } #[derive(Debug, Clone, Copy)] pub enum EbmlSize { Exact(usize), Unknown, } impl EbmlSize { pub fn from_vint((value, len): (u64, usize)) -> EbmlSize { if value == ((1 << (7 * len)) - 1) { Self::Unknown } else { Self::Exact(value as usize) } } pub fn some(self) -> Option { match self { EbmlSize::Exact(s) => Some(s), EbmlSize::Unknown => None, } } } impl Into for EbmlSize { fn into(self) -> usize { match self { EbmlSize::Exact(s) => s, EbmlSize::Unknown => panic!("unknown size, where it should have been known"), } } }