Skip to content

Commit 74d9361

Browse files
committed
EBML: Parse unknown length VInts
1 parent d77a9c5 commit 74d9361

File tree

3 files changed

+88
-13
lines changed

3 files changed

+88
-13
lines changed

lofty/src/ebml/tag/write/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,13 @@ pub(crate) trait EbmlWriteExt: Write + Sized {
4343
}
4444

4545
fn write_size(&mut self, ctx: ElementWriterCtx, size: VInt<u64>) -> Result<()> {
46-
VInt::<u64>::write_to(size.value(), None, Some(ctx.max_size_len), self)?;
46+
VInt::<u64>::write_to(
47+
size.value(),
48+
None,
49+
Some(ctx.max_size_len),
50+
size.is_unknown(),
51+
self,
52+
)?;
4753
Ok(())
4854
}
4955
}

lofty/src/ebml/tag/write/type_encodings.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ impl ElementEncodable for VInt<u64> {
2020

2121
fn write_to<W: Write>(&self, ctx: ElementWriterCtx, writer: &mut W) -> Result<()> {
2222
writer.write_size(ctx, self.len()?)?;
23-
VInt::<u64>::write_to(self.value(), None, None, writer)?;
23+
VInt::<u64>::write_to(self.value(), None, None, self.is_unknown(), writer)?;
2424
Ok(())
2525
}
2626
}
@@ -32,7 +32,7 @@ impl ElementEncodable for VInt<i64> {
3232

3333
fn write_to<W: Write>(&self, ctx: ElementWriterCtx, writer: &mut W) -> Result<()> {
3434
writer.write_size(ctx, self.len()?)?;
35-
VInt::<i64>::write_to(self.value() as u64, None, None, writer)?;
35+
VInt::<i64>::write_to(self.value() as u64, None, None, self.is_unknown(), writer)?;
3636
Ok(())
3737
}
3838
}
@@ -59,7 +59,7 @@ impl ElementEncodable for f32 {
5959

6060
fn write_to<W: Write>(&self, ctx: ElementWriterCtx, writer: &mut W) -> Result<()> {
6161
if *self == 0.0 {
62-
VInt::<u64>::write_to(VInt::<u64>::ZERO.value(), None, None, writer)?;
62+
VInt::<u64>::write_to(VInt::<u64>::ZERO.value(), None, None, false, writer)?;
6363
return Ok(());
6464
}
6565

@@ -76,7 +76,7 @@ impl ElementEncodable for f64 {
7676

7777
fn write_to<W: Write>(&self, ctx: ElementWriterCtx, writer: &mut W) -> Result<()> {
7878
if *self == 0.0 {
79-
VInt::<u64>::write_to(VInt::<u64>::ZERO.value(), None, None, writer)?;
79+
VInt::<u64>::write_to(VInt::<u64>::ZERO.value(), None, None, false, writer)?;
8080
return Ok(());
8181
}
8282

lofty/src/ebml/vint.rs

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::error::Result;
22
use crate::macros::err;
3-
use std::fmt::UpperHex;
43

4+
use std::fmt::{Debug, Display, UpperHex};
55
use std::io::{Read, Write};
66
use std::ops::{Add, Sub};
77

@@ -19,6 +19,10 @@ macro_rules! impl_vint {
1919
pub const MIN: $t = <$t>::MIN;
2020
/// A `VInt` with a value of 0
2121
pub const ZERO: Self = Self(0);
22+
/// An unknown-sized `VInt`
23+
///
24+
/// See [`Self::is_unknown()`]
25+
pub const UNKNOWN: Self = Self(Self::ZERO.0 | 1 << (<$t>::BITS as u64) - 1);
2226

2327
/// Gets the inner value of the `VInt`
2428
///
@@ -32,10 +36,20 @@ macro_rules! impl_vint {
3236
/// assert_eq!(vint.value(), 2);
3337
/// # Ok(()) }
3438
/// ```
35-
pub fn value(&self) -> $t {
39+
#[inline]
40+
pub fn value(self) -> $t {
3641
self.0
3742
}
3843

44+
/// Whether this `VInt` represents an unknown size
45+
///
46+
/// Since EBML is built for streaming, elements can specify that their data length
47+
/// is unknown.
48+
#[inline]
49+
pub fn is_unknown(self) -> bool {
50+
self == Self::UNKNOWN
51+
}
52+
3953
/// Parse a `VInt` from a reader
4054
///
4155
/// `max_length` can be used to specify the maximum number of octets the number should
@@ -94,7 +108,8 @@ macro_rules! impl_vint {
94108
/// assert_eq!(vint.octet_length(), 3);
95109
/// # Ok(()) }
96110
/// ```
97-
pub fn octet_length(&self) -> u8 {
111+
#[inline]
112+
pub fn octet_length(self) -> u8 {
98113
octet_length(self.0 as u64)
99114
}
100115

@@ -123,15 +138,19 @@ macro_rules! impl_vint {
123138
/// assert_eq!(bytes, &[0b1000_1010]);
124139
/// # Ok(()) }
125140
/// ```
126-
pub fn as_bytes(&self, min_length: Option<u8>, max_length: Option<u8>) -> Result<Vec<u8>> {
141+
pub fn as_bytes(self, min_length: Option<u8>, max_length: Option<u8>) -> Result<Vec<u8>> {
127142
let mut ret = Vec::with_capacity(8);
128-
VInt::<$t>::write_to(self.0 as u64, min_length, max_length, &mut ret)?;
143+
VInt::<$t>::write_to(self.0 as u64, min_length, max_length, self.is_unknown(), &mut ret)?;
129144
Ok(ret)
130145
}
131146

132147
#[inline]
133148
#[allow(dead_code)]
134149
pub(crate) fn saturating_sub(self, other: $t) -> Self {
150+
if self.is_unknown() {
151+
return self;
152+
}
153+
135154
let v = self.0.saturating_sub(other);
136155
if v < Self::MIN {
137156
return Self(Self::MIN);
@@ -145,6 +164,10 @@ macro_rules! impl_vint {
145164
type Output = Self;
146165

147166
fn add(self, other: Self) -> Self::Output {
167+
if self.is_unknown() {
168+
return self;
169+
}
170+
148171
let val = self.0 + other.0;
149172
assert!(val <= Self::MAX, "VInt overflow");
150173

@@ -156,6 +179,10 @@ macro_rules! impl_vint {
156179
type Output = Self;
157180

158181
fn sub(self, other: Self) -> Self::Output {
182+
if self.is_unknown() {
183+
return self;
184+
}
185+
159186
Self(self.0 - other.0)
160187
}
161188
}
@@ -177,6 +204,18 @@ macro_rules! impl_vint {
177204
Ok(Self(value))
178205
}
179206
}
207+
208+
impl Debug for VInt<$t> {
209+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
210+
let mut debug = f.debug_tuple("VInt");
211+
if self.is_unknown() {
212+
debug.field(&"<unknown>");
213+
} else {
214+
debug.field(&self.0);
215+
}
216+
debug.finish()
217+
}
218+
}
180219
}
181220
)*
182221
};
@@ -188,7 +227,7 @@ macro_rules! impl_vint {
188227
///
189228
/// To ensure safe construction of `VInt`s, users must create them through the `TryFrom` implementations or [`VInt::parse`].
190229
#[repr(transparent)]
191-
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
230+
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
192231
pub struct VInt<T>(pub(crate) T);
193232

194233
impl<T> VInt<T> {
@@ -201,6 +240,7 @@ impl<T> VInt<T> {
201240
mut value: u64,
202241
min_length: Option<u8>,
203242
max_length: Option<u8>,
243+
unknown: bool,
204244
writer: &mut W,
205245
) -> Result<()>
206246
where
@@ -214,6 +254,15 @@ impl<T> VInt<T> {
214254
// Add the octet length
215255
value |= 1 << (octets * (Self::USABLE_BITS_PER_BYTE as u8));
216256

257+
// All VINT_DATA bits set to one
258+
if unknown {
259+
for _ in 0..octets {
260+
writer.write_u8(u8::MAX)?;
261+
}
262+
263+
return Ok(());
264+
}
265+
217266
let mut byte_shift = (octets - 1) as i8;
218267
while byte_shift >= 0 {
219268
writer.write_u8((value >> (byte_shift * 8)) as u8)?;
@@ -224,6 +273,15 @@ impl<T> VInt<T> {
224273
}
225274
}
226275

276+
impl<T> Display for VInt<T>
277+
where
278+
T: Display,
279+
{
280+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
281+
write!(f, "{}", self.0)
282+
}
283+
}
284+
227285
impl_vint!(u64, i64);
228286

229287
fn parse_vint<R>(reader: &mut R, max_length: u8, retain_marker: bool) -> Result<(u64, u8)>
@@ -245,6 +303,11 @@ where
245303
val = (val << 8) | u64::from(reader.read_u8()?);
246304
}
247305

306+
// Special case for unknown VInts (all data bits set to one)
307+
if val + 1 == 1 << (7 * bytes_read) {
308+
return Ok((VInt::<u64>::UNKNOWN.0, bytes_read));
309+
}
310+
248311
Ok((val, bytes_read))
249312
}
250313

@@ -283,7 +346,7 @@ fn octet_length(mut value: u64) -> u8 {
283346
///
284347
/// * The `VINT_MARKER` is retained after parsing
285348
/// * When encoding, the minimum number of octets must be used
286-
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
349+
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
287350
pub struct ElementId(pub(crate) u64);
288351

289352
impl ElementId {
@@ -364,7 +427,7 @@ impl ElementId {
364427
pub(crate) fn write_to<W: Write>(self, max_length: Option<u8>, writer: &mut W) -> Result<()> {
365428
let mut val = self.0;
366429
val ^= 1 << val.ilog2();
367-
VInt::<()>::write_to(val, None, max_length, writer)?;
430+
VInt::<()>::write_to(val, None, max_length, false, writer)?;
368431
Ok(())
369432
}
370433
}
@@ -381,6 +444,12 @@ impl UpperHex for ElementId {
381444
}
382445
}
383446

447+
impl Debug for ElementId {
448+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
449+
write!(f, "ElementId({:#X})", self.0)
450+
}
451+
}
452+
384453
#[cfg(test)]
385454
mod tests {
386455
use crate::ebml::VInt;

0 commit comments

Comments
 (0)