Skip to content

Commit

Permalink
EBML: Start defining structure for writes
Browse files Browse the repository at this point in the history
  • Loading branch information
Serial-ATA committed Sep 17, 2024
1 parent 81fc15d commit a6669bf
Show file tree
Hide file tree
Showing 16 changed files with 695 additions and 240 deletions.
26 changes: 13 additions & 13 deletions lofty/src/ebml/element_reader.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::ebml::vint::VInt;
use crate::ebml::vint::{ElementId, VInt};
use crate::error::Result;
use crate::macros::{decode_err, try_vec};

Expand All @@ -10,8 +10,8 @@ use lofty_attr::ebml_master_elements;

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct ElementHeader {
pub(crate) id: VInt,
pub(crate) size: VInt,
pub(crate) id: ElementId,
pub(crate) size: VInt<u64>,
}

impl ElementHeader {
Expand All @@ -20,8 +20,8 @@ impl ElementHeader {
R: Read,
{
Ok(Self {
id: VInt::parse_from_element_id(reader, max_id_length)?,
size: VInt::parse(reader, max_vint_length)?,
id: ElementId::parse(reader, max_id_length)?,
size: VInt::<u64>::parse(reader, max_vint_length)?,
})
}
}
Expand All @@ -41,7 +41,7 @@ pub enum ElementDataType {
#[derive(Copy, Clone, Debug)]
struct MasterElement {
id: ElementIdent,
children: &'static [(VInt, ChildElementDescriptor)],
children: &'static [(ElementId, ChildElementDescriptor)],
}

#[derive(Copy, Clone, Debug)]
Expand Down Expand Up @@ -247,7 +247,7 @@ const ROOT_DEPTH: u8 = 1;
#[derive(Copy, Clone, Debug)]
struct Depth {
level: u8,
length: VInt,
length: VInt<u64>,
}

#[derive(Copy, Clone, Debug)]
Expand Down Expand Up @@ -302,7 +302,7 @@ impl ElementReaderContext {
self.masters.get((self.depth - 1) as usize).copied()
}

fn current_master_length(&self) -> VInt {
fn current_master_length(&self) -> VInt<u64> {
assert!(self.depth > 0);
self.current_master()
.expect("should have current master element")
Expand All @@ -316,7 +316,7 @@ impl ElementReaderContext {
}
}

fn remaining_lock_length(&self) -> VInt {
fn remaining_lock_length(&self) -> VInt<u64> {
assert!(self.locked && !self.lock_depths.is_empty());

let lock_depth = *self.lock_depths.last().unwrap();
Expand All @@ -326,8 +326,8 @@ impl ElementReaderContext {

#[derive(Debug)]
pub(crate) enum ElementReaderYield {
Master((ElementIdent, VInt)),
Child((ChildElementDescriptor, VInt)),
Master((ElementIdent, VInt<u64>)),
Child((ChildElementDescriptor, VInt<u64>)),
Unknown(ElementHeader),
Eof,
}
Expand Down Expand Up @@ -412,7 +412,7 @@ where
self.ctx.max_size_length = len
}

fn push_new_master(&mut self, master: MasterElement, size: VInt) -> Result<()> {
fn push_new_master(&mut self, master: MasterElement, size: VInt<u64>) -> Result<()> {
log::debug!("New master element: {:?}", master.id);

if self.ctx.depth == MAX_DEPTH {
Expand Down Expand Up @@ -662,7 +662,7 @@ where
// https://www.rfc-editor.org/rfc/rfc8794.html#section-7.8
// A Binary Element MUST declare a length in octets from zero to VINTMAX.

if element_length > VInt::MAX {
if element_length > VInt::<u64>::MAX {
decode_err!(@BAIL Ebml, "Binary element length is too large")
}

Expand Down
2 changes: 1 addition & 1 deletion lofty/src/ebml/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use lofty_attr::LoftyFile;

pub use properties::EbmlProperties;
pub use tag::*;
pub use vint::VInt;
pub use vint::*;

/// An EBML file
#[derive(LoftyFile, Default)]
Expand Down
7 changes: 5 additions & 2 deletions lofty/src/ebml/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ mod segment_tracks;
use super::EbmlFile;
use crate::config::ParseOptions;
use crate::ebml::element_reader::{ElementHeader, ElementIdent, ElementReader, ElementReaderYield};
use crate::ebml::vint::VInt;
use crate::ebml::vint::ElementId;
use crate::ebml::EbmlProperties;
use crate::error::Result;
use crate::macros::decode_err;
Expand All @@ -18,6 +18,9 @@ use std::io::{Read, Seek};

const SUPPORTED_DOC_TYPES: &[&str] = &["matroska", "webm"];

const CRC32_ID: ElementId = ElementId(0xBF);
const VOID_ID: ElementId = ElementId(0xEC);

pub(super) fn read_from<R>(reader: &mut R, parse_options: ParseOptions) -> Result<EbmlFile>
where
R: Read + Seek,
Expand Down Expand Up @@ -45,7 +48,7 @@ where
// CRC-32 (0xBF) and Void (0xEC) elements can occur at the top level.
// This is valid, and we can just skip them.
ElementReaderYield::Unknown(ElementHeader {
id: VInt(id @ (0xBF | 0xEC)),
id: id @ (CRC32_ID | VOID_ID),
size,
}) => {
log::debug!("Skipping global element: {:X}", id);
Expand Down
4 changes: 2 additions & 2 deletions lofty/src/ebml/read/segment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::config::ParseOptions;
use crate::ebml::element_reader::{ElementHeader, ElementIdent, ElementReader, ElementReaderYield};
use crate::ebml::properties::EbmlProperties;
use crate::ebml::tag::EbmlTag;
use crate::ebml::VInt;
use crate::ebml::ElementId;
use crate::error::Result;

use std::io::{Read, Seek};
Expand Down Expand Up @@ -72,7 +72,7 @@ where
// elements, so we can just skip any useless ones.

children_reader.skip_element(ElementHeader {
id: VInt(id as u64),
id: ElementId(id as u64),
size,
})?;
},
Expand Down
2 changes: 1 addition & 1 deletion lofty/src/ebml/read/segment_attachments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ where
uid = Some(children_reader.read_unsigned_int(size)?);
},
ElementIdent::FileReferral => {
referral = Some(children_reader.read_string(size)?);
referral = Some(children_reader.read_binary(size)?);
},
ElementIdent::FileUsedStartTime => {
used_start_time = Some(children_reader.read_unsigned_int(size)?);
Expand Down
4 changes: 2 additions & 2 deletions lofty/src/ebml/read/segment_tracks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::io::{Read, Seek};
pub(super) fn read_from<R>(
children_reader: &mut ElementChildIterator<'_, R>,
parse_options: ParseOptions,
properties: &mut EbmlProperties,
_properties: &mut EbmlProperties,
) -> Result<()>
where
R: Read + Seek,
Expand Down Expand Up @@ -44,7 +44,7 @@ const AUDIO_TRACK_TYPE: u64 = 2;

fn read_track_entry<R>(
children_reader: &mut ElementChildIterator<'_, R>,
parse_options: ParseOptions,
_parse_options: ParseOptions,
audio_tracks: &mut Vec<AudioTrack>,
) -> Result<()>
where
Expand Down
2 changes: 1 addition & 1 deletion lofty/src/ebml/tag/attached_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub struct AttachedFile {
/// Unique ID representing the file, as random as possible.
pub uid: u64,
/// A binary value that a track/codec can refer to when the attachment is needed.
pub referral: Option<String>,
pub referral: Option<Vec<u8>>,
/// The timestamp at which this optimized font attachment comes into context.
///
/// This is expressed in Segment Ticks which is based on `TimestampScale`. This element is
Expand Down
13 changes: 13 additions & 0 deletions lofty/src/ebml/tag/target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,16 @@ impl From<TargetType> for Target {
}
}
}

impl Target {
// TargetType::Album is the default value. If nothing else is set, it is valid to write
// a zero-sized Targets element.
pub(super) fn is_empty_candidate(&self) -> bool {
self.target_type == TargetType::Album
&& self.name.is_none()
&& self.track_uids.is_none()
&& self.edition_uids.is_none()
&& self.chapter_uids.is_none()
&& self.attachment_uids.is_none()
}
}
81 changes: 81 additions & 0 deletions lofty/src/ebml/tag/write/elements/attached_file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use crate::ebml::tag::write::{write_element, ElementWriterCtx, WriteableElement};
use crate::ebml::{AttachedFile, ElementId, VInt};
use crate::io::FileLike;

const FileDescription_ID: ElementId = ElementId(0x467E);
const FileName_ID: ElementId = ElementId(0x466E);
const FileMediaType_ID: ElementId = ElementId(0x4660);
const FileData_ID: ElementId = ElementId(0x465C);
const FileUID_ID: ElementId = ElementId(0x46AE);
const FileReferral_ID: ElementId = ElementId(0x4675);
const FileUsedStartTime_ID: ElementId = ElementId(0x4661);
const FileUsedEndTime_ID: ElementId = ElementId(0x4662);

impl WriteableElement for AttachedFile {
const ID: ElementId = ElementId(0x61A7);

fn write_element<F: FileLike>(
&self,
ctx: ElementWriterCtx,
writer: &mut F,
) -> crate::error::Result<()> {
self.validate()?;

let mut element_children = Vec::new();
if let Some(description) = &self.description {
write_element(
ctx,
FileDescription_ID,
&description.as_str(),
&mut element_children,
)?;
}

write_element(
ctx,
FileName_ID,
&self.file_name.as_str(),
&mut element_children,
)?;

write_element(
ctx,
FileMediaType_ID,
&self.mime_type.as_str(),
&mut element_children,
)?;

write_element(
ctx,
FileData_ID,
&self.file_data.as_slice(),
&mut element_children,
)?;

let uid = VInt::<u64>::try_from(self.uid)?;
write_element(ctx, FileUID_ID, &uid, &mut element_children)?;

if let Some(referral) = &self.referral {
write_element(
ctx,
FileReferral_ID,
&referral.as_slice(),
&mut element_children,
)?;
}

if let Some(start_time) = &self.used_start_time {
let vint = VInt::<u64>::try_from(*start_time)?;
write_element(ctx, FileUsedStartTime_ID, &vint, &mut element_children)?;
}

if let Some(end_time) = &self.used_end_time {
let vint = VInt::<u64>::try_from(*end_time)?;
write_element(ctx, FileUsedEndTime_ID, &vint, &mut element_children)?;
}

write_element(ctx, Self::ID, &element_children.as_slice(), writer)?;

Ok(())
}
}
2 changes: 2 additions & 0 deletions lofty/src/ebml/tag/write/elements/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub(super) mod attached_file;
pub(super) mod target;
102 changes: 102 additions & 0 deletions lofty/src/ebml/tag/write/elements/target.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use crate::ebml::tag::write::{write_element, EbmlWriteExt, ElementWriterCtx, WriteableElement};
use crate::ebml::{ElementId, Target, TargetType, VInt};
use crate::io::FileLike;

const TargetTypeValue_ID: ElementId = ElementId(0x68CA);
const TargetType_ID: ElementId = ElementId(0x63CA);
const TagTrackUID_ID: ElementId = ElementId(0x63C5);
const TagEditionUID_ID: ElementId = ElementId(0x63C9);
const TagChapterUID_ID: ElementId = ElementId(0x63C4);
const TagAttachmentUID_ID: ElementId = ElementId(0x63C6);

impl WriteableElement for Target {
const ID: ElementId = ElementId(0x63C0);

fn write_element<F: FileLike>(
&self,
ctx: ElementWriterCtx,
writer: &mut F,
) -> crate::error::Result<()> {
if self.is_empty_candidate() {
writer.write_id(ctx, Self::ID)?;
writer.write_size(ctx, VInt::<u64>::ZERO)?;
return Ok(());
}

let mut element_children = Vec::new();
if self.target_type == TargetType::Album {
write_element(
ctx,
TargetTypeValue_ID,
&[].as_slice(),
&mut element_children,
)?;
} else {
let vint = VInt::<u64>::try_from(self.target_type as u64)?;
write_element(ctx, TargetTypeValue_ID, &vint, &mut element_children)?;
}

if let Some(name) = &self.name {
write_element(ctx, TargetType_ID, &name.as_str(), &mut element_children)?;
}

if let Some(track_uids) = &self.track_uids {
for &uid in track_uids {
let vint = VInt::<u64>::try_from(uid)?;
write_element(ctx, TagTrackUID_ID, &vint, &mut element_children)?;
}
}

if let Some(edition_uids) = &self.edition_uids {
for &uid in edition_uids {
let vint = VInt::<u64>::try_from(uid)?;
write_element(ctx, TagEditionUID_ID, &vint, &mut element_children)?;
}
}

if let Some(chapter_uids) = &self.chapter_uids {
for &uid in chapter_uids {
let vint = VInt::<u64>::try_from(uid)?;
write_element(ctx, TagChapterUID_ID, &vint, &mut element_children)?;
}
}

if let Some(attachment_uids) = &self.attachment_uids {
for &uid in attachment_uids {
let vint = VInt::<u64>::try_from(uid)?;
write_element(ctx, TagAttachmentUID_ID, &vint, &mut element_children)?;
}
}

write_element(ctx, Self::ID, &element_children.as_slice(), writer)?;

Ok(())
}
}

#[cfg(test)]
mod tests {
use super::*;

use std::io::Cursor;

#[test_log::test]
fn write_empty_default() {
let target = Target::default();

let mut buf = Cursor::new(Vec::new());
target
.write_element(
ElementWriterCtx {
max_id_len: 4,
max_size_len: 8,
},
&mut buf,
)
.unwrap();

let expected = vec![0x63, 0xC0, 0x80];

assert_eq!(buf.into_inner(), expected);
}
}
Loading

0 comments on commit a6669bf

Please sign in to comment.