#![allow(clippy::missing_errors_doc)]
#![allow(clippy::unreadable_literal)]
#![allow(clippy::unusual_byte_groupings)]
use nom::bytes::complete::take;
use nom::IResult;
use oximedia_core::{OxiError, OxiResult};
pub mod element_id {
pub const EBML: u32 = 0x1A45_DFA3;
pub const EBML_VERSION: u32 = 0x4286;
pub const EBML_READ_VERSION: u32 = 0x42F7;
pub const EBML_MAX_ID_LENGTH: u32 = 0x42F2;
pub const EBML_MAX_SIZE_LENGTH: u32 = 0x42F3;
pub const DOC_TYPE: u32 = 0x4282;
pub const DOC_TYPE_VERSION: u32 = 0x4287;
pub const DOC_TYPE_READ_VERSION: u32 = 0x4285;
pub const SEGMENT: u32 = 0x1853_8067;
pub const SEEK_HEAD: u32 = 0x114D_9B74;
pub const SEEK: u32 = 0x4DBB;
pub const SEEK_ID: u32 = 0x53AB;
pub const SEEK_POSITION: u32 = 0x53AC;
pub const INFO: u32 = 0x1549_A966;
pub const TIMECODE_SCALE: u32 = 0x2AD7_B1;
pub const DURATION: u32 = 0x4489;
pub const TITLE: u32 = 0x7BA9;
pub const MUXING_APP: u32 = 0x4D80;
pub const WRITING_APP: u32 = 0x5741;
pub const DATE_UTC: u32 = 0x4461;
pub const SEGMENT_UID: u32 = 0x73A4;
pub const SEGMENT_FILENAME: u32 = 0x7384;
pub const PREV_UID: u32 = 0x3CB923;
pub const NEXT_UID: u32 = 0x3EB923;
pub const TRACKS: u32 = 0x1654_AE6B;
pub const TRACK_ENTRY: u32 = 0xAE;
pub const TRACK_NUMBER: u32 = 0xD7;
pub const TRACK_UID: u32 = 0x73C5;
pub const TRACK_TYPE: u32 = 0x83;
pub const FLAG_ENABLED: u32 = 0xB9;
pub const FLAG_DEFAULT: u32 = 0x88;
pub const FLAG_FORCED: u32 = 0x55AA;
pub const FLAG_LACING: u32 = 0x9C;
pub const MIN_CACHE: u32 = 0x6DE7;
pub const MAX_CACHE: u32 = 0x6DF8;
pub const DEFAULT_DURATION: u32 = 0x23E383;
pub const TRACK_TIMECODE_SCALE: u32 = 0x23314F;
pub const MAX_BLOCK_ADDITION_ID: u32 = 0x55EE;
pub const NAME: u32 = 0x536E;
pub const LANGUAGE: u32 = 0x22B5_9C;
pub const LANGUAGE_IETF: u32 = 0x22B5_9D;
pub const CODEC_ID: u32 = 0x86;
pub const CODEC_PRIVATE: u32 = 0x63A2;
pub const CODEC_NAME: u32 = 0x2586_88;
pub const CODEC_DELAY: u32 = 0x56AA;
pub const SEEK_PRE_ROLL: u32 = 0x56BB;
pub const ATTACHMENT_LINK: u32 = 0x7446;
pub const VIDEO: u32 = 0xE0;
pub const FLAG_INTERLACED: u32 = 0x9A;
pub const FIELD_ORDER: u32 = 0x9D;
pub const STEREO_MODE: u32 = 0x53B8;
pub const ALPHA_MODE: u32 = 0x53C0;
pub const PIXEL_WIDTH: u32 = 0xB0;
pub const PIXEL_HEIGHT: u32 = 0xBA;
pub const PIXEL_CROP_BOTTOM: u32 = 0x54AA;
pub const PIXEL_CROP_TOP: u32 = 0x54BB;
pub const PIXEL_CROP_LEFT: u32 = 0x54CC;
pub const PIXEL_CROP_RIGHT: u32 = 0x54DD;
pub const DISPLAY_WIDTH: u32 = 0x54B0;
pub const DISPLAY_HEIGHT: u32 = 0x54BA;
pub const DISPLAY_UNIT: u32 = 0x54B2;
pub const ASPECT_RATIO_TYPE: u32 = 0x54B3;
pub const COLOUR_SPACE: u32 = 0x2EB5_24;
pub const COLOUR: u32 = 0x55B0;
pub const MATRIX_COEFFICIENTS: u32 = 0x55B1;
pub const BITS_PER_CHANNEL: u32 = 0x55B2;
pub const CHROMA_SUBSAMPLING_HORZ: u32 = 0x55B3;
pub const CHROMA_SUBSAMPLING_VERT: u32 = 0x55B4;
pub const CB_SUBSAMPLING_HORZ: u32 = 0x55B5;
pub const CB_SUBSAMPLING_VERT: u32 = 0x55B6;
pub const CHROMA_SITING_HORZ: u32 = 0x55B7;
pub const CHROMA_SITING_VERT: u32 = 0x55B8;
pub const RANGE: u32 = 0x55B9;
pub const TRANSFER_CHARACTERISTICS: u32 = 0x55BA;
pub const PRIMARIES: u32 = 0x55BB;
pub const MAX_CLL: u32 = 0x55BC;
pub const MAX_FALL: u32 = 0x55BD;
pub const MASTERING_METADATA: u32 = 0x55D0;
pub const PRIMARY_R_CHROMATICITY_X: u32 = 0x55D1;
pub const PRIMARY_R_CHROMATICITY_Y: u32 = 0x55D2;
pub const PRIMARY_G_CHROMATICITY_X: u32 = 0x55D3;
pub const PRIMARY_G_CHROMATICITY_Y: u32 = 0x55D4;
pub const PRIMARY_B_CHROMATICITY_X: u32 = 0x55D5;
pub const PRIMARY_B_CHROMATICITY_Y: u32 = 0x55D6;
pub const WHITE_POINT_CHROMATICITY_X: u32 = 0x55D7;
pub const WHITE_POINT_CHROMATICITY_Y: u32 = 0x55D8;
pub const LUMINANCE_MAX: u32 = 0x55D9;
pub const LUMINANCE_MIN: u32 = 0x55DA;
pub const AUDIO: u32 = 0xE1;
pub const SAMPLING_FREQUENCY: u32 = 0xB5;
pub const OUTPUT_SAMPLING_FREQUENCY: u32 = 0x78B5;
pub const CHANNELS: u32 = 0x9F;
pub const BIT_DEPTH: u32 = 0x6264;
pub const CONTENT_ENCODINGS: u32 = 0x6D80;
pub const CONTENT_ENCODING: u32 = 0x6240;
pub const CONTENT_ENCODING_ORDER: u32 = 0x5031;
pub const CONTENT_ENCODING_SCOPE: u32 = 0x5032;
pub const CONTENT_ENCODING_TYPE: u32 = 0x5033;
pub const CONTENT_COMPRESSION: u32 = 0x5034;
pub const CONTENT_COMP_ALGO: u32 = 0x4254;
pub const CONTENT_COMP_SETTINGS: u32 = 0x4255;
pub const CLUSTER: u32 = 0x1F43_B675;
pub const TIMESTAMP: u32 = 0xE7;
pub const SILENT_TRACKS: u32 = 0x5854;
pub const SILENT_TRACK_NUMBER: u32 = 0x58D7;
pub const POSITION: u32 = 0xA7;
pub const PREV_SIZE: u32 = 0xAB;
pub const SIMPLE_BLOCK: u32 = 0xA3;
pub const BLOCK_GROUP: u32 = 0xA0;
pub const BLOCK: u32 = 0xA1;
pub const BLOCK_VIRTUAL: u32 = 0xA2;
pub const BLOCK_ADDITIONS: u32 = 0x75A1;
pub const BLOCK_MORE: u32 = 0xA6;
pub const BLOCK_ADD_ID: u32 = 0xEE;
pub const BLOCK_ADDITIONAL: u32 = 0xA5;
pub const BLOCK_DURATION: u32 = 0x9B;
pub const REFERENCE_PRIORITY: u32 = 0xFA;
pub const REFERENCE_BLOCK: u32 = 0xFB;
pub const REFERENCE_VIRTUAL: u32 = 0xFD;
pub const CODEC_STATE: u32 = 0xA4;
pub const DISCARD_PADDING: u32 = 0x75A2;
pub const CUES: u32 = 0x1C53_BB6B;
pub const CUE_POINT: u32 = 0xBB;
pub const CUE_TIME: u32 = 0xB3;
pub const CUE_TRACK_POSITIONS: u32 = 0xB7;
pub const CUE_TRACK: u32 = 0xF7;
pub const CUE_CLUSTER_POSITION: u32 = 0xF1;
pub const CUE_RELATIVE_POSITION: u32 = 0xF0;
pub const CUE_DURATION: u32 = 0xB2;
pub const CUE_BLOCK_NUMBER: u32 = 0x5378;
pub const CUE_CODEC_STATE: u32 = 0xEA;
pub const CUE_REFERENCE: u32 = 0xDB;
pub const CUE_REF_TIME: u32 = 0x96;
pub const ATTACHMENTS: u32 = 0x1941_A469;
pub const ATTACHED_FILE: u32 = 0x61A7;
pub const FILE_DESCRIPTION: u32 = 0x467E;
pub const FILE_NAME: u32 = 0x466E;
pub const FILE_MIME_TYPE: u32 = 0x4660;
pub const FILE_DATA: u32 = 0x465C;
pub const FILE_UID: u32 = 0x46AE;
pub const CHAPTERS: u32 = 0x1043_A770;
pub const EDITION_ENTRY: u32 = 0x45B9;
pub const EDITION_UID: u32 = 0x45BC;
pub const EDITION_FLAG_HIDDEN: u32 = 0x45BD;
pub const EDITION_FLAG_DEFAULT: u32 = 0x45DB;
pub const EDITION_FLAG_ORDERED: u32 = 0x45DD;
pub const CHAPTER_ATOM: u32 = 0xB6;
pub const CHAPTER_UID: u32 = 0x73C4;
pub const CHAPTER_STRING_UID: u32 = 0x5654;
pub const CHAPTER_TIME_START: u32 = 0x91;
pub const CHAPTER_TIME_END: u32 = 0x92;
pub const CHAPTER_FLAG_HIDDEN: u32 = 0x98;
pub const CHAPTER_FLAG_ENABLED: u32 = 0x4598;
pub const CHAPTER_SEGMENT_UID: u32 = 0x6E67;
pub const CHAPTER_SEGMENT_EDITION_UID: u32 = 0x6EBC;
pub const CHAPTER_PHYSICAL_EQUIV: u32 = 0x63C3;
pub const CHAPTER_TRACK: u32 = 0x8F;
pub const CHAPTER_TRACK_NUMBER: u32 = 0x89;
pub const CHAPTER_DISPLAY: u32 = 0x80;
pub const CHAP_STRING: u32 = 0x85;
pub const CHAP_LANGUAGE: u32 = 0x437C;
pub const CHAP_LANGUAGE_IETF: u32 = 0x437D;
pub const CHAP_COUNTRY: u32 = 0x437E;
pub const CHAPTER_PROCESS: u32 = 0x6944;
pub const CHAPTER_PROCESS_CODEC_ID: u32 = 0x6955;
pub const CHAPTER_PROCESS_PRIVATE: u32 = 0x450D;
pub const CHAPTER_PROCESS_COMMAND: u32 = 0x6911;
pub const CHAPTER_PROCESS_TIME: u32 = 0x6922;
pub const CHAPTER_PROCESS_DATA: u32 = 0x6933;
pub const TAGS: u32 = 0x1254_C367;
pub const TAG: u32 = 0x7373;
pub const TARGETS: u32 = 0x63C0;
pub const TARGET_TYPE_VALUE: u32 = 0x68CA;
pub const TARGET_TYPE: u32 = 0x63CA;
pub const TAG_TRACK_UID: u32 = 0x63C5;
pub const TAG_EDITION_UID: u32 = 0x63C9;
pub const TAG_CHAPTER_UID: u32 = 0x63C4;
pub const TAG_ATTACHMENT_UID: u32 = 0x63C6;
pub const SIMPLE_TAG: u32 = 0x67C8;
pub const TAG_NAME: u32 = 0x45A3;
pub const TAG_LANGUAGE: u32 = 0x447A;
pub const TAG_LANGUAGE_IETF: u32 = 0x447B;
pub const TAG_DEFAULT: u32 = 0x4484;
pub const TAG_STRING: u32 = 0x4487;
pub const TAG_BINARY: u32 = 0x4485;
pub const VOID: u32 = 0xEC;
pub const CRC32: u32 = 0xBF;
}
pub use element_id::EBML as EBML_HEADER;
pub use element_id::INFO as SEGMENT_INFO;
pub use element_id::{
ATTACHMENTS, BLOCK, BLOCK_DURATION, BLOCK_GROUP, CHAPTERS, CLUSTER, CODEC_ID, CODEC_PRIVATE,
CUES, DOC_TYPE, DOC_TYPE_READ_VERSION, DOC_TYPE_VERSION, EBML_MAX_ID_LENGTH,
EBML_MAX_SIZE_LENGTH, EBML_READ_VERSION, EBML_VERSION, SEGMENT, SIMPLE_BLOCK, TAGS, TIMESTAMP,
TRACKS, TRACK_ENTRY, TRACK_NUMBER, TRACK_TYPE, TRACK_UID,
};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct EbmlElement {
pub id: u32,
pub size: u64,
pub header_size: usize,
}
impl EbmlElement {
#[must_use]
pub const fn is_unbounded(&self) -> bool {
self.size == u64::MAX
}
#[must_use]
pub const fn total_size(&self) -> Option<u64> {
if self.is_unbounded() {
None
} else {
Some(self.header_size as u64 + self.size)
}
}
#[must_use]
pub const fn is_master(&self) -> bool {
matches!(
self.id,
element_id::EBML
| element_id::SEGMENT
| element_id::SEEK_HEAD
| element_id::SEEK
| element_id::INFO
| element_id::TRACKS
| element_id::TRACK_ENTRY
| element_id::VIDEO
| element_id::AUDIO
| element_id::COLOUR
| element_id::MASTERING_METADATA
| element_id::CONTENT_ENCODINGS
| element_id::CONTENT_ENCODING
| element_id::CONTENT_COMPRESSION
| element_id::CLUSTER
| element_id::BLOCK_GROUP
| element_id::BLOCK_ADDITIONS
| element_id::BLOCK_MORE
| element_id::CUES
| element_id::CUE_POINT
| element_id::CUE_TRACK_POSITIONS
| element_id::CUE_REFERENCE
| element_id::ATTACHMENTS
| element_id::ATTACHED_FILE
| element_id::CHAPTERS
| element_id::EDITION_ENTRY
| element_id::CHAPTER_ATOM
| element_id::CHAPTER_TRACK
| element_id::CHAPTER_DISPLAY
| element_id::CHAPTER_PROCESS
| element_id::CHAPTER_PROCESS_COMMAND
| element_id::TAGS
| element_id::TAG
| element_id::TARGETS
| element_id::SIMPLE_TAG
| element_id::SILENT_TRACKS
)
}
}
const VINT_LEN_TABLE: [u8; 256] = {
let mut table = [0u8; 256];
let mut i: usize = 1;
while i < 256 {
let lz = (i as u8).leading_zeros() as u8;
table[i] = lz + 1; i += 1;
}
table
};
const VINT_MASK_TABLE: [u8; 256] = {
let mut table = [0u8; 256];
let mut i: usize = 1;
while i < 256 {
let lz = (i as u8).leading_zeros() as u8; let mask = if lz >= 7 { 0u8 } else { (1u8 << (7 - lz)) - 1 };
table[i] = mask;
i += 1;
}
table
};
pub fn parse_vint(input: &[u8]) -> IResult<&[u8], u64> {
if input.is_empty() {
return Err(nom::Err::Incomplete(nom::Needed::new(1)));
}
let first = input[0];
let len = VINT_LEN_TABLE[first as usize] as usize;
if len == 0 {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Fail,
)));
}
if len > 8 {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Fail,
)));
}
if input.len() < len {
return Err(nom::Err::Incomplete(nom::Needed::new(len - input.len())));
}
let mask = VINT_MASK_TABLE[first as usize];
let mut value = u64::from(first & mask);
for byte in input.iter().take(len).skip(1) {
value = (value << 8) | u64::from(*byte);
}
Ok((&input[len..], value))
}
pub fn parse_element_id(input: &[u8]) -> IResult<&[u8], u32> {
if input.is_empty() {
return Err(nom::Err::Incomplete(nom::Needed::new(1)));
}
let first = input[0];
if first == 0 {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Fail,
)));
}
let len = first.leading_zeros() as usize + 1;
if len > 4 {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Fail,
)));
}
if input.len() < len {
return Err(nom::Err::Incomplete(nom::Needed::new(len - input.len())));
}
let mut id = u32::from(first);
for byte in input.iter().take(len).skip(1) {
id = (id << 8) | u32::from(*byte);
}
Ok((&input[len..], id))
}
pub fn parse_element_size(input: &[u8]) -> IResult<&[u8], u64> {
if input.is_empty() {
return Err(nom::Err::Incomplete(nom::Needed::new(1)));
}
let first = input[0];
if first == 0 {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Fail,
)));
}
let len = first.leading_zeros() as usize + 1;
if len > 8 {
return Err(nom::Err::Error(nom::error::Error::new(
input,
nom::error::ErrorKind::Fail,
)));
}
if input.len() < len {
return Err(nom::Err::Incomplete(nom::Needed::new(len - input.len())));
}
let (remaining, value) = parse_vint(input)?;
let max_value = (1u64 << (7 * len)) - 1;
if value == max_value {
return Ok((remaining, u64::MAX));
}
Ok((remaining, value))
}
pub fn parse_element_header(input: &[u8]) -> IResult<&[u8], EbmlElement> {
let start_len = input.len();
let (after_id, id) = parse_element_id(input)?;
let (remaining, size) = parse_element_size(after_id)?;
let header_size = start_len - remaining.len();
Ok((
remaining,
EbmlElement {
id,
size,
header_size,
},
))
}
pub fn read_uint(data: &[u8]) -> IResult<&[u8], u64> {
if data.is_empty() {
return Ok((data, 0));
}
if data.len() > 8 {
return Err(nom::Err::Error(nom::error::Error::new(
data,
nom::error::ErrorKind::TooLarge,
)));
}
let (remaining, bytes) = take(data.len())(data)?;
let mut value = 0u64;
for &byte in bytes {
value = (value << 8) | u64::from(byte);
}
Ok((remaining, value))
}
pub fn read_int(data: &[u8]) -> IResult<&[u8], i64> {
let (remaining, unsigned) = read_uint(data)?;
#[allow(clippy::cast_possible_wrap)]
let signed = if data.is_empty() {
0i64
} else if data[0] & 0x80 != 0 {
let shift = 64 - (data.len() * 8);
((unsigned << shift) as i64) >> shift
} else {
unsigned as i64
};
Ok((remaining, signed))
}
pub fn read_string(data: &[u8], len: usize) -> OxiResult<String> {
if len > data.len() {
return Err(OxiError::InvalidData(format!(
"String length {} exceeds data length {}",
len,
data.len()
)));
}
let bytes = &data[..len];
let end = bytes.iter().position(|&b| b == 0).unwrap_or(len);
let trimmed = &bytes[..end];
String::from_utf8(trimmed.to_vec())
.map_err(|e| OxiError::InvalidData(format!("Invalid UTF-8 string: {e}")))
}
pub fn read_float(data: &[u8], len: usize) -> OxiResult<f64> {
match len {
0 => Ok(0.0),
4 => {
if data.len() < 4 {
return Err(OxiError::InvalidData("Not enough bytes for f32".into()));
}
let bytes: [u8; 4] = data[..4]
.try_into()
.map_err(|_| OxiError::InvalidData("Failed to convert bytes to f32".into()))?;
Ok(f64::from(f32::from_be_bytes(bytes)))
}
8 => {
if data.len() < 8 {
return Err(OxiError::InvalidData("Not enough bytes for f64".into()));
}
let bytes: [u8; 8] = data[..8]
.try_into()
.map_err(|_| OxiError::InvalidData("Failed to convert bytes to f64".into()))?;
Ok(f64::from_be_bytes(bytes))
}
_ => Err(OxiError::InvalidData(format!(
"Invalid float size: {len} (expected 0, 4, or 8)"
))),
}
}
pub fn read_date(data: &[u8], len: usize) -> OxiResult<i64> {
match len {
0 => Ok(0),
8 => {
if data.len() < 8 {
return Err(OxiError::InvalidData("Not enough bytes for date".into()));
}
let bytes: [u8; 8] = data[..8]
.try_into()
.map_err(|_| OxiError::InvalidData("Failed to convert bytes to i64".into()))?;
Ok(i64::from_be_bytes(bytes))
}
_ => {
if len > 8 || len > data.len() {
return Err(OxiError::InvalidData(format!("Invalid date size: {len}")));
}
let (_, value) = read_int(&data[..len])
.map_err(|_| OxiError::InvalidData("Failed to parse date as integer".into()))?;
Ok(value)
}
}
}
pub fn read_binary(data: &[u8], len: usize) -> OxiResult<Vec<u8>> {
if len > data.len() {
return Err(OxiError::InvalidData(format!(
"Binary length {} exceeds data length {}",
len,
data.len()
)));
}
Ok(data[..len].to_vec())
}
#[must_use]
pub fn element_name(id: u32) -> &'static str {
match id {
element_id::EBML => "EBML",
element_id::EBML_VERSION => "EBMLVersion",
element_id::EBML_READ_VERSION => "EBMLReadVersion",
element_id::EBML_MAX_ID_LENGTH => "EBMLMaxIDLength",
element_id::EBML_MAX_SIZE_LENGTH => "EBMLMaxSizeLength",
element_id::DOC_TYPE => "DocType",
element_id::DOC_TYPE_VERSION => "DocTypeVersion",
element_id::DOC_TYPE_READ_VERSION => "DocTypeReadVersion",
element_id::SEGMENT => "Segment",
element_id::SEEK_HEAD => "SeekHead",
element_id::SEEK => "Seek",
element_id::SEEK_ID => "SeekID",
element_id::SEEK_POSITION => "SeekPosition",
element_id::INFO => "Info",
element_id::TIMECODE_SCALE => "TimecodeScale",
element_id::DURATION => "Duration",
element_id::TITLE => "Title",
element_id::MUXING_APP => "MuxingApp",
element_id::WRITING_APP => "WritingApp",
element_id::DATE_UTC => "DateUTC",
element_id::SEGMENT_UID => "SegmentUID",
element_id::TRACKS => "Tracks",
element_id::TRACK_ENTRY => "TrackEntry",
element_id::TRACK_NUMBER => "TrackNumber",
element_id::TRACK_UID => "TrackUID",
element_id::TRACK_TYPE => "TrackType",
element_id::FLAG_ENABLED => "FlagEnabled",
element_id::FLAG_DEFAULT => "FlagDefault",
element_id::FLAG_FORCED => "FlagForced",
element_id::FLAG_LACING => "FlagLacing",
element_id::DEFAULT_DURATION => "DefaultDuration",
element_id::NAME => "Name",
element_id::LANGUAGE => "Language",
element_id::CODEC_ID => "CodecID",
element_id::CODEC_PRIVATE => "CodecPrivate",
element_id::CODEC_NAME => "CodecName",
element_id::CODEC_DELAY => "CodecDelay",
element_id::SEEK_PRE_ROLL => "SeekPreRoll",
element_id::VIDEO => "Video",
element_id::PIXEL_WIDTH => "PixelWidth",
element_id::PIXEL_HEIGHT => "PixelHeight",
element_id::DISPLAY_WIDTH => "DisplayWidth",
element_id::DISPLAY_HEIGHT => "DisplayHeight",
element_id::FLAG_INTERLACED => "FlagInterlaced",
element_id::COLOUR => "Colour",
element_id::PRIMARIES => "Primaries",
element_id::TRANSFER_CHARACTERISTICS => "TransferCharacteristics",
element_id::MATRIX_COEFFICIENTS => "MatrixCoefficients",
element_id::AUDIO => "Audio",
element_id::SAMPLING_FREQUENCY => "SamplingFrequency",
element_id::OUTPUT_SAMPLING_FREQUENCY => "OutputSamplingFrequency",
element_id::CHANNELS => "Channels",
element_id::BIT_DEPTH => "BitDepth",
element_id::CLUSTER => "Cluster",
element_id::TIMESTAMP => "Timestamp",
element_id::SIMPLE_BLOCK => "SimpleBlock",
element_id::BLOCK_GROUP => "BlockGroup",
element_id::BLOCK => "Block",
element_id::BLOCK_DURATION => "BlockDuration",
element_id::REFERENCE_BLOCK => "ReferenceBlock",
element_id::DISCARD_PADDING => "DiscardPadding",
element_id::CUES => "Cues",
element_id::CUE_POINT => "CuePoint",
element_id::CUE_TIME => "CueTime",
element_id::CUE_TRACK_POSITIONS => "CueTrackPositions",
element_id::CUE_TRACK => "CueTrack",
element_id::CUE_CLUSTER_POSITION => "CueClusterPosition",
element_id::CUE_RELATIVE_POSITION => "CueRelativePosition",
element_id::CUE_DURATION => "CueDuration",
element_id::CUE_BLOCK_NUMBER => "CueBlockNumber",
element_id::ATTACHMENTS => "Attachments",
element_id::ATTACHED_FILE => "AttachedFile",
element_id::FILE_NAME => "FileName",
element_id::FILE_MIME_TYPE => "FileMimeType",
element_id::FILE_DATA => "FileData",
element_id::FILE_UID => "FileUID",
element_id::CHAPTERS => "Chapters",
element_id::EDITION_ENTRY => "EditionEntry",
element_id::CHAPTER_ATOM => "ChapterAtom",
element_id::CHAPTER_UID => "ChapterUID",
element_id::CHAPTER_TIME_START => "ChapterTimeStart",
element_id::CHAPTER_TIME_END => "ChapterTimeEnd",
element_id::CHAPTER_DISPLAY => "ChapterDisplay",
element_id::CHAP_STRING => "ChapString",
element_id::CHAP_LANGUAGE => "ChapLanguage",
element_id::TAGS => "Tags",
element_id::TAG => "Tag",
element_id::TARGETS => "Targets",
element_id::SIMPLE_TAG => "SimpleTag",
element_id::TAG_NAME => "TagName",
element_id::TAG_STRING => "TagString",
element_id::TAG_BINARY => "TagBinary",
element_id::VOID => "Void",
element_id::CRC32 => "CRC-32",
_ => "Unknown",
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_vint_1byte() {
let (remaining, value) = parse_vint(&[0x81]).expect("operation should succeed");
assert_eq!(value, 1);
assert!(remaining.is_empty());
let (_, value) = parse_vint(&[0xFF]).expect("operation should succeed");
assert_eq!(value, 127);
}
#[test]
fn test_parse_vint_2bytes() {
let (_, value) = parse_vint(&[0x40, 0x01]).expect("operation should succeed");
assert_eq!(value, 1);
let (_, value) = parse_vint(&[0x7F, 0xFF]).expect("operation should succeed");
assert_eq!(value, 16383);
}
#[test]
fn test_parse_element_id() {
let (_, id) = parse_element_id(&[0xA3]).expect("operation should succeed");
assert_eq!(id, 0xA3);
let (_, id) =
parse_element_id(&[0x1A, 0x45, 0xDF, 0xA3]).expect("operation should succeed");
assert_eq!(id, EBML_HEADER);
let (_, id) =
parse_element_id(&[0x18, 0x53, 0x80, 0x67]).expect("operation should succeed");
assert_eq!(id, SEGMENT);
}
#[test]
fn test_parse_element_header() {
let data = [0x1A, 0x45, 0xDF, 0xA3, 0x9F];
let (remaining, element) = parse_element_header(&data).expect("operation should succeed");
assert_eq!(element.id, EBML_HEADER);
assert_eq!(element.size, 31);
assert_eq!(element.header_size, 5);
assert!(remaining.is_empty());
}
#[test]
fn test_parse_element_unknown_size() {
let data = [0xA3, 0xFF];
let (_, element) = parse_element_header(&data).expect("operation should succeed");
assert!(element.is_unbounded());
assert_eq!(element.size, u64::MAX);
assert!(element.total_size().is_none());
}
#[test]
fn test_read_uint() {
let (_, value) = read_uint(&[0x01]).expect("operation should succeed");
assert_eq!(value, 1);
let (_, value) = read_uint(&[0x01, 0x00]).expect("operation should succeed");
assert_eq!(value, 256);
let (_, value) = read_uint(&[0xFF, 0xFF]).expect("operation should succeed");
assert_eq!(value, 65535);
let (_, value) = read_uint(&[]).expect("operation should succeed");
assert_eq!(value, 0);
}
#[test]
fn test_read_int() {
let (_, value) = read_int(&[0x01]).expect("operation should succeed");
assert_eq!(value, 1);
let (_, value) = read_int(&[0xFF]).expect("operation should succeed");
assert_eq!(value, -1);
let (_, value) = read_int(&[0x80]).expect("operation should succeed");
assert_eq!(value, -128);
}
#[test]
fn test_element_total_size() {
let bounded = EbmlElement {
id: 0xA3,
size: 100,
header_size: 2,
};
assert_eq!(bounded.total_size(), Some(102));
assert!(!bounded.is_unbounded());
let unbounded = EbmlElement {
id: SEGMENT,
size: u64::MAX,
header_size: 5,
};
assert!(unbounded.is_unbounded());
assert!(unbounded.total_size().is_none());
}
#[test]
fn test_read_string() {
let result = read_string(b"webm", 4).expect("operation should succeed");
assert_eq!(result, "webm");
let result = read_string(b"webm\0\0\0\0", 8).expect("operation should succeed");
assert_eq!(result, "webm");
let result = read_string(b"", 0).expect("operation should succeed");
assert_eq!(result, "");
let result = read_string(b"matroska", 8).expect("operation should succeed");
assert_eq!(result, "matroska");
}
#[test]
fn test_read_string_invalid_length() {
let result = read_string(b"test", 10);
assert!(result.is_err());
}
#[test]
fn test_read_float_f64() {
let bytes = 48000.0_f64.to_be_bytes();
let result = read_float(&bytes, 8).expect("operation should succeed");
assert!((result - 48000.0).abs() < f64::EPSILON);
}
#[test]
fn test_read_float_f32() {
let bytes = 44100.0_f32.to_be_bytes();
let result = read_float(&bytes, 4).expect("operation should succeed");
assert!((result - 44100.0).abs() < 1.0);
}
#[test]
fn test_read_float_zero() {
let result = read_float(&[], 0).expect("operation should succeed");
assert!((result - 0.0).abs() < f64::EPSILON);
}
#[test]
fn test_read_float_invalid_size() {
let result = read_float(&[0; 5], 5);
assert!(result.is_err());
}
#[test]
fn test_read_date() {
let result = read_date(&[0; 8], 8).expect("operation should succeed");
assert_eq!(result, 0);
let result = read_date(&[], 0).expect("operation should succeed");
assert_eq!(result, 0);
}
#[test]
fn test_read_binary() {
let data = [1, 2, 3, 4, 5];
let result = read_binary(&data, 3).expect("operation should succeed");
assert_eq!(result, vec![1, 2, 3]);
}
#[test]
fn test_read_binary_invalid_length() {
let result = read_binary(&[1, 2], 10);
assert!(result.is_err());
}
#[test]
fn test_element_name() {
assert_eq!(element_name(element_id::EBML), "EBML");
assert_eq!(element_name(element_id::SEGMENT), "Segment");
assert_eq!(element_name(element_id::TRACKS), "Tracks");
assert_eq!(element_name(element_id::CLUSTER), "Cluster");
assert_eq!(element_name(0xDEADBEEF), "Unknown");
}
#[test]
fn test_is_master() {
let master = EbmlElement {
id: element_id::SEGMENT,
size: 1000,
header_size: 8,
};
assert!(master.is_master());
let not_master = EbmlElement {
id: element_id::SIMPLE_BLOCK,
size: 100,
header_size: 4,
};
assert!(!not_master.is_master());
}
#[test]
fn test_parse_vint_single_byte_values() {
for value in 0..128 {
let byte = 0x80 | value; let data = [byte];
let (remaining, parsed) = parse_vint(&data).expect("operation should succeed");
assert_eq!(parsed, u64::from(value));
assert!(remaining.is_empty());
}
}
#[test]
fn test_parse_vint_two_byte_values() {
let (_, value) = parse_vint(&[0x40, 0x01]).expect("operation should succeed");
assert_eq!(value, 1);
let (_, value) = parse_vint(&[0x7F, 0xFF]).expect("operation should succeed");
assert_eq!(value, 16383); }
#[test]
fn test_parse_vint_three_byte_values() {
let (_, value) = parse_vint(&[0x20, 0x00, 0x01]).expect("operation should succeed");
assert_eq!(value, 1);
let (_, value) = parse_vint(&[0x3F, 0xFF, 0xFF]).expect("operation should succeed");
assert_eq!(value, 2_097_151); }
#[test]
fn test_parse_vint_invalid_zero_byte() {
let result = parse_vint(&[0x00, 0x01]);
assert!(result.is_err());
}
#[test]
fn test_parse_vint_incomplete() {
let result = parse_vint(&[0x40]);
assert!(result.is_err());
let result = parse_vint(&[0x20, 0x00]);
assert!(result.is_err());
}
#[test]
fn test_parse_element_id_ebml_header() {
let (remaining, id) =
parse_element_id(&[0x1A, 0x45, 0xDF, 0xA3, 0xFF]).expect("operation should succeed");
assert_eq!(id, element_id::EBML);
assert_eq!(remaining, &[0xFF]);
}
#[test]
fn test_parse_element_id_segment() {
let (_, id) =
parse_element_id(&[0x18, 0x53, 0x80, 0x67]).expect("operation should succeed");
assert_eq!(id, element_id::SEGMENT);
}
#[test]
fn test_parse_element_id_single_byte() {
let (remaining, id) = parse_element_id(&[0x83, 0x00]).expect("operation should succeed");
assert_eq!(id, element_id::TRACK_TYPE);
assert_eq!(remaining, &[0x00]);
}
#[test]
fn test_parse_element_size_unknown() {
let (_, size) = parse_element_size(&[0xFF]).expect("operation should succeed");
assert_eq!(size, u64::MAX);
let (_, size) = parse_element_size(&[0x7F, 0xFF]).expect("operation should succeed");
assert_eq!(size, u64::MAX);
}
#[test]
fn test_parse_element_size_normal_values() {
let (_, size) = parse_element_size(&[0x80]).expect("operation should succeed");
assert_eq!(size, 0);
let (_, size) = parse_element_size(&[0xFE]).expect("operation should succeed");
assert_eq!(size, 126);
let (_, size) = parse_element_size(&[0x40, 0x00]).expect("operation should succeed");
assert_eq!(size, 0);
}
#[test]
fn test_parse_element_header_complete() {
let data = [0xAE, 0xE4];
let (remaining, element) = parse_element_header(&data).expect("operation should succeed");
assert_eq!(element.id, element_id::TRACK_ENTRY);
assert_eq!(element.size, 100);
assert_eq!(element.header_size, 2);
assert!(remaining.is_empty());
}
#[test]
fn test_ebml_element_total_size() {
let element = EbmlElement {
id: element_id::SEGMENT,
size: 1000,
header_size: 8,
};
assert_eq!(element.total_size(), Some(1008));
let element_unknown = EbmlElement {
id: element_id::SEGMENT,
size: u64::MAX,
header_size: 8,
};
assert!(element_unknown.total_size().is_none());
}
#[test]
fn test_read_uint_various_sizes() {
let (remaining, result) = read_uint(&[0x12]).expect("operation should succeed");
assert_eq!(result, 0x12);
assert!(remaining.is_empty());
let (remaining, result) = read_uint(&[0x12, 0x34]).expect("operation should succeed");
assert_eq!(result, 0x1234);
assert!(remaining.is_empty());
let (remaining, result) =
read_uint(&[0x12, 0x34, 0x56, 0x78]).expect("operation should succeed");
assert_eq!(result, 0x12345678);
assert!(remaining.is_empty());
let (remaining, result) = read_uint(&[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0])
.expect("operation should succeed");
assert_eq!(result, 0x123456789ABCDEF0);
assert!(remaining.is_empty());
}
#[test]
fn test_read_uint_empty() {
let (remaining, result) = read_uint(&[]).expect("operation should succeed");
assert_eq!(result, 0);
assert!(remaining.is_empty());
}
#[test]
fn test_read_int_positive_and_negative() {
let (_, result) = read_int(&[0x12]).expect("operation should succeed");
assert_eq!(result, 0x12);
let (_, result) = read_int(&[0xFF]).expect("operation should succeed");
assert_eq!(result, -1);
let (_, result) = read_int(&[0xFF, 0xFF]).expect("operation should succeed");
assert_eq!(result, -1);
let (_, result) = read_int(&[0x7F, 0xFF]).expect("operation should succeed");
assert_eq!(result, 32767);
}
#[test]
fn test_read_string_utf8() {
let result = read_string("hello".as_bytes(), 5).expect("operation should succeed");
assert_eq!(result, "hello");
let result = read_string("hello world".as_bytes(), 11).expect("operation should succeed");
assert_eq!(result, "hello world");
}
#[test]
fn test_read_string_null_padding() {
let data = b"test\0\0\0\0";
let result = read_string(data, 8).expect("operation should succeed");
assert_eq!(result, "test");
}
#[test]
fn test_element_name_coverage() {
assert_eq!(element_name(element_id::EBML), "EBML");
assert_eq!(element_name(element_id::TRACK_ENTRY), "TrackEntry");
assert_eq!(element_name(element_id::VIDEO), "Video");
assert_eq!(element_name(element_id::AUDIO), "Audio");
assert_eq!(element_name(element_id::CLUSTER), "Cluster");
assert_eq!(element_name(element_id::SIMPLE_BLOCK), "SimpleBlock");
assert_eq!(element_name(element_id::CODEC_ID), "CodecID");
}
#[test]
fn test_is_master_all_variants() {
let master_ids = [
element_id::EBML,
element_id::SEGMENT,
element_id::TRACKS,
element_id::TRACK_ENTRY,
element_id::VIDEO,
element_id::AUDIO,
element_id::CLUSTER,
element_id::CUES,
];
for id in master_ids {
let element = EbmlElement {
id,
size: 100,
header_size: 4,
};
assert!(
element.is_master(),
"Expected {id:08X} to be a master element"
);
}
}
#[test]
fn test_vint_consecutive_parsing() {
let data = [
0x81, 0x82, 0x83, ];
let (rest, v1) = parse_vint(&data).expect("operation should succeed");
assert_eq!(v1, 1);
let (rest, v2) = parse_vint(rest).expect("operation should succeed");
assert_eq!(v2, 2);
let (rest, v3) = parse_vint(rest).expect("operation should succeed");
assert_eq!(v3, 3);
assert!(rest.is_empty());
}
}