#![allow(dead_code)]
use std::collections::HashMap;
use super::dataset::Hdf5Dataset;
use super::group::Hdf5Group;
use super::types::{AttrValue, Hdf5Dtype, Hdf5Error, Hdf5Result, LockState, ParallelHdf5Meta};
#[derive(Debug, Clone)]
pub struct Hdf5Superblock {
pub version: u8,
pub file_size: u64,
pub root_obj_header_offset: u64,
pub eof_address: u64,
pub size_of_lengths: u8,
pub size_of_offsets: u8,
}
impl Default for Hdf5Superblock {
fn default() -> Self {
Self {
version: 2,
file_size: 0,
root_obj_header_offset: 512,
eof_address: 0,
size_of_lengths: 8,
size_of_offsets: 8,
}
}
}
#[derive(Debug, Clone)]
pub struct Hdf5ObjectHeader {
pub object_type: String,
pub address: u64,
pub n_messages: u32,
pub header_size: u32,
}
#[derive(Debug, Clone, Default)]
pub struct NamedDatatypeRegistry {
types: HashMap<String, Hdf5Dtype>,
}
impl NamedDatatypeRegistry {
pub fn register(&mut self, name: &str, dtype: Hdf5Dtype) -> Hdf5Result<()> {
if self.types.contains_key(name) {
return Err(Hdf5Error::AlreadyExists(name.to_string()));
}
self.types.insert(name.to_string(), dtype);
Ok(())
}
pub fn get(&self, name: &str) -> Hdf5Result<&Hdf5Dtype> {
self.types
.get(name)
.ok_or_else(|| Hdf5Error::NotFound(format!("named type '{name}'")))
}
pub fn names(&self) -> Vec<String> {
let mut v: Vec<String> = self.types.keys().cloned().collect();
v.sort();
v
}
}
#[derive(Debug, Clone)]
pub struct Hdf5File {
pub filename: String,
pub root: Hdf5Group,
pub superblock: Hdf5Superblock,
pub named_types: NamedDatatypeRegistry,
pub lock_state: LockState,
pub parallel_meta: Option<ParallelHdf5Meta>,
pub write_tick: u64,
}
impl Hdf5File {
pub fn new(filename: &str) -> Self {
Self {
filename: filename.to_string(),
root: Hdf5Group::new("/"),
superblock: Hdf5Superblock::default(),
named_types: NamedDatatypeRegistry::default(),
lock_state: LockState::Unlocked,
parallel_meta: None,
write_tick: 0,
}
}
pub fn lock_write(&mut self, owner_id: u64) -> Hdf5Result<()> {
match self.lock_state {
LockState::Unlocked => {
self.lock_state = LockState::WriteLocked { owner_id };
Ok(())
}
_ => Err(Hdf5Error::FileLocked),
}
}
pub fn unlock(&mut self) {
self.lock_state = LockState::Unlocked;
}
pub fn lock_read(&mut self) -> Hdf5Result<()> {
match self.lock_state {
LockState::Unlocked => {
self.lock_state = LockState::ReadLocked { n_readers: 1 };
Ok(())
}
LockState::ReadLocked { n_readers } => {
self.lock_state = LockState::ReadLocked {
n_readers: n_readers + 1,
};
Ok(())
}
LockState::WriteLocked { .. } => Err(Hdf5Error::FileLocked),
}
}
pub fn is_locked(&self) -> bool {
matches!(self.lock_state, LockState::WriteLocked { .. })
}
pub fn update_eof(&mut self, new_eof: u64) {
self.superblock.eof_address = new_eof;
self.superblock.file_size = new_eof;
}
pub fn create_group(&mut self, path: &str) -> Hdf5Result<()> {
if self.is_locked() {
return Err(Hdf5Error::FileLocked);
}
let parts = split_path(path);
let mut current = &mut self.root;
for part in parts {
if !current.groups.contains_key(part) {
current
.groups
.insert(part.to_string(), Hdf5Group::new(part));
}
current = current
.groups
.get_mut(part)
.unwrap_or_else(|| unreachable!());
}
Ok(())
}
pub fn open_group(&self, path: &str) -> Hdf5Result<&Hdf5Group> {
let parts = split_path(path);
let mut current = &self.root;
for part in parts {
current = current
.groups
.get(part)
.ok_or_else(|| Hdf5Error::NotFound(format!("group '{path}'")))?;
}
Ok(current)
}
pub fn open_group_mut(&mut self, path: &str) -> Hdf5Result<&mut Hdf5Group> {
if self.is_locked() {
return Err(Hdf5Error::FileLocked);
}
let parts = split_path(path);
let mut current = &mut self.root;
for part in parts {
current = current
.groups
.get_mut(part)
.ok_or_else(|| Hdf5Error::NotFound(format!("group '{path}'")))?;
}
Ok(current)
}
#[allow(clippy::too_many_arguments)]
pub fn create_dataset(
&mut self,
group_path: &str,
name: &str,
shape: Vec<usize>,
dtype: Hdf5Dtype,
) -> Hdf5Result<()> {
if self.is_locked() {
return Err(Hdf5Error::FileLocked);
}
self.write_tick += 1;
let group = self.open_group_mut(group_path)?;
group.create_dataset(name, shape, dtype)
}
pub fn open_dataset(&self, group_path: &str, name: &str) -> Hdf5Result<&Hdf5Dataset> {
let group = self.open_group(group_path)?;
group.open_dataset(name)
}
pub fn open_dataset_mut(
&mut self,
group_path: &str,
name: &str,
) -> Hdf5Result<&mut Hdf5Dataset> {
if self.is_locked() {
return Err(Hdf5Error::FileLocked);
}
let group = self.open_group_mut(group_path)?;
group.open_dataset_mut(name)
}
pub fn set_dataset_attr(
&mut self,
group_path: &str,
dataset: &str,
attr_name: &str,
value: AttrValue,
) -> Hdf5Result<()> {
if self.is_locked() {
return Err(Hdf5Error::FileLocked);
}
let ds = self.open_dataset_mut(group_path, dataset)?;
ds.set_attr(attr_name, value);
Ok(())
}
pub fn get_dataset_attr(
&self,
group_path: &str,
dataset: &str,
attr_name: &str,
) -> Hdf5Result<&AttrValue> {
let ds = self.open_dataset(group_path, dataset)?;
ds.get_attr(attr_name)
}
pub fn commit_datatype(&mut self, name: &str, dtype: Hdf5Dtype) -> Hdf5Result<()> {
self.named_types.register(name, dtype)
}
pub fn find_named_type(&self, name: &str) -> Hdf5Result<&Hdf5Dtype> {
self.named_types.get(name)
}
pub fn create_soft_link(
&mut self,
group_path: &str,
link_name: &str,
target: &str,
) -> Hdf5Result<()> {
if self.is_locked() {
return Err(Hdf5Error::FileLocked);
}
let group = self.open_group_mut(group_path)?;
group.create_soft_link(link_name, target)
}
pub fn create_hard_link(
&mut self,
group_path: &str,
link_name: &str,
target: &str,
) -> Hdf5Result<()> {
if self.is_locked() {
return Err(Hdf5Error::FileLocked);
}
let group = self.open_group_mut(group_path)?;
group.create_hard_link(link_name, target)
}
pub fn init_parallel(&mut self, n_ranks: usize) {
self.parallel_meta = Some(ParallelHdf5Meta::new(n_ranks));
}
pub fn record_rank_bytes(&mut self, rank: usize, bytes: u64) {
if let Some(ref mut meta) = self.parallel_meta {
meta.record_rank_bytes(rank, bytes);
}
}
}
pub(crate) fn split_path(path: &str) -> Vec<&str> {
path.split('/').filter(|s| !s.is_empty()).collect()
}