use std::collections::HashMap;
use std::io::{Seek, SeekFrom, Write};
use crate::engine::allocator::FileAllocator;
use crate::error::{Error, Result};
use crate::format::checksum::checksum_metadata;
use crate::format::object_header::*;
use crate::format::superblock::Superblock;
use crate::io::reader::UNDEF_ADDR;
pub struct HdfFileWriter<W: Write + Seek> {
writer: W,
allocator: FileAllocator,
sizeof_addr: u8,
sizeof_size: u8,
groups: HashMap<String, u64>,
links: Vec<(String, String, u64)>,
pending_root_attrs: Vec<(u16, Vec<u8>)>,
special_links: Vec<(String, Vec<u8>)>,
}
pub struct AttrSpec<'a> {
pub name: &'a str,
pub shape: &'a [u64],
pub dtype: DtypeSpec,
pub data: &'a [u8],
}
pub struct DatasetSpec<'a> {
pub name: &'a str,
pub shape: &'a [u64],
pub dtype: DtypeSpec,
pub data: &'a [u8],
}
#[derive(Debug, Clone, Copy)]
pub enum DtypeSpec {
F64,
F32,
I64,
I32,
I16,
I8,
U64,
U32,
U16,
U8,
}
impl DtypeSpec {
pub fn size(&self) -> u32 {
match self {
DtypeSpec::F64 | DtypeSpec::I64 | DtypeSpec::U64 => 8,
DtypeSpec::F32 | DtypeSpec::I32 | DtypeSpec::U32 => 4,
DtypeSpec::I16 | DtypeSpec::U16 => 2,
DtypeSpec::I8 | DtypeSpec::U8 => 1,
}
}
pub fn encode(&self) -> Vec<u8> {
let mut buf = Vec::new();
match self {
DtypeSpec::F32 | DtypeSpec::F64 => {
let size = self.size();
let class_and_version = 0x11u8;
if size == 4 {
buf.push(class_and_version);
buf.extend_from_slice(&[0x20, 31, 0x00]); buf.extend_from_slice(&size.to_le_bytes());
buf.extend_from_slice(&0u16.to_le_bytes()); buf.extend_from_slice(&32u16.to_le_bytes()); buf.push(23); buf.push(8); buf.push(0); buf.push(23); buf.extend_from_slice(&127u32.to_le_bytes()); } else {
buf.push(class_and_version);
buf.extend_from_slice(&[0x20, 63, 0x00]); buf.extend_from_slice(&size.to_le_bytes());
buf.extend_from_slice(&0u16.to_le_bytes()); buf.extend_from_slice(&64u16.to_le_bytes()); buf.push(52); buf.push(11); buf.push(0); buf.push(52); buf.extend_from_slice(&1023u32.to_le_bytes()); }
}
_ => {
let size = self.size();
let is_signed = matches!(self, DtypeSpec::I8 | DtypeSpec::I16 | DtypeSpec::I32 | DtypeSpec::I64);
let class_and_version = 0x10u8; let bf0 = if is_signed { 0x08u8 } else { 0x00u8 };
buf.push(class_and_version);
buf.extend_from_slice(&[bf0, 0x00, 0x00]);
buf.extend_from_slice(&size.to_le_bytes());
buf.extend_from_slice(&0u16.to_le_bytes()); buf.extend_from_slice(&(size as u16 * 8).to_le_bytes()); }
}
buf
}
}
fn encode_dataspace(shape: &[u64]) -> Vec<u8> {
let mut buf = Vec::new();
if shape.is_empty() {
buf.push(2); buf.push(0); buf.push(0); buf.push(0); } else {
buf.push(2); buf.push(shape.len() as u8); buf.push(0); buf.push(1); for &d in shape {
buf.extend_from_slice(&d.to_le_bytes());
}
}
buf
}
fn encode_link_message(name: &str, target_addr: u64, sizeof_addr: u8) -> Vec<u8> {
let mut buf = Vec::new();
let name_bytes = name.as_bytes();
let name_len = name_bytes.len();
let (size_flag, len_bytes) = if name_len < 256 {
(0u8, 1usize)
} else if name_len < 65536 {
(1u8, 2usize)
} else {
(2u8, 4usize)
};
buf.push(1);
let flags = size_flag | 0x10;
buf.push(flags);
buf.push(1);
match len_bytes {
1 => buf.push(name_len as u8),
2 => buf.extend_from_slice(&(name_len as u16).to_le_bytes()),
4 => buf.extend_from_slice(&(name_len as u32).to_le_bytes()),
_ => unreachable!(),
}
buf.extend_from_slice(name_bytes);
let addr_bytes = target_addr.to_le_bytes();
buf.extend_from_slice(&addr_bytes[..sizeof_addr as usize]);
buf
}
fn encode_soft_link_message(name: &str, target_path: &str) -> Vec<u8> {
let mut buf = Vec::new();
let name_bytes = name.as_bytes();
let name_len = name_bytes.len();
let target_bytes = target_path.as_bytes();
let (size_flag, len_bytes) = if name_len < 256 { (0u8, 1) } else if name_len < 65536 { (1u8, 2) } else { (2u8, 4) };
buf.push(1); buf.push(size_flag | 0x08 | 0x10);
buf.push(1); buf.push(1);
match len_bytes {
1 => buf.push(name_len as u8),
2 => buf.extend_from_slice(&(name_len as u16).to_le_bytes()),
4 => buf.extend_from_slice(&(name_len as u32).to_le_bytes()),
_ => unreachable!(),
}
buf.extend_from_slice(name_bytes);
buf.extend_from_slice(&(target_bytes.len() as u16).to_le_bytes());
buf.extend_from_slice(target_bytes);
buf
}
fn encode_external_link_message(name: &str, filename: &str, obj_path: &str) -> Vec<u8> {
let mut buf = Vec::new();
let name_bytes = name.as_bytes();
let name_len = name_bytes.len();
let (size_flag, len_bytes) = if name_len < 256 { (0u8, 1) } else if name_len < 65536 { (1u8, 2) } else { (2u8, 4) };
buf.push(1); buf.push(size_flag | 0x08 | 0x10);
buf.push(64); buf.push(1);
match len_bytes {
1 => buf.push(name_len as u8),
2 => buf.extend_from_slice(&(name_len as u16).to_le_bytes()),
4 => buf.extend_from_slice(&(name_len as u32).to_le_bytes()),
_ => unreachable!(),
}
buf.extend_from_slice(name_bytes);
let info_len = 1 + filename.len() + 1 + obj_path.len() + 1;
buf.extend_from_slice(&(info_len as u16).to_le_bytes());
buf.push(0); buf.extend_from_slice(filename.as_bytes());
buf.push(0); buf.extend_from_slice(obj_path.as_bytes());
buf.push(0);
buf
}
fn encode_contiguous_layout(data_addr: u64, data_size: u64, sizeof_addr: u8, sizeof_size: u8) -> Vec<u8> {
let mut buf = Vec::new();
buf.push(3); buf.push(1);
let addr_bytes = data_addr.to_le_bytes();
buf.extend_from_slice(&addr_bytes[..sizeof_addr as usize]);
let size_bytes = data_size.to_le_bytes();
buf.extend_from_slice(&size_bytes[..sizeof_size as usize]);
buf
}
fn encode_chunked_layout_v3(
btree_addr: u64,
chunk_dims: &[u64],
element_size: u32,
sizeof_addr: u8,
) -> Vec<u8> {
let mut buf = Vec::new();
buf.push(3); buf.push(2);
let ndims = chunk_dims.len() + 1;
buf.push(ndims as u8);
let addr_bytes = btree_addr.to_le_bytes();
buf.extend_from_slice(&addr_bytes[..sizeof_addr as usize]);
for &d in chunk_dims {
buf.extend_from_slice(&(d as u32).to_le_bytes());
}
buf.extend_from_slice(&element_size.to_le_bytes());
buf
}
fn encode_filter_pipeline(compression_level: Option<u32>, shuffle: bool) -> Vec<u8> {
let mut filters = Vec::new();
if shuffle {
filters.push((2u16, Vec::<u32>::new())); }
if let Some(level) = compression_level {
filters.push((1u16, vec![level])); }
if filters.is_empty() {
return Vec::new();
}
let mut buf = Vec::new();
buf.push(2); buf.push(filters.len() as u8);
for (id, params) in &filters {
buf.extend_from_slice(&id.to_le_bytes()); buf.extend_from_slice(&0u16.to_le_bytes()); buf.extend_from_slice(&(params.len() as u16).to_le_bytes()); for &p in params {
buf.extend_from_slice(&p.to_le_bytes());
}
}
buf
}
fn encode_attribute_message(name: &str, dtype: &DtypeSpec, shape: &[u64], data: &[u8]) -> Vec<u8> {
let mut buf = Vec::new();
let dtype_bytes = dtype.encode();
let ds_bytes = encode_dataspace(shape);
let name_bytes = name.as_bytes();
let name_with_null = name_bytes.len() + 1;
buf.push(3); buf.push(0); buf.extend_from_slice(&(name_with_null as u16).to_le_bytes());
buf.extend_from_slice(&(dtype_bytes.len() as u16).to_le_bytes());
buf.extend_from_slice(&(ds_bytes.len() as u16).to_le_bytes());
buf.push(0);
buf.extend_from_slice(name_bytes);
buf.push(0);
buf.extend_from_slice(&dtype_bytes);
buf.extend_from_slice(&ds_bytes);
buf.extend_from_slice(data);
buf
}
fn build_v2_object_header(messages: &[(u16, &[u8])], flags: u8) -> Vec<u8> {
let mut chunk_data_size: usize = 0;
for (_, data) in messages {
chunk_data_size += 4 + data.len();
}
let (chunk0_flag, chunk0_bytes) = if chunk_data_size < 256 {
(0u8, 1usize)
} else if chunk_data_size < 65536 {
(1u8, 2usize)
} else {
(2u8, 4usize)
};
let oh_flags = flags | chunk0_flag;
let mut buf = Vec::new();
buf.extend_from_slice(b"OHDR");
buf.push(2);
buf.push(oh_flags);
if oh_flags & HDR_STORE_TIMES != 0 {
let now = 0u32; buf.extend_from_slice(&now.to_le_bytes()); buf.extend_from_slice(&now.to_le_bytes()); buf.extend_from_slice(&now.to_le_bytes()); buf.extend_from_slice(&now.to_le_bytes()); }
match chunk0_bytes {
1 => buf.push(chunk_data_size as u8),
2 => buf.extend_from_slice(&(chunk_data_size as u16).to_le_bytes()),
4 => buf.extend_from_slice(&(chunk_data_size as u32).to_le_bytes()),
8 => buf.extend_from_slice(&(chunk_data_size as u64).to_le_bytes()),
_ => unreachable!(),
}
for (msg_type, data) in messages {
buf.push(*msg_type as u8); buf.extend_from_slice(&(data.len() as u16).to_le_bytes()); buf.push(0); buf.extend_from_slice(data);
}
let checksum = checksum_metadata(&buf);
buf.extend_from_slice(&checksum.to_le_bytes());
buf
}
impl<W: Write + Seek> HdfFileWriter<W> {
pub fn new(writer: W) -> Self {
Self {
writer,
allocator: FileAllocator::new(0),
sizeof_addr: 8,
sizeof_size: 8,
groups: HashMap::new(),
links: Vec::new(),
pending_root_attrs: Vec::new(),
special_links: Vec::new(),
}
}
pub fn begin(&mut self) -> Result<()> {
let sb_size = 12 + 4 * self.sizeof_addr as u64 + 4;
self.allocator = FileAllocator::new(sb_size);
let zeros = vec![0u8; sb_size as usize];
self.write_at(0, &zeros)?;
Ok(())
}
fn write_group_object_header(&mut self, extra_messages: &[(u16, &[u8])]) -> Result<u64> {
let messages: Vec<(u16, &[u8])> = extra_messages.to_vec();
let oh_bytes = build_v2_object_header(&messages, 0);
let oh_addr = self.allocator.allocate(oh_bytes.len() as u64, 8);
self.write_at(oh_addr, &oh_bytes)?;
Ok(oh_addr)
}
pub fn create_root_group(&mut self) -> Result<u64> {
let addr = self.write_group_object_header(&[])?;
self.groups.insert("/".to_string(), addr);
Ok(addr)
}
pub fn create_group(&mut self, parent: &str, name: &str) -> Result<u64> {
let addr = self.write_group_object_header(&[])?;
let full_path = if parent == "/" {
format!("/{name}")
} else {
format!("{parent}/{name}")
};
self.groups.insert(full_path.clone(), addr);
self.links.push((parent.to_string(), name.to_string(), addr));
Ok(addr)
}
pub fn create_soft_link(&mut self, parent: &str, name: &str, target_path: &str) {
let msg = encode_soft_link_message(name, target_path);
self.special_links.push((parent.to_string(), msg));
}
pub fn create_external_link(&mut self, parent: &str, name: &str, filename: &str, obj_path: &str) {
let msg = encode_external_link_message(name, filename, obj_path);
self.special_links.push((parent.to_string(), msg));
}
pub fn create_compact_dataset(
&mut self,
parent: &str,
spec: &DatasetSpec,
) -> Result<u64> {
let dtype_bytes = spec.dtype.encode();
let ds_bytes = encode_dataspace(spec.shape);
let mut layout_bytes = Vec::new();
layout_bytes.push(3); layout_bytes.push(0); layout_bytes.extend_from_slice(&(spec.data.len() as u16).to_le_bytes());
layout_bytes.extend_from_slice(spec.data);
let fill_value_bytes = vec![3u8, 0x09];
let messages: Vec<(u16, &[u8])> = vec![
(MSG_DATASPACE, &ds_bytes),
(MSG_DATATYPE, &dtype_bytes),
(MSG_FILL_VALUE, &fill_value_bytes),
(MSG_LAYOUT, &layout_bytes),
];
let oh_bytes = build_v2_object_header(&messages, 0);
let oh_addr = self.allocator.allocate(oh_bytes.len() as u64, 8);
self.write_at(oh_addr, &oh_bytes)?;
self.links.push((parent.to_string(), spec.name.to_string(), oh_addr));
Ok(oh_addr)
}
pub fn create_dataset(
&mut self,
parent: &str,
spec: &DatasetSpec,
) -> Result<u64> {
let dtype_bytes = spec.dtype.encode();
let ds_bytes = encode_dataspace(spec.shape);
let data_size = spec.data.len() as u64;
let data_addr = if data_size > 0 {
let addr = self.allocator.allocate(data_size, 8);
self.write_at(addr, spec.data)?;
addr
} else {
UNDEF_ADDR
};
let layout_bytes = encode_contiguous_layout(
data_addr,
data_size,
self.sizeof_addr,
self.sizeof_size,
);
let fill_value_bytes = vec![3u8, 0x09];
let messages: Vec<(u16, &[u8])> = vec![
(MSG_DATASPACE, &ds_bytes),
(MSG_DATATYPE, &dtype_bytes),
(MSG_FILL_VALUE, &fill_value_bytes),
(MSG_LAYOUT, &layout_bytes),
];
let oh_bytes = build_v2_object_header(&messages, 0);
let oh_addr = self.allocator.allocate(oh_bytes.len() as u64, 8);
self.write_at(oh_addr, &oh_bytes)?;
self.links.push((parent.to_string(), spec.name.to_string(), oh_addr));
Ok(oh_addr)
}
pub fn create_dataset_with_attrs(
&mut self,
parent: &str,
spec: &DatasetSpec,
attrs: &[AttrSpec],
) -> Result<u64> {
let dtype_bytes = spec.dtype.encode();
let ds_bytes = encode_dataspace(spec.shape);
let data_size = spec.data.len() as u64;
let data_addr = if data_size > 0 {
let addr = self.allocator.allocate(data_size, 8);
self.write_at(addr, spec.data)?;
addr
} else {
UNDEF_ADDR
};
let layout_bytes = encode_contiguous_layout(data_addr, data_size, self.sizeof_addr, self.sizeof_size);
let fill_value_bytes = vec![3u8, 0x09];
let mut messages: Vec<(u16, Vec<u8>)> = vec![
(MSG_DATASPACE, ds_bytes),
(MSG_DATATYPE, dtype_bytes),
(MSG_FILL_VALUE, fill_value_bytes),
(MSG_LAYOUT, layout_bytes),
];
for attr in attrs {
let attr_bytes = encode_attribute_message(attr.name, &attr.dtype, attr.shape, attr.data);
messages.push((MSG_ATTRIBUTE, attr_bytes));
}
let msg_refs: Vec<(u16, &[u8])> = messages.iter().map(|(t, d)| (*t, d.as_slice())).collect();
let oh_bytes = build_v2_object_header(&msg_refs, 0);
let oh_addr = self.allocator.allocate(oh_bytes.len() as u64, 8);
self.write_at(oh_addr, &oh_bytes)?;
self.links.push((parent.to_string(), spec.name.to_string(), oh_addr));
Ok(oh_addr)
}
pub fn set_root_attrs(&mut self, attrs: Vec<(u16, Vec<u8>)>) {
for (msg_type, data) in attrs {
self.pending_root_attrs.push((msg_type, data));
}
}
pub fn add_root_attr(&mut self, attr: &AttrSpec) {
let attr_bytes = encode_attribute_message(attr.name, &attr.dtype, attr.shape, attr.data);
self.pending_root_attrs.push((MSG_ATTRIBUTE, attr_bytes));
}
pub fn create_chunked_dataset(
&mut self,
parent: &str,
spec: &DatasetSpec,
chunk_dims: &[u64],
compression_level: Option<u32>,
shuffle: bool,
) -> Result<u64> {
let dtype_bytes = spec.dtype.encode();
let ds_bytes = encode_dataspace(spec.shape);
let element_size = spec.dtype.size() as usize;
let ndims = spec.shape.len();
let chunk_elements: usize = chunk_dims.iter().map(|&d| d as usize).product();
let chunk_raw_bytes = chunk_elements * element_size;
let mut n_chunks_per_dim = Vec::with_capacity(ndims);
for i in 0..ndims {
n_chunks_per_dim.push(((spec.shape[i] as usize) + chunk_dims[i] as usize - 1) / chunk_dims[i] as usize);
}
let total_chunks: usize = n_chunks_per_dim.iter().product();
let mut chunk_entries: Vec<(Vec<u64>, u64, u32)> = Vec::new();
for chunk_idx in 0..total_chunks {
let mut coords = vec![0u64; ndims];
let mut rem = chunk_idx;
for d in (0..ndims).rev() {
coords[d] = (rem % n_chunks_per_dim[d]) as u64 * chunk_dims[d];
rem /= n_chunks_per_dim[d];
}
let mut chunk_buf = vec![0u8; chunk_raw_bytes];
self.extract_chunk(spec.data, spec.shape, &coords, chunk_dims, element_size, &mut chunk_buf);
let mut filtered = chunk_buf;
if shuffle {
filtered = crate::filters::shuffle::shuffle(&filtered, element_size)?;
}
if let Some(level) = compression_level {
filtered = crate::filters::deflate::compress(&filtered, level)?;
}
let compressed_size = filtered.len() as u32;
let addr = self.allocator.allocate(filtered.len() as u64, 1);
self.write_at(addr, &filtered)?;
chunk_entries.push((coords, addr, compressed_size));
}
let btree_addr = self.write_chunk_btree_v1(&chunk_entries, ndims, element_size as u32)?;
let layout_bytes = encode_chunked_layout_v3(
btree_addr,
chunk_dims,
element_size as u32,
self.sizeof_addr,
);
let pipeline_bytes = encode_filter_pipeline(compression_level, shuffle);
let fill_value_bytes = vec![3u8, 0x09];
let mut messages: Vec<(u16, &[u8])> = vec![
(MSG_DATASPACE, &ds_bytes),
(MSG_DATATYPE, &dtype_bytes),
(MSG_FILL_VALUE, &fill_value_bytes),
(MSG_LAYOUT, &layout_bytes),
];
if !pipeline_bytes.is_empty() {
messages.push((MSG_FILTER_PIPELINE, &pipeline_bytes));
}
let oh_bytes = build_v2_object_header(&messages, 0);
let oh_addr = self.allocator.allocate(oh_bytes.len() as u64, 8);
self.write_at(oh_addr, &oh_bytes)?;
self.links.push((parent.to_string(), spec.name.to_string(), oh_addr));
Ok(oh_addr)
}
fn extract_chunk(
&self,
data: &[u8],
shape: &[u64],
chunk_start: &[u64],
chunk_dims: &[u64],
element_size: usize,
out: &mut [u8],
) {
let ndims = shape.len();
if ndims == 1 {
let start = chunk_start[0] as usize;
let chunk_len = chunk_dims[0] as usize;
let data_len = shape[0] as usize;
let copy_len = chunk_len.min(data_len - start);
let src_start = start * element_size;
let src_end = src_start + copy_len * element_size;
out[..copy_len * element_size].copy_from_slice(&data[src_start..src_end]);
} else {
let chunk_elements: usize = chunk_dims.iter().map(|&d| d as usize).product();
let mut idx = vec![0usize; ndims];
for elem in 0..chunk_elements {
let mut rem = elem;
for d in (0..ndims).rev() {
idx[d] = rem % chunk_dims[d] as usize;
rem /= chunk_dims[d] as usize;
}
let mut in_bounds = true;
let mut src_linear = 0usize;
let mut stride = 1usize;
for d in (0..ndims).rev() {
let global = chunk_start[d] as usize + idx[d];
if global >= shape[d] as usize {
in_bounds = false;
break;
}
src_linear += global * stride;
stride *= shape[d] as usize;
}
if in_bounds {
let src_offset = src_linear * element_size;
let dst_offset = elem * element_size;
if src_offset + element_size <= data.len() && dst_offset + element_size <= out.len() {
out[dst_offset..dst_offset + element_size]
.copy_from_slice(&data[src_offset..src_offset + element_size]);
}
}
}
}
}
fn write_chunk_btree_v1(
&mut self,
chunks: &[(Vec<u64>, u64, u32)],
ndims: usize,
element_size: u32,
) -> Result<u64> {
let sa = self.sizeof_addr as usize;
let nchunks = chunks.len();
let key_size = 4 + 4 + (ndims + 1) * 8;
let btree_k: usize = 32; let max_entries = 2 * btree_k;
let header_size = 4 + 1 + 1 + 2 + sa * 2;
let max_node_size = header_size + (max_entries + 1) * key_size + max_entries * sa;
let mut buf = Vec::with_capacity(max_node_size);
buf.extend_from_slice(b"TREE");
buf.push(1);
buf.push(0);
buf.extend_from_slice(&(nchunks as u16).to_le_bytes());
let undef = UNDEF_ADDR.to_le_bytes();
buf.extend_from_slice(&undef[..sa]);
buf.extend_from_slice(&undef[..sa]);
for (coords, addr, compressed_size) in chunks {
buf.extend_from_slice(&compressed_size.to_le_bytes());
buf.extend_from_slice(&0u32.to_le_bytes()); for &c in coords {
buf.extend_from_slice(&c.to_le_bytes());
}
buf.extend_from_slice(&0u64.to_le_bytes());
let addr_bytes = addr.to_le_bytes();
buf.extend_from_slice(&addr_bytes[..sa]);
}
buf.extend_from_slice(&0u32.to_le_bytes()); buf.extend_from_slice(&0u32.to_le_bytes()); if let Some((last_coords, _, _)) = chunks.last() {
for &c in last_coords {
buf.extend_from_slice(&c.to_le_bytes());
}
} else {
for _ in 0..ndims {
buf.extend_from_slice(&0u64.to_le_bytes());
}
}
buf.extend_from_slice(&(element_size as u64).to_le_bytes());
buf.resize(max_node_size, 0);
let btree_addr = self.allocator.allocate(buf.len() as u64, 8);
self.write_at(btree_addr, &buf)?;
Ok(btree_addr)
}
pub fn finalize(&mut self) -> Result<()> {
let mut group_paths: Vec<String> = self.groups.keys().cloned().collect();
group_paths.sort_by(|a, b| {
let depth_a = a.split('/').filter(|s| !s.is_empty()).count();
let depth_b = b.split('/').filter(|s| !s.is_empty()).count();
depth_b.cmp(&depth_a) });
for path in group_paths {
let group_links: Vec<(String, u64)> = self.links
.iter()
.filter(|(parent, _, _)| *parent == path)
.map(|(_, name, addr)| {
let target_path = if path == "/" {
format!("/{name}")
} else {
format!("{path}/{name}")
};
let current_addr = self.groups.get(&target_path).copied().unwrap_or(*addr);
(name.clone(), current_addr)
})
.collect();
if group_links.is_empty() && path != "/" {
continue;
}
let mut messages: Vec<(u16, Vec<u8>)> = Vec::new();
let mut link_info = Vec::new();
link_info.push(0); link_info.push(0); let undef_bytes = UNDEF_ADDR.to_le_bytes();
link_info.extend_from_slice(&undef_bytes[..self.sizeof_addr as usize]); link_info.extend_from_slice(&undef_bytes[..self.sizeof_addr as usize]); messages.push((MSG_LINK_INFO, link_info));
for (name, addr) in &group_links {
let link_bytes = encode_link_message(name, *addr, self.sizeof_addr);
messages.push((MSG_LINK, link_bytes));
}
for (parent, link_data) in &self.special_links {
if *parent == path {
messages.push((MSG_LINK, link_data.clone()));
}
}
if path == "/" {
for (msg_type, attr_data) in &self.pending_root_attrs {
messages.push((*msg_type, attr_data.clone()));
}
}
let msg_refs: Vec<(u16, &[u8])> = messages
.iter()
.map(|(t, d)| (*t, d.as_slice()))
.collect();
let oh_bytes = build_v2_object_header(&msg_refs, 0);
let oh_addr = self.allocator.allocate(oh_bytes.len() as u64, 8);
self.write_at(oh_addr, &oh_bytes)?;
self.groups.insert(path.clone(), oh_addr);
}
let root_addr = *self.groups.get("/").ok_or_else(|| {
Error::Other("no root group".into())
})?;
let eof = self.allocator.eof();
let sb = Superblock {
version: 2,
sizeof_addr: self.sizeof_addr,
sizeof_size: self.sizeof_size,
status_flags: 0,
base_addr: 0,
ext_addr: UNDEF_ADDR,
eof_addr: eof,
root_addr,
..Default::default()
};
let mut sb_bytes = Vec::new();
sb.write_v2(&mut sb_bytes);
self.write_at(0, &sb_bytes)?;
self.writer.flush()?;
Ok(())
}
fn write_at(&mut self, offset: u64, data: &[u8]) -> Result<()> {
self.writer.seek(SeekFrom::Start(offset))?;
self.writer.write_all(data)?;
Ok(())
}
}