EBML: Stop requiring explicit locking of reader

Signed-off-by: Serial <69764315+Serial-ATA@users.noreply.github.com>
This commit is contained in:
Serial 2023-11-28 15:11:02 -05:00
parent daef1a2b4a
commit f5f52ac07f
No known key found for this signature in database
GPG key ID: DA95198DC17C4568
4 changed files with 131 additions and 75 deletions

View file

@ -3,6 +3,7 @@ use crate::error::Result;
use crate::macros::{decode_err, try_vec}; use crate::macros::{decode_err, try_vec};
use std::io::Read; use std::io::Read;
use std::ops::{Deref, DerefMut};
use byteorder::{BigEndian, ReadBytesExt}; use byteorder::{BigEndian, ReadBytesExt};
use lofty_attr::ebml_master_elements; use lofty_attr::ebml_master_elements;
@ -229,7 +230,11 @@ where
self.ctx.previous_master_length = self.ctx.master_length; self.ctx.previous_master_length = self.ctx.master_length;
} }
fn next_master(&mut self) -> Result<ElementReaderYield> { fn goto_next_master(&mut self) -> Result<ElementReaderYield> {
if self.ctx.master_length != 0 {
self.skip(self.ctx.master_length)?;
}
let header = ElementHeader::read( let header = ElementHeader::read(
&mut self.reader, &mut self.reader,
self.ctx.max_id_length, self.ctx.max_id_length,
@ -249,15 +254,6 @@ where
))) )))
} }
/// Lock the reader to the current master element
pub(crate) fn lock(&mut self) {
self.ctx.locked = true;
}
pub(crate) fn unlock(&mut self) {
self.ctx.locked = false;
}
pub(crate) fn goto_previous_master(&mut self) -> Result<()> { pub(crate) fn goto_previous_master(&mut self) -> Result<()> {
if let Some(previous_master) = self.ctx.previous_master { if let Some(previous_master) = self.ctx.previous_master {
self.ctx.current_master = Some(previous_master); self.ctx.current_master = Some(previous_master);
@ -270,7 +266,7 @@ where
pub(crate) fn next(&mut self) -> Result<ElementReaderYield> { pub(crate) fn next(&mut self) -> Result<ElementReaderYield> {
let Some(current_master) = self.ctx.current_master else { let Some(current_master) = self.ctx.current_master else {
return self.next_master(); return self.goto_next_master();
}; };
if self.ctx.master_length == 0 { if self.ctx.master_length == 0 {
@ -278,7 +274,7 @@ where
return Ok(ElementReaderYield::Eof); return Ok(ElementReaderYield::Eof);
} }
return self.next_master(); return self.goto_next_master();
} }
let header = ElementHeader::read(self, self.ctx.max_id_length, self.ctx.max_size_length)?; let header = ElementHeader::read(self, self.ctx.max_id_length, self.ctx.max_size_length)?;
@ -310,11 +306,33 @@ where
Ok(ElementReaderYield::Child((*child, header.size.value()))) Ok(ElementReaderYield::Child((*child, header.size.value())))
} }
fn lock(&mut self) {
self.ctx.locked = true;
}
fn unlock(&mut self) {
self.ctx.locked = false;
}
pub(crate) fn children(&mut self) -> ElementChildIterator<'_, R> {
self.lock();
ElementChildIterator::new(self)
}
pub(crate) fn skip(&mut self, length: u64) -> Result<()> { pub(crate) fn skip(&mut self, length: u64) -> Result<()> {
std::io::copy(&mut self.by_ref().take(length), &mut std::io::sink())?; std::io::copy(&mut self.by_ref().take(length), &mut std::io::sink())?;
Ok(()) Ok(())
} }
pub(crate) fn skip_element(&mut self, element_header: ElementHeader) -> Result<()> {
log::debug!(
"Encountered unknown EBML element in header: {:X}",
element_header.id.0
);
self.skip(element_header.size.value())?;
Ok(())
}
pub(crate) fn read_signed_int(&mut self, element_length: u64) -> Result<i64> { pub(crate) fn read_signed_int(&mut self, element_length: u64) -> Result<i64> {
// https://www.rfc-editor.org/rfc/rfc8794.html#section-7.1 // https://www.rfc-editor.org/rfc/rfc8794.html#section-7.1
// A Signed Integer Element MUST declare a length from zero to eight octets // A Signed Integer Element MUST declare a length from zero to eight octets
@ -390,3 +408,68 @@ where
todo!() todo!()
} }
} }
pub(crate) struct ElementChildIterator<'a, R>
where
R: Read,
{
reader: &'a mut ElementReader<R>,
}
impl<'a, R> ElementChildIterator<'a, R>
where
R: Read,
{
pub(crate) fn new(reader: &'a mut ElementReader<R>) -> Self {
Self { reader }
}
pub(crate) fn next(&mut self) -> Result<Option<ElementReaderYield>> {
match self.reader.next() {
Ok(ElementReaderYield::Unknown(header)) => {
self.reader.skip_element(header)?;
self.next()
},
Ok(ElementReaderYield::Eof) => Ok(None),
Err(e) => Err(e),
element => element.map(Some),
}
}
pub(crate) fn master_exhausted(&self) -> bool {
self.reader.ctx.master_length == 0
}
pub(crate) fn inner(&mut self) -> &mut ElementReader<R> {
self.reader
}
}
impl<'a, R> Deref for ElementChildIterator<'a, R>
where
R: Read,
{
type Target = ElementReader<R>;
fn deref(&self) -> &Self::Target {
self.reader
}
}
impl<'a, R> DerefMut for ElementChildIterator<'a, R>
where
R: Read,
{
fn deref_mut(&mut self) -> &mut Self::Target {
self.reader
}
}
impl<'a, R> Drop for ElementChildIterator<'a, R>
where
R: Read,
{
fn drop(&mut self) {
self.reader.unlock();
}
}

View file

@ -69,15 +69,13 @@ where
Err(e) => return Err(e), Err(e) => return Err(e),
} }
element_reader.lock(); let mut child_reader = element_reader.children();
while let Some(child) = child_reader.next()? {
loop {
let ident; let ident;
let data_ty; let data_ty;
let size; let size;
let res = element_reader.next()?; match child {
match res {
// The only expected master element in the header is `DocTypeExtension` // The only expected master element in the header is `DocTypeExtension`
ElementReaderYield::Master((ElementIdent::DocTypeExtension, _)) => continue, ElementReaderYield::Master((ElementIdent::DocTypeExtension, _)) => continue,
ElementReaderYield::Child((child, size_)) => { ElementReaderYield::Child((child, size_)) => {
@ -85,53 +83,46 @@ where
data_ty = child.data_type; data_ty = child.data_type;
size = size_; size = size_;
}, },
ElementReaderYield::Unknown(element) => {
log::debug!(
"Encountered unknown EBML element in header: {:X}",
element.id.0
);
element_reader.skip(element.size.value())?;
continue;
},
_ => break, _ => break,
} }
if ident == ElementIdent::EBMLMaxIDLength { if ident == ElementIdent::EBMLMaxIDLength {
properties.header.max_id_length = element_reader.read_unsigned_int(size)? as u8; properties.header.max_id_length = child_reader.read_unsigned_int(size)? as u8;
element_reader.set_max_id_length(properties.header.max_id_length); child_reader.set_max_id_length(properties.header.max_id_length);
continue; continue;
} }
if ident == ElementIdent::EBMLMaxSizeLength { if ident == ElementIdent::EBMLMaxSizeLength {
properties.header.max_size_length = element_reader.read_unsigned_int(size)? as u8; properties.header.max_size_length = child_reader.read_unsigned_int(size)? as u8;
element_reader.set_max_size_length(properties.header.max_size_length); child_reader.set_max_size_length(properties.header.max_size_length);
continue; continue;
} }
// Anything else in the header is unnecessary, and only read for the properties // Anything else in the header is unnecessary, and only read for the properties
// struct // struct
if !parse_options.read_properties { if !parse_options.read_properties {
element_reader.skip(size)?; child_reader.skip(size)?;
continue; continue;
} }
match ident { match ident {
ElementIdent::EBMLVersion => { ElementIdent::EBMLVersion => {
properties.header.version = element_reader.read_unsigned_int(size)? properties.header.version = child_reader.read_unsigned_int(size)?
}, },
ElementIdent::EBMLReadVersion => { ElementIdent::EBMLReadVersion => {
properties.header.read_version = element_reader.read_unsigned_int(size)? properties.header.read_version = child_reader.read_unsigned_int(size)?
},
ElementIdent::DocType => {
properties.header.doc_type = element_reader.read_string(size)?
}, },
ElementIdent::DocType => properties.header.doc_type = child_reader.read_string(size)?,
ElementIdent::DocTypeVersion => { ElementIdent::DocTypeVersion => {
properties.header.doc_type_version = element_reader.read_unsigned_int(size)? properties.header.doc_type_version = child_reader.read_unsigned_int(size)?
}, },
_ => element_reader.skip(size)?, _ => child_reader.skip(size)?,
} }
} }
element_reader.unlock(); debug_assert!(
child_reader.master_exhausted(),
"There should be no remaining elements in the header"
);
Ok(()) Ok(())
} }

View file

@ -16,16 +16,14 @@ pub(super) fn read_from<R>(
where where
R: Read + Seek, R: Read + Seek,
{ {
element_reader.lock();
let mut tags = None; let mut tags = None;
loop { let mut children_reader = element_reader.children();
let res = element_reader.next()?; while let Some(child) = children_reader.next()? {
match res { match child {
ElementReaderYield::Master((id, size)) => match id { ElementReaderYield::Master((id, size)) => match id {
ElementIdent::Info => { ElementIdent::Info => {
segment_info::read_from(element_reader, parse_options, properties)? segment_info::read_from(children_reader.inner(), parse_options, properties)?
}, },
ElementIdent::Cluster => todo!("Support segment.Cluster"), ElementIdent::Cluster => todo!("Support segment.Cluster"),
ElementIdent::Tracks => todo!("Support segment.Tracks"), ElementIdent::Tracks => todo!("Support segment.Tracks"),
@ -37,23 +35,15 @@ where
// elements, so we can just skip any useless ones. // elements, so we can just skip any useless ones.
log::debug!("Skipping EBML master element: {:?}", id); log::debug!("Skipping EBML master element: {:?}", id);
element_reader.skip(size)?; children_reader.skip(size)?;
element_reader.goto_previous_master()?; children_reader.goto_previous_master()?;
continue; continue;
}, },
}, },
ElementReaderYield::Unknown(element) => { ElementReaderYield::Child(_) => {
log::debug!("Skipping unknown EBML element: {:X}", element.id.0);
element_reader.skip(element.size.value())?;
continue;
},
ElementReaderYield::Eof => {
element_reader.unlock();
break;
},
_ => {
decode_err!(@BAIL Ebml, "Segment element should only contain master elements") decode_err!(@BAIL Ebml, "Segment element should only contain master elements")
}, },
_ => break,
} }
} }

View file

@ -14,25 +14,24 @@ pub(super) fn read_from<R>(
where where
R: Read + Seek, R: Read + Seek,
{ {
element_reader.lock(); let mut children_reader = element_reader.children();
loop { while let Some(child) = children_reader.next()? {
let res = element_reader.next()?; match child {
match res {
ElementReaderYield::Master((id, size)) => { ElementReaderYield::Master((id, size)) => {
// We do not end up using information from any of the nested master // We do not end up using information from any of the nested master
// elements, so we can just skip them. // elements, so we can just skip them.
log::debug!("Skipping EBML master element: {:?}", id); log::debug!("Skipping EBML master element: {:?}", id);
element_reader.skip(size)?; children_reader.skip(size)?;
element_reader.goto_previous_master()?; children_reader.goto_previous_master()?;
continue; continue;
}, },
ElementReaderYield::Child((child, size)) => { ElementReaderYield::Child((child, size)) => {
match child.ident { match child.ident {
ElementIdent::TimecodeScale => { ElementIdent::TimecodeScale => {
properties.segment_info.timestamp_scale = properties.segment_info.timestamp_scale =
element_reader.read_unsigned_int(size)?; children_reader.read_unsigned_int(size)?;
if properties.segment_info.timestamp_scale == 0 { if properties.segment_info.timestamp_scale == 0 {
log::warn!("Segment.Info.TimecodeScale is 0, which is invalid"); log::warn!("Segment.Info.TimecodeScale is 0, which is invalid");
@ -42,33 +41,26 @@ where
} }
}, },
ElementIdent::MuxingApp => { ElementIdent::MuxingApp => {
properties.segment_info.muxing_app = element_reader.read_utf8(size)? properties.segment_info.muxing_app = children_reader.read_utf8(size)?
}, },
ElementIdent::WritingApp => { ElementIdent::WritingApp => {
properties.segment_info.writing_app = element_reader.read_utf8(size)? properties.segment_info.writing_app = children_reader.read_utf8(size)?
}, },
_ => { _ => {
// We do not end up using information from all of the segment // We do not end up using information from all of the segment
// elements, so we can just skip any useless ones. // elements, so we can just skip any useless ones.
log::debug!("Skipping EBML child element: {:?}", child.ident); log::debug!("Skipping EBML child element: {:?}", child.ident);
element_reader.skip(size)?; children_reader.skip(size)?;
continue; continue;
}, },
} }
}, },
ElementReaderYield::Unknown(element) => { _ => break,
log::debug!("Skipping unknown EBML element: {:X}", element.id.0);
element_reader.skip(element.size.value())?;
continue;
},
ElementReaderYield::Eof => {
element_reader.unlock();
break;
},
} }
} }
drop(children_reader);
element_reader.goto_previous_master()?; element_reader.goto_previous_master()?;
Ok(()) Ok(())
} }