mirror of
https://github.com/Serial-ATA/lofty-rs
synced 2024-12-12 13:42:34 +00:00
EBML: Stop requiring explicit locking of reader
Signed-off-by: Serial <69764315+Serial-ATA@users.noreply.github.com>
This commit is contained in:
parent
daef1a2b4a
commit
f5f52ac07f
4 changed files with 131 additions and 75 deletions
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue