use crate::error::{Error, Result};
use crate::reader::Reader;
use crate::stream::StreamHeader;
use crate::writer::Writer;
pub const METADATA_SIGNATURE: u32 = 0x424A5342;
#[derive(Debug, Clone)]
pub struct MetadataRoot {
pub major_version: u16,
pub minor_version: u16,
pub reserved: u32,
pub version: String,
pub flags: u16,
pub streams: Vec<StreamHeader>,
}
impl MetadataRoot {
pub fn parse(data: &[u8]) -> Result<Self> {
let mut reader = Reader::new(data);
Self::parse_from_reader(&mut reader)
}
pub fn parse_from_reader(reader: &mut Reader<'_>) -> Result<Self> {
let signature = reader.read_u32()?;
if signature != METADATA_SIGNATURE {
return Err(Error::InvalidSignature(signature));
}
let major_version = reader.read_u16()?;
let minor_version = reader.read_u16()?;
let reserved = reader.read_u32()?;
let version_len = reader.read_u32()? as usize;
let version_bytes = reader.read_bytes(version_len)?;
let version = version_bytes
.iter()
.position(|&b| b == 0)
.map(|pos| &version_bytes[..pos])
.unwrap_or(version_bytes);
let version =
std::str::from_utf8(version).map_err(|_| Error::InvalidString(reader.position()))?;
let version = version.to_string();
let flags = reader.read_u16()?;
let stream_count = reader.read_u16()? as usize;
let mut streams = Vec::with_capacity(stream_count);
for _ in 0..stream_count {
streams.push(StreamHeader::parse(reader)?);
}
Ok(Self {
major_version,
minor_version,
reserved,
version,
flags,
streams,
})
}
#[must_use]
pub fn write(&self) -> Vec<u8> {
let mut writer = Writer::new();
self.write_to(&mut writer);
writer.into_inner()
}
pub fn write_to(&self, writer: &mut Writer) {
writer.write_u32(METADATA_SIGNATURE);
writer.write_u16(self.major_version);
writer.write_u16(self.minor_version);
writer.write_u32(self.reserved);
let version_bytes = self.version.as_bytes();
let version_len_with_null = version_bytes.len() + 1;
let padded_len = (version_len_with_null + 3) & !3;
writer.write_u32(padded_len as u32);
writer.write_bytes(version_bytes);
writer.write_u8(0); for _ in version_len_with_null..padded_len {
writer.write_u8(0);
}
writer.write_u16(self.flags);
writer.write_u16(self.streams.len() as u16);
for stream in &self.streams {
stream.write(writer);
}
}
#[must_use]
pub fn header_size(&self) -> usize {
let version_len_with_null = self.version.len() + 1;
let padded_version_len = (version_len_with_null + 3) & !3;
let base = 4 + 2 + 2 + 4 + 4 + padded_version_len + 2 + 2;
let streams_size: usize = self.streams.iter().map(|s| s.serialized_size()).sum();
base + streams_size
}
#[must_use]
pub fn find_stream(&self, name: &str) -> Option<&StreamHeader> {
self.streams.iter().find(|s| s.name == name)
}
#[must_use]
pub fn tables_stream(&self) -> Option<&StreamHeader> {
self.streams.iter().find(|s| s.is_tables())
}
}