Skip to content

Commit 66c0544

Browse files
committed
EBML: Support locking at multiple depths
1 parent e641666 commit 66c0544

8 files changed

+219
-141
lines changed

lofty/src/ebml/element_reader.rs

Lines changed: 82 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ ebml_master_elements! {
107107
Segment: {
108108
id: 0x1853_8067,
109109
children: [
110-
SeekHead: { 0x114D_9B74, Master },
110+
// SeekHead: { 0x114D_9B74, Master },
111111
Info: { 0x1549_A966, Master },
112112
Cluster: { 0x1F43_B675, Master },
113113
Tracks: { 0x1654_AE6B, Master },
@@ -118,12 +118,12 @@ ebml_master_elements! {
118118
},
119119

120120
// segment.seekHead
121-
SeekHead: {
122-
id: 0x114D_9B74,
123-
children: [
124-
Seek: { 0x4DBB, Master },
125-
],
126-
},
121+
// SeekHead: {
122+
// id: 0x114D_9B74,
123+
// children: [
124+
// Seek: { 0x4DBB, Master },
125+
// ],
126+
// },
127127

128128
// segment.info
129129
Info: {
@@ -190,6 +190,33 @@ ebml_master_elements! {
190190
],
191191
},
192192

193+
// segment.tags.tag.targets
194+
Targets: {
195+
id: 0x63C0,
196+
children: [
197+
TargetTypeValue: { 0x68CA, UnsignedInt },
198+
TargetType: { 0x63CA, String },
199+
TagTrackUID: { 0x63C5, UnsignedInt },
200+
TagEditionUID: { 0x63C9, UnsignedInt },
201+
TagChapterUID: { 0x63C4, UnsignedInt },
202+
TagAttachmentUID: { 0x63C6, UnsignedInt },
203+
],
204+
},
205+
206+
// segment.tags.tag.simpleTag
207+
SimpleTag: {
208+
id: 0x67C8,
209+
children: [
210+
TagName: { 0x45A3, Utf8 },
211+
TagLanguage: { 0x447A, String },
212+
TagLanguageBCP47: { 0x447B, String },
213+
TagDefault: { 0x4484, UnsignedInt },
214+
TagDefaultBogus: { 0x44B4, UnsignedInt },
215+
TagString: { 0x4487, Utf8 },
216+
TagBinary: { 0x4485, Binary },
217+
],
218+
},
219+
193220
// segment.attachments
194221
Attachments: {
195222
id: 0x1941_A469,
@@ -235,8 +262,13 @@ struct ElementReaderContext {
235262
/// This is set with [`ElementReader::lock`], and is used to prevent
236263
/// the reader from reading past the end of the current master element.
237264
locked: bool,
238-
/// The depth at which we are locked to
239-
lock_depth: u8,
265+
/// The depths at which we are locked
266+
///
267+
/// When we reach the end of one lock and unlock the reader, we need
268+
/// to know which depth to lock the reader at again (if any).
269+
///
270+
/// This will **always** be sorted, so the current lock will be at the end.
271+
lock_depths: Vec<u8>,
240272
lock_len: VInt,
241273
}
242274

@@ -250,7 +282,7 @@ impl Default for ElementReaderContext {
250282
// https://www.rfc-editor.org/rfc/rfc8794.html#name-ebmlmaxsizelength-element
251283
max_size_length: 8,
252284
locked: false,
253-
lock_depth: 0,
285+
lock_depths: Vec::with_capacity(MAX_DEPTH as usize),
254286
lock_len: VInt::ZERO,
255287
}
256288
}
@@ -285,6 +317,7 @@ impl ElementReaderYield {
285317
}
286318
}
287319

320+
/// An EBML element reader.
288321
pub struct ElementReader<R> {
289322
reader: R,
290323
ctx: ElementReaderContext,
@@ -308,6 +341,11 @@ where
308341
let ret = self.reader.read(buf)?;
309342
let len = self.current_master_length();
310343
self.set_current_master_length(len.saturating_sub(ret as u64));
344+
345+
if self.ctx.locked {
346+
self.ctx.lock_len = self.ctx.lock_len.saturating_sub(ret as u64);
347+
}
348+
311349
Ok(ret)
312350
}
313351
}
@@ -355,10 +393,6 @@ where
355393
return;
356394
}
357395

358-
if self.ctx.locked {
359-
self.ctx.lock_len = length;
360-
}
361-
362396
self.ctx.masters[(self.ctx.depth - 1) as usize].remaining_length = length;
363397
}
364398

@@ -405,7 +439,7 @@ where
405439
}
406440

407441
fn goto_previous_master(&mut self) -> Result<()> {
408-
if self.ctx.depth == 0 || self.ctx.depth == self.ctx.lock_depth {
442+
if self.ctx.depth == 0 || self.ctx.lock_depths.last() == Some(&self.ctx.depth) {
409443
decode_err!(@BAIL Ebml, "Cannot go to previous master element, already at root")
410444
}
411445

@@ -468,10 +502,22 @@ where
468502
pub(crate) fn lock(&mut self) {
469503
self.ctx.locked = true;
470504
self.ctx.lock_len = self.current_master_length();
505+
self.ctx.lock_depths.push(self.ctx.depth);
471506
}
472507

473508
pub(crate) fn unlock(&mut self) {
474-
self.ctx.locked = false;
509+
let _ = self.ctx.lock_depths.pop();
510+
511+
let [.., last] = &*self.ctx.lock_depths else {
512+
// We can only ever *truly* unlock if we are at the root level.
513+
log::trace!("Lock freed");
514+
515+
self.ctx.locked = false;
516+
return;
517+
};
518+
519+
log::trace!("Moving lock to depth: {}", last);
520+
self.ctx.lock_len = self.ctx.masters[(*last - 1) as usize].remaining_length;
475521
}
476522

477523
pub(crate) fn children(&mut self) -> ElementChildIterator<'_, R> {
@@ -595,6 +641,17 @@ where
595641
}
596642
}
597643

644+
/// An iterator over the children of an EBML master element.
645+
///
646+
/// This is created by calling [`ElementReader::children`].
647+
///
648+
/// This is essentially a fancy wrapper around `ElementReader` that:
649+
///
650+
/// * Automatically skips unknown elements ([`ElementReaderYield::Unknown`]).
651+
/// * [`Deref`]s to `ElementReader` so you can access the reader's methods.
652+
/// * Unlocks the reader when dropped.
653+
/// * If the reader is locked at multiple depths (meaning [`ElementReader::children`] was called
654+
/// multiple times), it will move the lock to the previously locked depth.
598655
pub(crate) struct ElementChildIterator<'a, R>
599656
where
600657
R: Read,
@@ -622,10 +679,15 @@ where
622679
}
623680

624681
pub(crate) fn master_exhausted(&self) -> bool {
625-
let lock_depth = self.reader.ctx.lock_depth;
626-
assert!(lock_depth < self.reader.ctx.depth);
627-
628-
self.reader.ctx.masters[lock_depth as usize].remaining_length == 0
682+
let lock_depth = *self
683+
.reader
684+
.ctx
685+
.lock_depths
686+
.last()
687+
.expect("a child iterator should always have a lock depth");
688+
assert!(lock_depth <= self.reader.ctx.depth);
689+
690+
self.reader.ctx.masters[(lock_depth - 1) as usize].remaining_length == 0
629691
}
630692
}
631693

lofty/src/ebml/read/segment.rs

Lines changed: 56 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
1-
use super::{
2-
segment_attachments, segment_chapters, segment_cluster, segment_info, segment_tags,
3-
segment_tracks,
4-
};
1+
use super::{segment_attachments, segment_cluster, segment_info, segment_tags, segment_tracks};
52
use crate::config::ParseOptions;
63
use crate::ebml::element_reader::{ElementHeader, ElementIdent, ElementReader, ElementReaderYield};
74
use crate::ebml::properties::EbmlProperties;
85
use crate::ebml::tag::EbmlTag;
96
use crate::ebml::VInt;
107
use crate::error::Result;
11-
use crate::macros::decode_err;
128

139
use std::io::{Read, Seek};
1410

@@ -21,60 +17,69 @@ where
2117
R: Read + Seek,
2218
{
2319
let mut tags = None;
20+
let mut children_reader = element_reader.children();
2421

25-
element_reader.lock();
26-
27-
loop {
28-
let child = element_reader.next()?;
29-
22+
while let Some(child) = children_reader.next()? {
3023
match child {
31-
ElementReaderYield::Master((id, size)) => match id {
32-
ElementIdent::Info if parse_options.read_properties => {
33-
segment_info::read_from(element_reader, parse_options, properties)?
34-
},
35-
ElementIdent::Cluster if parse_options.read_properties => {
36-
segment_cluster::read_from(element_reader, parse_options, properties)?
37-
},
38-
ElementIdent::Tracks if parse_options.read_properties => {
39-
segment_tracks::read_from(element_reader, parse_options, properties)?
40-
},
41-
// TODO: ElementIdent::Chapters
42-
ElementIdent::Tags if parse_options.read_tags => {
43-
let mut tag = tags.unwrap_or_default();
24+
ElementReaderYield::Master((id, size)) => {
25+
match id {
26+
ElementIdent::Info if parse_options.read_properties => {
27+
segment_info::read_from(
28+
&mut children_reader.children(),
29+
parse_options,
30+
properties,
31+
)?;
32+
},
33+
ElementIdent::Cluster if parse_options.read_properties => {
34+
segment_cluster::read_from(
35+
&mut children_reader.children(),
36+
parse_options,
37+
properties,
38+
)?;
39+
},
40+
ElementIdent::Tracks if parse_options.read_properties => {
41+
segment_tracks::read_from(
42+
&mut children_reader.children(),
43+
parse_options,
44+
properties,
45+
)?;
46+
},
47+
// TODO: ElementIdent::Chapters
48+
ElementIdent::Tags if parse_options.read_tags => {
49+
let mut tag = tags.unwrap_or_default();
4450

45-
segment_tags::read_from(element_reader, parse_options, &mut tag)?;
51+
segment_tags::read_from(
52+
&mut children_reader.children(),
53+
parse_options,
54+
&mut tag,
55+
)?;
4656

47-
tags = Some(tag);
48-
},
49-
ElementIdent::Attachments if parse_options.read_cover_art => {
50-
let mut tag = tags.unwrap_or_default();
57+
tags = Some(tag);
58+
},
59+
ElementIdent::Attachments if parse_options.read_cover_art => {
60+
let mut tag = tags.unwrap_or_default();
5161

52-
segment_attachments::read_from(element_reader, parse_options, &mut tag)?;
62+
segment_attachments::read_from(
63+
&mut children_reader.children(),
64+
parse_options,
65+
&mut tag,
66+
)?;
5367

54-
tags = Some(tag);
55-
},
56-
_ => {
57-
// We do not end up using information from all of the segment
58-
// elements, so we can just skip any useless ones.
68+
tags = Some(tag);
69+
},
70+
_ => {
71+
// We do not end up using information from all of the segment
72+
// elements, so we can just skip any useless ones.
5973

60-
element_reader.skip_element(ElementHeader {
61-
id: VInt(id as u64),
62-
size,
63-
})?;
64-
continue;
65-
},
66-
},
67-
ElementReaderYield::Unknown(header) => {
68-
element_reader.skip_element(header)?;
69-
continue;
70-
},
71-
ElementReaderYield::Child(_) => {
72-
decode_err!(@BAIL Ebml, "Segment element should only contain master elements")
73-
},
74-
ElementReaderYield::Eof => {
75-
element_reader.unlock();
76-
break;
74+
children_reader.skip_element(ElementHeader {
75+
id: VInt(id as u64),
76+
size,
77+
})?;
78+
},
79+
}
7780
},
81+
ElementReaderYield::Eof => break,
82+
_ => unreachable!("Unhandled child element in \\Ebml\\Segment: {child:?}"),
7883
}
7984
}
8085

lofty/src/ebml/read/segment_attachments.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use crate::config::ParseOptions;
2-
use crate::ebml::element_reader::{ElementIdent, ElementReader, ElementReaderYield};
2+
use crate::ebml::element_reader::{
3+
ElementChildIterator, ElementIdent, ElementReader, ElementReaderYield,
4+
};
35
use crate::ebml::{AttachedFile, EbmlTag};
46
use crate::error::Result;
57
use crate::macros::decode_err;
@@ -8,19 +10,17 @@ use crate::picture::MimeType;
810
use std::io::{Read, Seek};
911

1012
pub(super) fn read_from<R>(
11-
element_reader: &mut ElementReader<R>,
13+
children_reader: &mut ElementChildIterator<'_, R>,
1214
_parse_options: ParseOptions,
1315
tag: &mut EbmlTag,
1416
) -> Result<()>
1517
where
1618
R: Read + Seek,
1719
{
18-
let mut children_reader = element_reader.children();
19-
2020
while let Some(child) = children_reader.next()? {
2121
match child {
2222
ElementReaderYield::Master((ElementIdent::AttachedFile, size)) => {
23-
let attached_file = read_attachment(&mut children_reader)?;
23+
let attached_file = read_attachment(children_reader)?;
2424
tag.attached_files.push(attached_file);
2525
},
2626
ElementReaderYield::Eof => break,

lofty/src/ebml/read/segment_chapters.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
use crate::config::ParseOptions;
2-
use crate::ebml::element_reader::ElementReader;
2+
use crate::ebml::element_reader::ElementChildIterator;
33
use crate::ebml::EbmlTag;
44
use crate::error::Result;
55

66
use std::io::{Read, Seek};
77

88
#[allow(dead_code)]
99
pub(super) fn read_from<R>(
10-
_element_reader: &mut ElementReader<R>,
10+
_children_reader: &mut ElementChildIterator<'_, R>,
1111
_parse_options: ParseOptions,
1212
_tag: &mut EbmlTag,
1313
) -> Result<()>

lofty/src/ebml/read/segment_cluster.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use crate::config::ParseOptions;
2-
use crate::ebml::element_reader::ElementReader;
2+
use crate::ebml::element_reader::ElementChildIterator;
33
use crate::ebml::properties::EbmlProperties;
44
use crate::error::Result;
55

66
use std::io::{Read, Seek};
77

88
pub(super) fn read_from<R>(
9-
_element_reader: &mut ElementReader<R>,
9+
_children_reader: &mut ElementChildIterator<'_, R>,
1010
_parse_options: ParseOptions,
1111
_properties: &mut EbmlProperties,
1212
) -> Result<()>

0 commit comments

Comments
 (0)