use std::io::{Read, Seek};
use crate::error::{Error, Result};
use crate::format::checksum::checksum_metadata;
use crate::io::reader::HdfReader;
use super::chunk::read_v1_continuation;
use super::chunk::read_v2_continuation;
use super::msg::{read_v1_messages, read_v2_messages};
use super::{
ObjectHeader, HDR_ATTR_CRT_ORDER_TRACKED, HDR_ATTR_STORE_PHASE_CHANGE, HDR_CHUNK0_SIZE_MASK,
HDR_STORE_TIMES, OHDR_MAGIC,
};
impl ObjectHeader {
pub(super) fn read_v1<R: Read + Seek>(reader: &mut HdfReader<R>) -> Result<Self> {
let header_start = reader.position()?;
let version = reader.read_u8()?;
if version != 1 {
return Err(Error::InvalidFormat(format!(
"expected object header v1, got {version}"
)));
}
reader.skip(1)?;
let num_messages = reader.read_u16()?;
let refcount = reader.read_u32()?;
let chunk_data_size = reader.read_u32()? as u64;
reader.skip(4)?;
let chunk_start = reader.position()?;
let chunk_end = chunk_start
.checked_add(chunk_data_size)
.ok_or_else(|| Error::InvalidFormat("object header v1 chunk size overflow".into()))?;
let mut messages = Vec::new();
let mut continuations = Vec::new();
let mut chunk_ranges = vec![(header_start, chunk_end)];
read_v1_messages(
reader,
chunk_end,
num_messages,
&mut messages,
&mut continuations,
&mut chunk_ranges,
0,
)?;
for (cont_addr, cont_len) in continuations {
read_v1_continuation(
reader,
cont_addr,
cont_len,
&mut messages,
&mut chunk_ranges,
1,
)?;
}
if messages.len() > num_messages as usize {
return Err(Error::InvalidFormat(format!(
"object header v1 declared {num_messages} messages but decoded {} non-NIL/non-continuation messages",
messages.len()
)));
}
Ok(ObjectHeader {
version: 1,
flags: 0,
refcount,
atime: None,
mtime: None,
ctime: None,
btime: None,
max_compact_attrs: None,
min_dense_attrs: None,
messages,
})
}
pub(super) fn read_v2<R: Read + Seek>(
reader: &mut HdfReader<R>,
header_addr: u64,
) -> Result<Self> {
let _ = OHDR_MAGIC; let version = reader.read_u8()?;
if version != 2 {
return Err(Error::InvalidFormat(format!(
"expected object header v2, got {version}"
)));
}
let flags = reader.read_u8()?;
let (atime, mtime, ctime, btime) = if flags & HDR_STORE_TIMES != 0 {
(
Some(reader.read_u32()?),
Some(reader.read_u32()?),
Some(reader.read_u32()?),
Some(reader.read_u32()?),
)
} else {
(None, None, None, None)
};
let (max_compact_attrs, min_dense_attrs) = if flags & HDR_ATTR_STORE_PHASE_CHANGE != 0 {
(Some(reader.read_u16()?), Some(reader.read_u16()?))
} else {
(None, None)
};
let chunk0_size_bytes = 1u8 << (flags & HDR_CHUNK0_SIZE_MASK);
let chunk0_data_size = reader.read_uint(chunk0_size_bytes)?;
let chunk0_data_start = reader.position()?;
let chunk0_data_end = chunk0_data_start
.checked_add(chunk0_data_size)
.ok_or_else(|| Error::InvalidFormat("object header v2 chunk size overflow".into()))?;
let checksum_pos = chunk0_data_end;
reader.seek(checksum_pos)?;
let stored_checksum = reader.read_u32()?;
let check_len = (checksum_pos - header_addr) as usize;
reader.seek(header_addr)?;
let check_data = reader.read_bytes(check_len)?;
let computed = checksum_metadata(&check_data);
if stored_checksum != computed {
return Err(Error::InvalidFormat(format!(
"object header checksum mismatch: stored={stored_checksum:#010x}, computed={computed:#010x}"
)));
}
reader.seek(chunk0_data_start)?;
let has_crt_order = flags & HDR_ATTR_CRT_ORDER_TRACKED != 0;
let mut messages = Vec::new();
let mut continuations = Vec::new();
let chunk0_range_end = chunk0_data_end
.checked_add(4)
.ok_or_else(|| Error::InvalidFormat("object header v2 chunk range overflow".into()))?;
let mut chunk_ranges = vec![(header_addr, chunk0_range_end)];
read_v2_messages(
reader,
chunk0_data_end,
has_crt_order,
&mut messages,
&mut continuations,
&mut chunk_ranges,
0,
)?;
for (cont_addr, cont_len) in continuations {
read_v2_continuation(
reader,
cont_addr,
cont_len,
has_crt_order,
&mut messages,
&mut chunk_ranges,
1,
)?;
}
Ok(ObjectHeader {
version: 2,
flags,
refcount: 1, atime,
mtime,
ctime,
btime,
max_compact_attrs,
min_dense_attrs,
messages,
})
}
}