#![allow(dead_code, non_snake_case)]
use std::borrow::Cow;
use std::collections::{BTreeMap, BTreeSet};
use std::fmt;
use std::io::Cursor;
use crate::error::{Error, Result};
use crate::format::superblock::Superblock;
use crate::io::reader::HdfReader;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FileApiState {
pub id: u64,
pub name: String,
pub actual_name: String,
pub intent: u32,
pub eof: u64,
pub eoa: u64,
pub base_addr: u64,
pub low_bound: u8,
pub high_bound: u8,
pub nopen_objs: usize,
pub nrefs: usize,
pub fileno: u64,
pub flags: u64,
pub mdc_hit_rate_resets: usize,
pub page_buffer_stats_resets: usize,
pub file_locking: bool,
pub swmr_write: bool,
pub metadata_logging: bool,
pub coll_metadata_reads: bool,
pub mpi_atomicity: bool,
pub vol_obj: Option<u64>,
pub mounts: BTreeSet<String>,
pub open_ids: BTreeSet<u64>,
pub object_ids: BTreeSet<u64>,
pub image: Vec<u8>,
pub accum: Vec<u8>,
pub super_ext_addr: Option<u64>,
pub sohm_addr: Option<u64>,
pub sohm_vers: u8,
pub sohm_nindexes: u8,
pub store_msg_crt_idx: bool,
pub retries: u32,
}
impl Default for FileApiState {
fn default() -> Self {
Self {
id: 0,
name: String::new(),
actual_name: String::new(),
intent: 0,
eof: 0,
eoa: 0,
base_addr: 0,
low_bound: 0,
high_bound: u8::MAX,
nopen_objs: 0,
nrefs: 1,
fileno: 0,
flags: 0,
mdc_hit_rate_resets: 0,
page_buffer_stats_resets: 0,
file_locking: true,
swmr_write: false,
metadata_logging: false,
coll_metadata_reads: false,
mpi_atomicity: false,
vol_obj: None,
mounts: BTreeSet::new(),
open_ids: BTreeSet::new(),
object_ids: BTreeSet::new(),
image: Vec::new(),
accum: Vec::new(),
super_ext_addr: None,
sohm_addr: None,
sohm_vers: 0,
sohm_nindexes: 0,
store_msg_crt_idx: false,
retries: 0,
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct FilePackageState {
pub initialized: bool,
pub next_id: u64,
pub files: BTreeMap<u64, FileApiState>,
pub file_locks: Option<bool>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ExternalFileCache {
pub max_nfiles: usize,
pub files: BTreeMap<String, FileApiState>,
pub close_attempts: usize,
}
#[derive(Debug, Clone, Copy, Default, PartialEq)]
pub struct FileMetadataQueryStats {
pub mdc_hit_rate: f64,
pub mdc_hit_rate_resets: usize,
pub page_buffer_stats_resets: usize,
pub nopen_objs: usize,
pub nrefs: usize,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct FileApiSuperblockInfo {
pub version: u8,
pub size: u64,
pub extension_size: u64,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct FileApiFreeSpaceInfo {
pub version: u8,
pub metadata_size: u64,
pub total_space: u64,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct FileApiSharedMessageInfo {
pub header_size: u64,
pub message_info_size: u64,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct FileApiInfo {
pub superblock: FileApiSuperblockInfo,
pub free_space: FileApiFreeSpaceInfo,
pub shared_messages: FileApiSharedMessageInfo,
}
pub const H5F_NUM_METADATA_READ_RETRY_TYPES: usize = 21;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FileMetadataReadRetryInfo {
pub nbins: usize,
pub retries: [u32; H5F_NUM_METADATA_READ_RETRY_TYPES],
}
impl Default for FileMetadataReadRetryInfo {
fn default() -> Self {
Self {
nbins: H5F_NUM_METADATA_READ_RETRY_TYPES,
retries: [0; H5F_NUM_METADATA_READ_RETRY_TYPES],
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct PageBufferQueryStats {
pub accesses: usize,
pub hits: usize,
pub misses: usize,
pub evictions: usize,
pub bypasses: usize,
pub resets: usize,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct FileLoggingFlags {
pub swmr_write: bool,
pub metadata_logging: bool,
pub coll_metadata_reads: bool,
pub mpi_atomicity: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DriverInfoBlock<'a> {
pub version: u8,
pub name: [u8; 8],
pub data: Cow<'a, [u8]>,
}
#[derive(Debug, Clone)]
pub struct SuperblockCacheImage<'a> {
pub superblock: Superblock,
pub raw: Cow<'a, [u8]>,
}
fn unsupported_file(name: &str) -> Error {
Error::Unsupported(format!(
"{name} requires libhdf5 file-driver behavior not implemented in pure-Rust mode"
))
}
pub fn H5F_init() -> FilePackageState {
H5F__init_package()
}
pub fn H5F__init_package() -> FilePackageState {
FilePackageState {
initialized: true,
next_id: 1,
..FilePackageState::default()
}
}
pub fn H5F_term_package(pkg: &mut FilePackageState) {
pkg.initialized = false;
pkg.files.clear();
}
pub fn H5F__alloc(file: &mut FileApiState, size: u64) -> Result<u64> {
if size == 0 {
return Err(Error::InvalidFormat("zero-byte H5F allocation".into()));
}
let addr = file.eoa;
file.eoa = file
.eoa
.checked_add(size)
.ok_or_else(|| Error::InvalidFormat("H5F allocation overflow".into()))?;
file.eof = file.eof.max(file.eoa);
Ok(addr)
}
pub fn H5F__free(_file: &mut FileApiState, _addr: u64, _size: u64) -> Result<()> {
Ok(())
}
pub fn H5F__try_extend(file: &mut FileApiState, addr: u64, old_size: u64, new_size: u64) -> bool {
addr.checked_add(old_size) == Some(file.eoa) && new_size >= old_size
}
pub fn H5F_cwfs_add(file: &mut FileApiState, addr: u64) {
file.open_ids.insert(addr);
}
pub fn H5F__get_all_count_cb(file: &FileApiState) -> usize {
file.object_ids.len()
}
pub fn H5F__get_all_ids_cb_into(file: &FileApiState, out: &mut Vec<u64>) -> usize {
H5F_get_obj_ids_into(file, out)
}
pub fn H5F__visit_all_ids_cb<F>(file: &FileApiState, visitor: F)
where
F: FnMut(u64),
{
H5F_visit_obj_ids(file, visitor);
}
pub fn H5Fis_accessible(container_name: &str, _fapl_id: u64) -> Result<bool> {
let file = match std::fs::File::open(container_name) {
Ok(file) => file,
Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(false),
Err(err) => return Err(Error::Io(err)),
};
let mut reader = HdfReader::new(file);
match Superblock::read(&mut reader) {
Ok(_) => Ok(true),
Err(Error::InvalidFormat(_)) | Err(Error::Unsupported(_)) => Ok(false),
Err(err) => Err(err),
}
}
pub fn H5F__post_open_api_common(file: FileApiState) -> FileApiState {
file
}
pub fn H5F__create_api_common(pkg: &mut FilePackageState, name: &str, intent: u32) -> u64 {
let id = pkg.next_id.max(1);
pkg.next_id = id.saturating_add(1);
let file = FileApiState {
id,
name: name.to_string(),
actual_name: name.to_string(),
intent,
fileno: id,
..FileApiState::default()
};
pkg.files.insert(id, file);
id
}
pub fn H5Fcreate(pkg: &mut FilePackageState, name: &str, intent: u32) -> Result<u64> {
Ok(H5F__create_api_common(pkg, name, intent))
}
pub fn H5Fopen(pkg: &FilePackageState, name: &str) -> Result<FileApiState> {
pkg.files
.values()
.find(|file| file.name == name || file.actual_name == name)
.cloned()
.map(H5F__post_open_api_common)
.ok_or_else(|| unsupported_file("H5Fopen"))
}
pub fn H5F__flush_api_common(file: &mut FileApiState) {
H5F__flush(file);
}
pub fn H5Fflush(file: &mut FileApiState) -> Result<()> {
H5F__flush_api_common(file);
Ok(())
}
pub fn H5Fclose(file: FileApiState) -> Result<()> {
H5F__close(file);
Ok(())
}
pub fn H5Fmount(file: &mut FileApiState, name: &str) -> Result<()> {
H5F_mount(file, name);
Ok(())
}
pub fn H5Funmount(file: &mut FileApiState, name: &str) -> Result<()> {
H5F_unmount(file, name);
Ok(())
}
pub fn H5F__reopen_api_common(file: &FileApiState) -> FileApiState {
H5F_open(file)
}
pub fn H5Freopen(file: &FileApiState) -> FileApiState {
H5F_open(file)
}
pub fn H5Freopen_async(_file: &FileApiState) -> Result<FileApiState> {
Err(unsupported_file("H5Freopen_async"))
}
pub fn H5Fcreate_async(_pkg: &mut FilePackageState, _name: &str, _intent: u32) -> Result<u64> {
Err(unsupported_file("H5Fcreate_async"))
}
pub fn H5Fopen_async(_file: &FileApiState) -> Result<FileApiState> {
Err(unsupported_file("H5Fopen_async"))
}
pub fn H5Fflush_async(_file: &mut FileApiState) -> Result<()> {
Err(unsupported_file("H5Fflush_async"))
}
pub fn H5Fclose_async(_file: FileApiState) -> Result<()> {
Err(unsupported_file("H5Fclose_async"))
}
pub fn H5Fdelete(filename: &str) -> Result<()> {
std::fs::remove_file(filename)?;
Ok(())
}
pub fn H5Freset_mdc_hit_rate_stats(file: &mut FileApiState) {
file.mdc_hit_rate_resets = file.mdc_hit_rate_resets.saturating_add(1);
}
pub fn H5F_get_mdc_hit_rate(file: &FileApiState) -> f64 {
if file.mdc_hit_rate_resets == 0 {
0.0
} else {
1.0
}
}
pub fn H5F_get_metadata_query_stats(file: &FileApiState) -> FileMetadataQueryStats {
FileMetadataQueryStats {
mdc_hit_rate: H5F_get_mdc_hit_rate(file),
mdc_hit_rate_resets: file.mdc_hit_rate_resets,
page_buffer_stats_resets: file.page_buffer_stats_resets,
nopen_objs: file.nopen_objs,
nrefs: file.nrefs,
}
}
pub fn H5Fget_metadata_read_retry_info(file: &FileApiState) -> FileMetadataReadRetryInfo {
FileMetadataReadRetryInfo {
retries: [file.retries; H5F_NUM_METADATA_READ_RETRY_TYPES],
..FileMetadataReadRetryInfo::default()
}
}
pub fn H5Fget_metadata_read_retry_info_into(
file: &FileApiState,
out: &mut FileMetadataReadRetryInfo,
) {
*out = H5Fget_metadata_read_retry_info(file);
}
pub fn H5F_get_page_buffering_stats(file: &FileApiState) -> PageBufferQueryStats {
PageBufferQueryStats {
resets: file.page_buffer_stats_resets,
..PageBufferQueryStats::default()
}
}
pub fn H5Fget_page_buffering_stats(file: &FileApiState) -> PageBufferQueryStats {
H5F_get_page_buffering_stats(file)
}
pub fn H5F_get_swmr_write(file: &FileApiState) -> bool {
file.swmr_write
}
pub fn H5F_get_mdc_logging_status(file: &FileApiState) -> FileLoggingFlags {
FileLoggingFlags {
swmr_write: file.swmr_write,
metadata_logging: file.metadata_logging,
coll_metadata_reads: file.coll_metadata_reads,
mpi_atomicity: file.mpi_atomicity,
}
}
pub fn H5Fget_mdc_logging_status(file: &FileApiState) -> FileLoggingFlags {
H5F_get_mdc_logging_status(file)
}
pub fn H5Fget_mdc_logging_status_into(
file: &FileApiState,
is_enabled: &mut bool,
is_currently_logging: &mut bool,
) {
let flags = H5Fget_mdc_logging_status(file);
*is_enabled = flags.metadata_logging;
*is_currently_logging = flags.metadata_logging;
}
pub fn H5Fget_mdc_flush_disabled(_file: &FileApiState) -> Result<bool> {
Err(unsupported_file("H5Fget_mdc_flush_disabled"))
}
pub fn H5Fget_mdc_flush_disabled_into(_file: &FileApiState, _is_disabled: &mut bool) -> Result<()> {
Err(unsupported_file("H5Fget_mdc_flush_disabled"))
}
pub fn H5Fget_mdc_config(_file: &FileApiState) -> Result<()> {
Err(unsupported_file("H5Fget_mdc_config"))
}
pub fn H5Fset_mdc_config(_file: &mut FileApiState) -> Result<()> {
Err(unsupported_file("H5Fset_mdc_config"))
}
pub fn H5Fget_mdc_size(_file: &FileApiState) -> Result<()> {
Err(unsupported_file("H5Fget_mdc_size"))
}
pub fn H5Fget_mdc_size_into(
_file: &FileApiState,
_max_size: &mut usize,
_min_clean_size: &mut usize,
_cur_size: &mut usize,
_cur_num_entries: &mut i32,
) -> Result<()> {
Err(unsupported_file("H5Fget_mdc_size"))
}
pub fn H5Fget_mdc_image_info(_file: &FileApiState) -> Result<()> {
Err(unsupported_file("H5Fget_mdc_image_info"))
}
pub fn H5Fget_mdc_image_info_into(
_file: &FileApiState,
_image_addr: &mut u64,
_image_size: &mut u64,
) -> Result<()> {
Err(unsupported_file("H5Fget_mdc_image_info"))
}
pub fn H5Fget_dset_no_attrs_hint(_file: &FileApiState) -> Result<()> {
Err(unsupported_file("H5Fget_dset_no_attrs_hint"))
}
pub fn H5Fget_dset_no_attrs_hint_into(_file: &FileApiState, _minimize: &mut bool) -> Result<()> {
Err(unsupported_file("H5Fget_dset_no_attrs_hint"))
}
pub fn H5Fset_dset_no_attrs_hint(_file: &mut FileApiState, _minimize: bool) -> Result<()> {
Err(unsupported_file("H5Fset_dset_no_attrs_hint"))
}
pub fn H5Fclear_elink_file_cache(_file: &mut FileApiState) -> Result<()> {
Err(unsupported_file("H5Fclear_elink_file_cache"))
}
pub fn H5Fstart_swmr_write(file: &mut FileApiState) -> Result<()> {
H5F__start_swmr_write(file);
Ok(())
}
pub fn H5Fstart_mdc_logging(file: &mut FileApiState) -> Result<()> {
file.metadata_logging = true;
Ok(())
}
pub fn H5Fstop_mdc_logging(file: &mut FileApiState) -> Result<()> {
file.metadata_logging = false;
Ok(())
}
pub fn H5Fformat_convert(_file: &mut FileApiState) -> Result<()> {
Err(unsupported_file("H5Fformat_convert"))
}
pub fn H5Fset_libver_bounds(file: &mut FileApiState, low: u8, high: u8) -> Result<()> {
if low > high {
return Err(Error::InvalidFormat(format!(
"low libver bound {low} exceeds high bound {high}"
)));
}
H5F__set_libver_bounds(file, low, high);
Ok(())
}
pub fn H5Fset_latest_format(file: &mut FileApiState, latest_format: bool) -> Result<()> {
if latest_format {
H5F__set_libver_bounds(file, file.low_bound, u8::MAX);
}
Ok(())
}
pub fn H5Freset_page_buffering_stats(file: &mut FileApiState) {
file.page_buffer_stats_resets = file.page_buffer_stats_resets.saturating_add(1);
}
pub fn H5Fincrement_filesize(file: &mut FileApiState, increment: u64) -> Result<u64> {
file.eof = file
.eof
.checked_add(increment)
.ok_or_else(|| Error::InvalidFormat("H5F filesize overflow".into()))?;
Ok(file.eof)
}
pub fn H5F__cache_superblock_get_initial_load_size() -> usize {
8
}
pub fn H5F__cache_superblock_get_final_load_size(image: &[u8]) -> usize {
image.len()
}
pub fn H5F__cache_superblock_verify_chksum(_image: &[u8]) -> bool {
H5F__is_hdf5(_image)
}
pub fn H5F__cache_superblock_deserialize(image: &[u8]) -> Result<SuperblockCacheImage<'_>> {
let mut reader = HdfReader::new(Cursor::new(image));
let superblock = Superblock::read(&mut reader)?;
let size = superblock.checked_size()?;
if image.len() < size {
return Err(Error::InvalidFormat(
"cached superblock image is truncated".into(),
));
}
Ok(SuperblockCacheImage {
superblock,
raw: Cow::Borrowed(&image[..size]),
})
}
pub fn H5F__cache_superblock_image_len(image: &SuperblockCacheImage<'_>) -> usize {
image.raw.len()
}
pub fn H5F__cache_superblock_free_icr(_image: SuperblockCacheImage<'_>) {}
pub fn H5F__cache_drvrinfo_get_initial_load_size() -> usize {
16
}
pub fn H5F__cache_drvrinfo_get_final_load_size(image: &[u8]) -> Result<usize> {
let len = decode_driver_info_block_payload_len(image)?;
16usize
.checked_add(len)
.ok_or_else(|| Error::InvalidFormat("driver info cache block size overflow".into()))
}
pub fn H5F__cache_drvrinfo_deserialize(image: &[u8]) -> Result<DriverInfoBlock<'_>> {
let len = decode_driver_info_block_payload_len(image)?;
let total = 16usize
.checked_add(len)
.ok_or_else(|| Error::InvalidFormat("driver info cache block size overflow".into()))?;
if image.len() != total {
return Err(Error::InvalidFormat(format!(
"driver info cache block length mismatch: expected {total}, got {}",
image.len()
)));
}
let mut name = [0u8; 8];
name.copy_from_slice(&image[8..16]);
Ok(DriverInfoBlock {
version: image[0],
name,
data: Cow::Borrowed(&image[16..total]),
})
}
pub fn H5F__cache_drvrinfo_image_len(block: &DriverInfoBlock<'_>) -> Result<usize> {
let len = driver_info_block_payload_len_u32(block)?;
16usize
.checked_add(len as usize)
.ok_or_else(|| Error::InvalidFormat("driver info cache block size overflow".into()))
}
pub fn H5F__cache_drvrinfo_serialize_into(
block: &DriverInfoBlock<'_>,
out: &mut Vec<u8>,
) -> Result<usize> {
if block.version != 0 {
return Err(Error::InvalidFormat(format!(
"unsupported driver info cache block version {}",
block.version
)));
}
let len = driver_info_block_payload_len_u32(block)?;
let total = H5F__cache_drvrinfo_image_len(block)?;
out.clear();
out.reserve(total);
out.push(block.version);
out.extend_from_slice(&[0, 0, 0]);
out.extend_from_slice(&len.to_le_bytes());
out.extend_from_slice(&block.name);
out.extend_from_slice(&block.data);
Ok(out.len())
}
pub fn H5F__cache_drvrinfo_free_icr(_block: DriverInfoBlock<'_>) {}
fn decode_driver_info_block_payload_len(image: &[u8]) -> Result<usize> {
if image.len() < 16 {
return Err(Error::InvalidFormat(
"truncated driver info cache block prefix".into(),
));
}
if image[0] != 0 {
return Err(Error::InvalidFormat(format!(
"unsupported driver info cache block version {}",
image[0]
)));
}
Ok(u32::from_le_bytes([image[4], image[5], image[6], image[7]]) as usize)
}
fn driver_info_block_payload_len_u32(block: &DriverInfoBlock<'_>) -> Result<u32> {
u32::try_from(block.data.len())
.map_err(|_| Error::InvalidFormat("driver info cache block payload too large".into()))
}
pub fn H5F__close_cb(_file: FileApiState) {}
pub fn H5F__parse_file_lock_env_var(value: Option<&str>) -> Result<Option<bool>> {
match value {
None => Ok(None),
Some("0") => Ok(Some(false)),
Some("1") => Ok(Some(true)),
Some(value) if value.eq_ignore_ascii_case("FALSE") => Ok(Some(false)),
Some(value) if value.eq_ignore_ascii_case("TRUE") => Ok(Some(true)),
Some(value) if value.eq_ignore_ascii_case("BEST_EFFORT") => Ok(Some(true)),
Some(other) => Err(Error::InvalidFormat(format!(
"invalid HDF5 file locking environment value '{other}'"
))),
}
}
pub fn H5F__set_vol_conn(file: &mut FileApiState, vol_obj: u64) {
file.vol_obj = Some(vol_obj);
}
pub fn H5F_get_access_plist(file: &FileApiState) -> u32 {
file.intent
}
pub fn H5Fget_access_plist(file: &FileApiState) -> u32 {
H5F_get_access_plist(file)
}
pub fn H5Fget_intent(file: &FileApiState) -> u32 {
H5F_shared_get_intent(file)
}
pub fn H5Fget_intent_into(file: &FileApiState, out: &mut u32) {
*out = H5Fget_intent(file);
}
pub fn H5F_get_obj_count(file: &FileApiState) -> usize {
file.object_ids.len()
}
pub fn H5Fget_obj_count(file: &FileApiState) -> usize {
H5F_get_obj_count(file)
}
pub fn H5F_get_obj_ids_into(file: &FileApiState, out: &mut Vec<u64>) -> usize {
out.clear();
out.extend(file.object_ids.iter().copied());
out.len()
}
pub fn H5Fget_obj_ids_into(file: &FileApiState, out: &mut Vec<u64>) -> usize {
H5F_get_obj_ids_into(file, out)
}
pub fn H5F_visit_obj_ids<F>(file: &FileApiState, mut visitor: F)
where
F: FnMut(u64),
{
for id in &file.object_ids {
visitor(*id);
}
}
pub fn H5F__get_objects_into(file: &FileApiState, out: &mut Vec<u64>) -> usize {
H5F_get_obj_ids_into(file, out)
}
pub fn H5F__visit_objects<F>(file: &FileApiState, visitor: F)
where
F: FnMut(u64),
{
H5F_visit_obj_ids(file, visitor);
}
pub fn H5F__get_objects_cb(file: &FileApiState) -> usize {
H5F_get_obj_count(file)
}
pub fn H5F__build_name_cow<'a>(prefix: &str, name: &'a str) -> Cow<'a, str> {
if prefix.is_empty() {
Cow::Borrowed(name)
} else {
Cow::Owned(format!("{prefix}/{name}"))
}
}
pub fn H5F__build_name_into(prefix: &str, name: &str, out: &mut String) {
out.clear();
if prefix.is_empty() {
out.push_str(name);
} else {
out.push_str(prefix);
out.push('/');
out.push_str(name);
}
}
pub fn H5F__getenv_prefix_name_cow(value: Option<&str>) -> Option<Cow<'_, str>> {
value.map(Cow::Borrowed)
}
pub fn H5F_prefix_open_file_cow<'a>(prefix: &str, name: &'a str) -> Cow<'a, str> {
H5F__build_name_cow(prefix, name)
}
pub fn H5F_prefix_open_file_into(prefix: &str, name: &str, out: &mut String) {
H5F__build_name_into(prefix, name, out);
}
pub fn H5F__is_hdf5(image: &[u8]) -> bool {
image.starts_with(b"\x89HDF\r\n\x1a\n")
}
pub fn H5F__dest(_file: FileApiState) {}
pub fn H5F__check_if_using_file_locks(file: &FileApiState) -> bool {
file.file_locking
}
pub fn H5F_open(file: &FileApiState) -> FileApiState {
let mut reopened = FileApiState::default();
H5F_open_into(file, &mut reopened);
reopened
}
pub fn H5F_open_into(file: &FileApiState, out: &mut FileApiState) {
out.clone_from(file);
out.nrefs = out.nrefs.saturating_add(1);
}
pub fn H5F__post_open(file: FileApiState) -> FileApiState {
file
}
pub fn H5F__flush_phase1(_file: &mut FileApiState) {}
pub fn H5F__flush_phase2(_file: &mut FileApiState) {}
pub fn H5F__flush(file: &mut FileApiState) {
file.eof = file.eof.max(file.eoa);
}
pub fn H5F__close(_file: FileApiState) {}
pub fn H5F__delete(pkg: &mut FilePackageState, id: u64) -> Option<FileApiState> {
pkg.files.remove(&id)
}
pub fn H5F_try_close(file: &mut FileApiState) -> bool {
file.nopen_objs == 0
}
pub fn H5F_get_id(file: &FileApiState) -> u64 {
file.id
}
pub fn H5F_incr_nopen_objs(file: &mut FileApiState) -> usize {
file.nopen_objs = file.nopen_objs.saturating_add(1);
file.nopen_objs
}
pub fn H5F_decr_nopen_objs(file: &mut FileApiState) -> usize {
file.nopen_objs = file.nopen_objs.saturating_sub(1);
file.nopen_objs
}
pub fn H5F__build_actual_name(file: &mut FileApiState, name: &str) {
file.actual_name.clear();
file.actual_name.push_str(name);
}
pub fn H5F_set_grp_btree_shared(file: &mut FileApiState, enabled: bool) {
if enabled {
file.flags |= 1;
} else {
file.flags &= !1;
}
}
pub fn H5F_set_sohm_addr(file: &mut FileApiState, addr: u64) {
file.sohm_addr = Some(addr);
}
pub fn H5F_set_sohm_vers(file: &mut FileApiState, version: u8) {
file.sohm_vers = version;
}
pub fn H5F_set_sohm_nindexes(file: &mut FileApiState, nindexes: u8) {
file.sohm_nindexes = nindexes;
}
pub fn H5F_set_store_msg_crt_idx(file: &mut FileApiState, enabled: bool) {
file.store_msg_crt_idx = enabled;
}
pub fn H5F__set_libver_bounds(file: &mut FileApiState, low: u8, high: u8) {
file.low_bound = low;
file.high_bound = high;
}
pub fn H5F__get_file_image_into(file: &FileApiState, out: &mut [u8]) -> Result<usize> {
if out.len() < file.image.len() {
return Err(Error::InvalidFormat(
"H5F file image output buffer is too small".into(),
));
}
out[..file.image.len()].copy_from_slice(&file.image);
Ok(file.image.len())
}
pub fn H5F__get_file_image_size(file: &FileApiState) -> usize {
file.image.len()
}
pub fn H5Fget_file_image_size(file: &FileApiState) -> usize {
H5F__get_file_image_size(file)
}
pub fn H5Fget_file_image_into(file: &FileApiState, out: &mut [u8]) -> Result<usize> {
H5F__get_file_image_into(file, out)
}
pub fn H5F__get_file_image_vec_into(file: &FileApiState, out: &mut Vec<u8>) -> usize {
out.clear();
out.extend_from_slice(&file.image);
out.len()
}
pub fn H5Fget_file_image_vec_into(file: &FileApiState, out: &mut Vec<u8>) -> usize {
H5F__get_file_image_vec_into(file, out)
}
pub fn H5F__get_info(file: &FileApiState) -> (u64, u64) {
(file.eof, file.eoa)
}
pub fn H5Fget_info2(file: &FileApiState) -> FileApiInfo {
FileApiInfo {
superblock: FileApiSuperblockInfo {
extension_size: file
.super_ext_addr
.map(|_| H5F_sizeof_addr() as u64)
.unwrap_or(0),
..FileApiSuperblockInfo::default()
},
free_space: FileApiFreeSpaceInfo {
total_space: H5Fget_freespace(file),
..FileApiFreeSpaceInfo::default()
},
shared_messages: FileApiSharedMessageInfo {
header_size: file
.sohm_addr
.map(|_| H5F_sizeof_addr() as u64)
.unwrap_or(0),
message_info_size: u64::from(file.sohm_nindexes),
},
}
}
pub fn H5Fget_info2_into(file: &FileApiState, out: &mut FileApiInfo) {
*out = H5Fget_info2(file);
}
pub fn H5Fget_info1(file: &FileApiState) -> FileApiInfo {
H5Fget_info2(file)
}
pub fn H5F_set_retries(file: &mut FileApiState, retries: u32) {
file.retries = retries;
}
pub fn H5F__set_eoa(file: &mut FileApiState, eoa: u64) {
file.eoa = eoa;
file.eof = file.eof.max(eoa);
}
pub fn H5F__set_paged_aggr(_file: &mut FileApiState) {}
pub fn H5F__get_max_eof_eoa(file: &FileApiState) -> u64 {
file.eof.max(file.eoa)
}
pub fn H5F__start_swmr_write(file: &mut FileApiState) {
file.swmr_write = true;
}
pub fn H5F__format_convert(file: &mut FileApiState) -> Result<()> {
H5Fformat_convert(file)
}
pub fn H5F_get_file_id(file: &FileApiState) -> u64 {
file.id
}
pub fn H5F__super_ext_create(file: &mut FileApiState, addr: u64) {
file.super_ext_addr = Some(addr);
}
pub fn H5F__super_ext_open(file: &FileApiState) -> Option<u64> {
file.super_ext_addr
}
pub fn H5F__super_ext_close(_file: &mut FileApiState) {}
pub fn H5F__update_super_ext_driver_msg(_file: &mut FileApiState) {}
pub fn H5F__super_init(file: &mut FileApiState) {
file.image.clear();
file.image.extend_from_slice(b"\x89HDF\r\n\x1a\n");
}
pub fn H5F_eoa_dirty(_file: &mut FileApiState) {}
pub fn H5F_super_dirty(_file: &mut FileApiState) {}
pub fn H5F__super_free(file: &mut FileApiState) {
file.super_ext_addr = None;
}
pub fn H5F__super_ext_remove_msg(file: &mut FileApiState) {
file.super_ext_addr = None;
}
pub fn H5F_shared_block_read_into(
file: &FileApiState,
offset: usize,
out: &mut [u8],
) -> Result<()> {
H5F_block_read_into(file, offset, out)
}
pub fn H5F_block_read_into(file: &FileApiState, offset: usize, out: &mut [u8]) -> Result<()> {
let end = offset
.checked_add(out.len())
.ok_or_else(|| Error::InvalidFormat("H5F read overflow".into()))?;
let src = file
.image
.get(offset..end)
.ok_or_else(|| Error::InvalidFormat("H5F read is outside file image".into()))?;
out.copy_from_slice(src);
Ok(())
}
pub fn H5F_shared_block_write(file: &mut FileApiState, offset: usize, data: &[u8]) -> Result<()> {
H5F_block_write(file, offset, data)
}
pub fn H5F_block_write(file: &mut FileApiState, offset: usize, data: &[u8]) -> Result<()> {
let end = offset
.checked_add(data.len())
.ok_or_else(|| Error::InvalidFormat("H5F write overflow".into()))?;
if file.image.len() < end {
file.image.resize(end, 0);
}
file.image[offset..end].copy_from_slice(data);
let end_u64 = usize_to_u64(end, "H5F EOF")?;
file.eof = file.eof.max(end_u64);
Ok(())
}
pub fn H5F_shared_select_read_into(
file: &FileApiState,
spans: &[(usize, usize)],
out: &mut [u8],
) -> Result<()> {
let mut total_len = 0usize;
for &(offset, len) in spans {
let span_end = offset
.checked_add(len)
.ok_or_else(|| Error::InvalidFormat("H5F select input offset overflow".into()))?;
file.image
.get(offset..span_end)
.ok_or_else(|| Error::InvalidFormat("H5F read is outside file image".into()))?;
total_len = total_len
.checked_add(len)
.ok_or_else(|| Error::InvalidFormat("H5F select output offset overflow".into()))?;
}
if total_len != out.len() {
return Err(Error::InvalidFormat(
"H5F select output buffer length does not match requested spans".into(),
));
}
let mut out_offset = 0usize;
for &(offset, len) in spans {
let end = out_offset + len;
out[out_offset..end].copy_from_slice(&file.image[offset..offset + len]);
out_offset = end;
}
Ok(())
}
pub fn H5F_shared_select_write(file: &mut FileApiState, spans: &[(usize, &[u8])]) -> Result<()> {
for &(offset, data) in spans {
H5F_block_write(file, offset, data)?;
}
Ok(())
}
pub fn H5F_shared_vector_read_into(
file: &FileApiState,
spans: &[(usize, usize)],
out: &mut [u8],
) -> Result<()> {
H5F_shared_select_read_into(file, spans, out)
}
pub fn H5F_shared_vector_write(file: &mut FileApiState, spans: &[(usize, &[u8])]) -> Result<()> {
H5F_shared_select_write(file, spans)
}
pub fn H5F_flush_tagged_metadata(file: &mut FileApiState) {
H5F__flush(file);
}
pub fn H5F_get_checksums(file: &FileApiState) -> u32 {
file.image
.iter()
.fold(0u32, |acc, byte| acc.wrapping_add(u32::from(*byte)))
}
pub fn H5F_debug_into<W>(file: &FileApiState, out: &mut W) -> fmt::Result
where
W: fmt::Write + ?Sized,
{
write!(
out,
"H5F(id={}, name={}, eof={})",
file.id, file.name, file.eof
)
}
pub fn H5F_sfile_assert_num(file: &FileApiState, expected: usize) -> bool {
file.open_ids.len() == expected
}
pub fn H5F__sfile_add(file: &mut FileApiState, id: u64) {
file.open_ids.insert(id);
}
pub fn H5F__sfile_search(file: &FileApiState, id: u64) -> bool {
file.open_ids.contains(&id)
}
pub fn H5F__sfile_remove(file: &mut FileApiState, id: u64) {
file.open_ids.remove(&id);
}
pub fn H5F_mpi_get_rank() -> Result<u32> {
Err(unsupported_file("H5F_mpi_get_rank"))
}
pub fn H5F_mpi_get_comm() -> Result<()> {
Err(unsupported_file("H5F_mpi_get_comm"))
}
pub fn H5F_mpi_get_info() -> Result<()> {
Err(unsupported_file("H5F_mpi_get_info"))
}
pub fn H5F_shared_mpi_get_size() -> Result<u64> {
Err(unsupported_file("H5F_shared_mpi_get_size"))
}
pub fn H5F_mpi_get_size() -> Result<u64> {
Err(unsupported_file("H5F_mpi_get_size"))
}
pub fn H5F__set_mpi_atomicity(file: &mut FileApiState, atomicity: bool) {
file.mpi_atomicity = atomicity;
}
pub fn H5Fset_mpi_atomicity(_file: &mut FileApiState, _atomicity: bool) -> Result<()> {
Err(unsupported_file("H5Fset_mpi_atomicity"))
}
pub fn H5F__get_mpi_atomicity(file: &FileApiState) -> bool {
file.mpi_atomicity
}
pub fn H5Fget_mpi_atomicity(_file: &FileApiState) -> Result<bool> {
Err(unsupported_file("H5Fget_mpi_atomicity"))
}
pub fn H5Fget_mpi_atomicity_into(_file: &FileApiState, _atomicity: &mut bool) -> Result<()> {
Err(unsupported_file("H5Fget_mpi_atomicity"))
}
pub fn H5F_mpi_retrieve_comm() -> Result<()> {
Err(unsupported_file("H5F_mpi_retrieve_comm"))
}
pub fn H5F_get_coll_metadata_reads(file: &FileApiState) -> bool {
file.coll_metadata_reads
}
pub fn H5F_shared_get_coll_metadata_reads(file: &FileApiState) -> bool {
file.coll_metadata_reads
}
pub fn H5F_set_coll_metadata_reads(file: &mut FileApiState, value: bool) {
file.coll_metadata_reads = value;
}
pub fn H5F_mpi_get_file_block_type() -> Result<()> {
Err(unsupported_file("H5F_mpi_get_file_block_type"))
}
pub fn H5F__efc_create(max_nfiles: usize) -> ExternalFileCache {
ExternalFileCache {
max_nfiles,
..ExternalFileCache::default()
}
}
pub fn H5F__efc_open_file(cache: &mut ExternalFileCache, name: &str, intent: u32) -> Result<u64> {
let id = cache
.files
.len()
.checked_add(1)
.ok_or_else(|| Error::InvalidFormat("external file cache id overflow".into()))
.and_then(|id| {
u64::try_from(id)
.map_err(|_| Error::InvalidFormat("external file cache id exceeds u64".into()))
})?;
let file = FileApiState {
id,
name: name.to_string(),
actual_name: name.to_string(),
intent,
fileno: id,
..FileApiState::default()
};
H5F__efc_open(cache, file)
}
pub fn H5F__efc_open(cache: &mut ExternalFileCache, file: FileApiState) -> Result<u64> {
if cache.max_nfiles != 0
&& cache.files.len() >= cache.max_nfiles
&& !cache.files.contains_key(&file.name)
{
return Err(Error::InvalidFormat(
"external file cache maximum file count exceeded".into(),
));
}
let id = file.id;
cache.files.insert(file.name.clone(), file);
Ok(id)
}
pub fn H5F_efc_close(cache: &mut ExternalFileCache, name: &str) -> Option<FileApiState> {
cache.close_attempts = cache.close_attempts.saturating_add(1);
cache.files.remove(name)
}
pub fn H5F__efc_max_nfiles(cache: &ExternalFileCache) -> usize {
cache.max_nfiles
}
pub fn H5F__efc_release_real(cache: &mut ExternalFileCache, name: &str) -> Option<FileApiState> {
H5F_efc_close(cache, name)
}
pub fn H5F__efc_release(cache: &mut ExternalFileCache, name: &str) -> Option<FileApiState> {
H5F__efc_release_real(cache, name)
}
pub fn H5F__efc_destroy(cache: &mut ExternalFileCache) {
cache.files.clear();
}
pub fn H5F__efc_remove_ent(cache: &mut ExternalFileCache, name: &str) -> Option<FileApiState> {
cache.files.remove(name)
}
pub fn H5F__efc_try_close_tag1(cache: &mut ExternalFileCache, name: &str) -> bool {
H5F__efc_try_close(cache, name)
}
pub fn H5F__efc_try_close_tag2(cache: &mut ExternalFileCache, name: &str) -> bool {
H5F__efc_try_close(cache, name)
}
pub fn H5F__efc_try_close(cache: &mut ExternalFileCache, name: &str) -> bool {
cache.close_attempts = cache.close_attempts.saturating_add(1);
cache.files.remove(name).is_some()
}
pub fn H5F_fake_alloc(file: &mut FileApiState, size: u64) -> Result<u64> {
H5F__alloc(file, size)
}
pub fn H5F_fake_free(file: &mut FileApiState, addr: u64, size: u64) -> Result<()> {
H5F__free(file, addr, size)
}
pub fn H5F__get_sohm_mesg_count_test(file: &FileApiState) -> u8 {
file.sohm_nindexes
}
pub fn H5F__check_cached_stab_test(_file: &FileApiState) -> bool {
true
}
pub fn H5F__get_maxaddr_test(file: &FileApiState) -> u64 {
H5F__get_max_eof_eoa(file)
}
pub fn H5F__get_sbe_addr_test(file: &FileApiState) -> Option<u64> {
file.super_ext_addr
}
pub fn H5F__same_file_test(left: &FileApiState, right: &FileApiState) -> bool {
left.fileno == right.fileno
}
pub fn H5F__accum_read(file: &FileApiState) -> &[u8] {
&file.accum
}
pub fn H5F__accum_adjust(file: &mut FileApiState, len: usize) {
file.accum.resize(len, 0);
}
pub fn H5F__accum_write(file: &mut FileApiState, data: &[u8]) {
file.accum.clear();
file.accum.extend_from_slice(data);
}
pub fn H5F__accum_free(file: &mut FileApiState) {
file.accum.clear();
}
pub fn H5F__accum_flush(file: &mut FileApiState) -> Result<()> {
let data = std::mem::take(&mut file.accum);
let eof = u64_to_usize(file.eof, "H5F accumulator EOF")?;
match H5F_block_write(file, eof, &data) {
Ok(()) => Ok(()),
Err(err) => {
file.accum = data;
Err(err)
}
}
}
fn usize_to_u64(value: usize, context: &str) -> Result<u64> {
u64::try_from(value).map_err(|_| Error::InvalidFormat(format!("{context} exceeds u64")))
}
fn u64_to_usize(value: u64, context: &str) -> Result<usize> {
usize::try_from(value).map_err(|_| Error::InvalidFormat(format!("{context} exceeds usize")))
}
pub fn H5F__accum_reset(file: &mut FileApiState) {
file.accum.clear();
}
pub fn H5F_shared_get_intent(file: &FileApiState) -> u32 {
file.intent
}
pub fn H5Fget_filesize(file: &FileApiState) -> u64 {
file.eof
}
pub fn H5Fget_filesize_into(file: &FileApiState, out: &mut u64) {
*out = H5Fget_filesize(file);
}
pub fn H5F_get_low_bound(file: &FileApiState) -> u8 {
file.low_bound
}
pub fn H5F_get_high_bound(file: &FileApiState) -> u8 {
file.high_bound
}
pub fn H5F_get_actual_name(file: &FileApiState) -> &str {
&file.actual_name
}
pub fn H5F_get_actual_name_into(file: &FileApiState, out: &mut String) {
out.clear();
out.push_str(&file.actual_name);
}
pub fn H5Fget_name(file: &FileApiState) -> &str {
if file.actual_name.is_empty() {
&file.name
} else {
&file.actual_name
}
}
pub fn H5Fget_name_into(file: &FileApiState, out: &mut String) {
out.clear();
out.push_str(H5Fget_name(file));
}
pub fn H5F_get_extpath(file: &FileApiState) -> &str {
&file.name
}
pub fn H5F_get_extpath_into(file: &FileApiState, out: &mut String) {
out.clear();
out.push_str(&file.name);
}
pub fn H5F_get_shared(file: &FileApiState) -> &FileApiState {
file
}
pub fn H5F_same_shared(left: &FileApiState, right: &FileApiState) -> bool {
left.fileno == right.fileno
}
pub fn H5F_get_nopen_objs(file: &FileApiState) -> usize {
file.nopen_objs
}
pub fn H5F_file_id_exists(pkg: &FilePackageState, id: u64) -> bool {
pkg.files.contains_key(&id)
}
pub fn H5F_get_parent(_file: &FileApiState) -> Option<u64> {
None
}
pub fn H5F_get_nmounts(file: &FileApiState) -> usize {
file.mounts.len()
}
pub fn H5F_get_fcpl(file: &FileApiState) -> u64 {
file.flags
}
pub fn H5Fget_create_plist(file: &FileApiState) -> u64 {
H5F_get_fcpl(file)
}
pub fn H5F_sizeof_addr() -> usize {
std::mem::size_of::<u64>()
}
pub fn H5F_get_sohm_addr(file: &FileApiState) -> Option<u64> {
file.sohm_addr
}
pub fn H5F_get_sohm_vers(file: &FileApiState) -> u8 {
file.sohm_vers
}
pub fn H5F_get_sohm_nindexes(file: &FileApiState) -> u8 {
file.sohm_nindexes
}
pub fn H5F_sym_leaf_k() -> u16 {
32
}
pub fn H5F_get_min_dset_ohdr() -> usize {
0
}
pub fn H5F_kvalue() -> u16 {
32
}
pub fn H5F_get_nrefs(file: &FileApiState) -> usize {
file.nrefs
}
pub fn H5F_rdcc_nslots() -> usize {
521
}
pub fn H5F_rdcc_nbytes() -> usize {
1024 * 1024
}
pub fn H5F_rdcc_w0() -> f64 {
0.75
}
pub fn H5F_get_base_addr(file: &FileApiState) -> u64 {
file.base_addr
}
pub fn H5F_grp_btree_shared(file: &FileApiState) -> bool {
file.flags & 1 != 0
}
pub fn H5F_gc_ref() -> bool {
false
}
pub fn H5F_get_fc_degree() -> u8 {
0
}
pub fn H5F_get_evict_on_close() -> bool {
false
}
pub fn H5F_store_msg_crt_idx(file: &FileApiState) -> bool {
file.store_msg_crt_idx
}
pub fn H5F_shared_has_feature(file: &FileApiState, feature: u64) -> bool {
file.flags & feature != 0
}
pub fn H5F_has_feature(file: &FileApiState, feature: u64) -> bool {
H5F_shared_has_feature(file, feature)
}
pub fn H5F_get_fileno(file: &FileApiState) -> u64 {
file.fileno
}
pub fn H5Fget_fileno(file: &FileApiState) -> u64 {
H5F_get_fileno(file)
}
pub fn H5Fget_fileno_into(file: &FileApiState, out: &mut u64) {
*out = H5Fget_fileno(file);
}
pub fn H5F_shared_get_eoa(file: &FileApiState) -> u64 {
file.eoa
}
pub fn H5F_get_eoa(file: &FileApiState) -> u64 {
file.eoa
}
pub fn H5Fget_eoa(file: &FileApiState) -> u64 {
H5F_get_eoa(file)
}
pub fn H5Fget_freespace(_file: &FileApiState) -> u64 {
0
}
pub fn H5Fget_free_sections(_file: &FileApiState) -> Result<()> {
Err(unsupported_file("H5Fget_free_sections"))
}
pub fn H5Fget_free_sections_into(
_file: &FileApiState,
_sections: &mut Vec<FileApiFreeSpaceInfo>,
) -> Result<()> {
Err(unsupported_file("H5Fget_free_sections"))
}
pub fn H5Fget_mdc_hit_rate(file: &FileApiState) -> f64 {
H5F_get_mdc_hit_rate(file)
}
pub fn H5Fget_mdc_hit_rate_into(file: &FileApiState, out: &mut f64) {
*out = H5Fget_mdc_hit_rate(file);
}
pub fn H5F_get_vfd_handle() -> Result<()> {
Err(unsupported_file("H5F_get_vfd_handle"))
}
pub fn H5Fget_vfd_handle(_file: &FileApiState) -> Result<()> {
Err(unsupported_file("H5Fget_vfd_handle"))
}
pub fn H5Fget_vfd_handle_into(_file: &FileApiState, _handle: &mut Option<u64>) -> Result<()> {
Err(unsupported_file("H5Fget_vfd_handle"))
}
pub fn H5F_is_tmp_addr(_addr: u64) -> bool {
false
}
pub fn H5F_use_tmp_space() -> bool {
false
}
pub fn H5F_shared_get_mpi_file_sync_required() -> bool {
false
}
pub fn H5F_use_mdc_logging(file: &FileApiState) -> bool {
file.metadata_logging
}
pub fn H5F_start_mdc_log_on_access(file: &mut FileApiState) {
file.metadata_logging = true;
}
pub fn H5F_mdc_log_location(file: &FileApiState) -> &str {
&file.name
}
pub fn H5F_get_alignment() -> (u64, u64) {
(1, 1)
}
pub fn H5F_get_threshold() -> u64 {
1
}
pub fn H5F_get_pgend_meta_thres() -> u32 {
0
}
pub fn H5F_get_null_fsm_addr() -> Option<u64> {
None
}
pub fn H5F_get_vol_obj(file: &FileApiState) -> Option<u64> {
file.vol_obj
}
pub fn H5F__get_cont_info(file: &FileApiState) -> (u64, u64) {
(file.base_addr, file.eoa)
}
pub fn H5F_get_use_file_locking(file: &FileApiState) -> bool {
file.file_locking
}
pub fn H5F_has_vector_select_io() -> bool {
true
}
pub fn H5F_get_rfic_flags(file: &FileApiState) -> u64 {
file.flags
}
pub fn H5F__close_mounts(file: &mut FileApiState) {
file.mounts.clear();
}
pub fn H5F_mount(file: &mut FileApiState, name: &str) {
file.mounts.insert(name.to_string());
}
pub fn H5F_unmount(file: &mut FileApiState, name: &str) {
file.mounts.remove(name);
}
pub fn H5F_is_mount(file: &FileApiState, name: &str) -> bool {
file.mounts.contains(name)
}
pub fn H5F__mount_count_ids_recurse(file: &FileApiState) -> usize {
file.mounts.len()
}
pub fn H5F__mount_count_ids(file: &FileApiState) -> usize {
file.mounts.len()
}
pub fn H5F__flush_mounts_recurse(_file: &mut FileApiState) {}
pub fn H5F_flush_mounts(file: &mut FileApiState) {
H5F__flush(file);
}
pub fn H5F_traverse_mount_into(file: &FileApiState, out: &mut Vec<String>) -> usize {
out.clear();
out.extend(file.mounts.iter().cloned());
out.len()
}
pub fn H5F_visit_mounts<'a, F>(file: &'a FileApiState, mut visitor: F)
where
F: FnMut(&'a str),
{
for mount in &file.mounts {
visitor(mount);
}
}
pub fn H5Fis_hdf5(image: &[u8]) -> bool {
H5F__is_hdf5(image)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn file_metadata_queries_reflect_existing_state() {
let mut file = FileApiState::default();
H5Freset_mdc_hit_rate_stats(&mut file);
H5Freset_page_buffering_stats(&mut file);
H5F__start_swmr_write(&mut file);
H5F_start_mdc_log_on_access(&mut file);
H5F_set_coll_metadata_reads(&mut file, true);
H5F__set_mpi_atomicity(&mut file, true);
H5F_incr_nopen_objs(&mut file);
let stats = H5F_get_metadata_query_stats(&file);
assert_eq!(stats.mdc_hit_rate_resets, 1);
assert_eq!(stats.page_buffer_stats_resets, 1);
assert_eq!(stats.nopen_objs, 1);
assert_eq!(H5F_get_page_buffering_stats(&file).resets, 1);
assert_eq!(H5Fget_page_buffering_stats(&file).resets, 1);
assert_eq!(H5Fget_mdc_hit_rate(&file), H5F_get_mdc_hit_rate(&file));
let mut hit_rate = 0.0;
H5Fget_mdc_hit_rate_into(&file, &mut hit_rate);
assert_eq!(hit_rate, H5Fget_mdc_hit_rate(&file));
let flags = H5F_get_mdc_logging_status(&file);
assert_eq!(H5Fget_mdc_logging_status(&file), flags);
assert!(H5F_get_swmr_write(&file));
assert!(flags.swmr_write);
assert!(flags.metadata_logging);
assert!(flags.coll_metadata_reads);
assert!(flags.mpi_atomicity);
let mut is_enabled = false;
let mut is_currently_logging = false;
H5Fget_mdc_logging_status_into(&file, &mut is_enabled, &mut is_currently_logging);
assert!(is_enabled);
assert!(is_currently_logging);
let file = FileApiState::default();
H5Fget_mdc_logging_status_into(&file, &mut is_enabled, &mut is_currently_logging);
assert!(!is_enabled);
assert!(!is_currently_logging);
}
#[test]
fn file_allocation_helpers_preserve_state_on_overflow() {
let mut file = FileApiState {
eof: u64::MAX - 1,
eoa: u64::MAX - 1,
..FileApiState::default()
};
let before = file.clone();
assert!(H5F__alloc(&mut file, 2).is_err());
assert_eq!(file, before);
assert!(H5Fincrement_filesize(&mut file, 2).is_err());
assert_eq!(file, before);
}
#[test]
fn public_create_and_open_route_through_package_state() {
let mut pkg = H5F_init();
let id = H5Fcreate(&mut pkg, "created.h5", 0x12).unwrap();
assert!(H5F_file_id_exists(&pkg, id));
let opened = H5Fopen(&pkg, "created.h5").unwrap();
assert_eq!(opened.id, id);
assert_eq!(opened.name, "created.h5");
assert_eq!(opened.intent, 0x12);
assert_eq!(opened.fileno, id);
pkg.files.get_mut(&id).unwrap().actual_name = "resolved.h5".into();
assert_eq!(H5Fopen(&pkg, "resolved.h5").unwrap().id, id);
assert!(matches!(
H5Fopen(&pkg, "missing.h5"),
Err(Error::Unsupported(_))
));
}
#[test]
fn public_accessibility_probe_uses_superblock_parser() {
let dir = tempfile::tempdir().unwrap();
let valid = dir.path().join("valid.h5");
std::fs::copy("tests/data/hdf5_ref/v4_fixed_array_chunks.h5", &valid).unwrap();
assert!(H5Fis_accessible(valid.to_str().unwrap(), 0).unwrap());
let userblock = dir.path().join("userblock.h5");
let mut prefixed = vec![0u8; 512];
prefixed.extend_from_slice(&std::fs::read(&valid).unwrap());
std::fs::write(&userblock, prefixed).unwrap();
assert!(H5Fis_accessible(userblock.to_str().unwrap(), 0).unwrap());
let invalid = dir.path().join("invalid.bin");
std::fs::write(&invalid, b"not hdf5").unwrap();
assert!(!H5Fis_accessible(invalid.to_str().unwrap(), 0).unwrap());
assert!(!H5Fis_accessible(dir.path().join("missing.h5").to_str().unwrap(), 0).unwrap());
}
#[test]
fn public_flush_and_close_route_through_file_callbacks() {
let mut file = FileApiState {
eoa: 128,
eof: 64,
..FileApiState::default()
};
H5Fflush(&mut file).unwrap();
assert_eq!(file.eof, 128);
H5Fclose(file).unwrap();
}
#[test]
fn public_object_id_queries_route_through_file_state() {
let mut file = FileApiState::default();
file.object_ids.extend([42, 7, 99]);
assert_eq!(H5F_get_obj_count(&file), 3);
assert_eq!(H5Fget_obj_count(&file), 3);
let mut ids = vec![1];
assert_eq!(H5Fget_obj_ids_into(&file, &mut ids), 3);
assert_eq!(ids, vec![7, 42, 99]);
ids.clear();
assert_eq!(H5F__get_all_ids_cb_into(&file, &mut ids), 3);
assert_eq!(ids, vec![7, 42, 99]);
}
#[test]
fn metadata_read_retry_info_public_api_reports_configured_retries() {
let mut file = FileApiState::default();
H5F_set_retries(&mut file, 7);
let info = H5Fget_metadata_read_retry_info(&file);
assert_eq!(info.nbins, H5F_NUM_METADATA_READ_RETRY_TYPES);
assert_eq!(info.retries, [7; H5F_NUM_METADATA_READ_RETRY_TYPES]);
let mut out = FileMetadataReadRetryInfo {
nbins: 1,
retries: [99; H5F_NUM_METADATA_READ_RETRY_TYPES],
};
H5Fget_metadata_read_retry_info_into(&file, &mut out);
assert_eq!(out, info);
}
#[test]
fn public_mpi_and_vfd_handle_apis_are_explicit_unsupported_boundaries() {
let file = FileApiState::default();
assert!(matches!(
H5Fget_mpi_atomicity(&file),
Err(Error::Unsupported(_))
));
let mut atomicity = true;
assert!(matches!(
H5Fget_mpi_atomicity_into(&file, &mut atomicity),
Err(Error::Unsupported(_))
));
assert!(atomicity);
assert!(matches!(
H5Fget_vfd_handle(&file),
Err(Error::Unsupported(_))
));
let mut vfd_handle = Some(42);
let err = H5Fget_vfd_handle_into(&file, &mut vfd_handle).unwrap_err();
assert_eq!(
err.to_string(),
"Unsupported: H5Fget_vfd_handle requires libhdf5 file-driver behavior not implemented in pure-Rust mode"
);
assert_eq!(vfd_handle, Some(42));
assert!(matches!(H5F_get_vfd_handle(), Err(Error::Unsupported(_))));
assert!(matches!(
H5Fget_mdc_config(&file),
Err(Error::Unsupported(_))
));
assert!(matches!(
H5Fget_mdc_flush_disabled(&file),
Err(Error::Unsupported(_))
));
let mut flush_disabled = true;
let err = H5Fget_mdc_flush_disabled_into(&file, &mut flush_disabled).unwrap_err();
assert_eq!(
err.to_string(),
"Unsupported: H5Fget_mdc_flush_disabled requires libhdf5 file-driver behavior not implemented in pure-Rust mode"
);
assert!(flush_disabled);
assert!(matches!(H5Fget_mdc_size(&file), Err(Error::Unsupported(_))));
let mut max_size = 1;
let mut min_clean_size = 2;
let mut cur_size = 3;
let mut cur_num_entries = 4;
let err = H5Fget_mdc_size_into(
&file,
&mut max_size,
&mut min_clean_size,
&mut cur_size,
&mut cur_num_entries,
)
.unwrap_err();
assert_eq!(
err.to_string(),
"Unsupported: H5Fget_mdc_size requires libhdf5 file-driver behavior not implemented in pure-Rust mode"
);
assert_eq!(max_size, 1);
assert_eq!(min_clean_size, 2);
assert_eq!(cur_size, 3);
assert_eq!(cur_num_entries, 4);
assert!(matches!(
H5Fget_mdc_image_info(&file),
Err(Error::Unsupported(_))
));
let mut image_addr = 5;
let mut image_size = 6;
let err = H5Fget_mdc_image_info_into(&file, &mut image_addr, &mut image_size).unwrap_err();
assert_eq!(
err.to_string(),
"Unsupported: H5Fget_mdc_image_info requires libhdf5 file-driver behavior not implemented in pure-Rust mode"
);
assert_eq!(image_addr, 5);
assert_eq!(image_size, 6);
assert!(matches!(
H5Fget_dset_no_attrs_hint(&file),
Err(Error::Unsupported(_))
));
let mut minimize_attrs = true;
let err = H5Fget_dset_no_attrs_hint_into(&file, &mut minimize_attrs).unwrap_err();
assert_eq!(
err.to_string(),
"Unsupported: H5Fget_dset_no_attrs_hint requires libhdf5 file-driver behavior not implemented in pure-Rust mode"
);
assert!(minimize_attrs);
let mut file = FileApiState {
low_bound: 2,
high_bound: 3,
flags: 0x55,
metadata_logging: false,
swmr_write: false,
..file
};
assert!(matches!(
H5Fset_mdc_config(&mut file),
Err(Error::Unsupported(_))
));
assert!(matches!(
H5Fset_dset_no_attrs_hint(&mut file, true),
Err(Error::Unsupported(_))
));
H5Fset_libver_bounds(&mut file, 0, 4).unwrap();
assert_eq!(file.low_bound, 0);
assert_eq!(file.high_bound, 4);
let err = H5Fset_libver_bounds(&mut file, 5, 4).unwrap_err();
assert!(matches!(err, Error::InvalidFormat(_)));
assert_eq!(file.low_bound, 0);
assert_eq!(file.high_bound, 4);
H5Fset_latest_format(&mut file, true).unwrap();
assert_eq!(file.high_bound, u8::MAX);
H5Fstart_swmr_write(&mut file).unwrap();
assert!(file.swmr_write);
H5Fstart_mdc_logging(&mut file).unwrap();
assert!(file.metadata_logging);
H5Fstop_mdc_logging(&mut file).unwrap();
assert!(!file.metadata_logging);
let before_format_convert = file.clone();
assert!(matches!(
H5Fformat_convert(&mut file),
Err(Error::Unsupported(_))
));
assert_eq!(file, before_format_convert);
assert!(matches!(
H5F__format_convert(&mut file),
Err(Error::Unsupported(_))
));
assert_eq!(file, before_format_convert);
assert!(matches!(
H5Fget_free_sections(&file),
Err(Error::Unsupported(_))
));
let mut free_sections = vec![FileApiFreeSpaceInfo {
version: 1,
metadata_size: 2,
total_space: 3,
}];
let err = H5Fget_free_sections_into(&file, &mut free_sections).unwrap_err();
assert_eq!(
err.to_string(),
"Unsupported: H5Fget_free_sections requires libhdf5 file-driver behavior not implemented in pure-Rust mode"
);
assert_eq!(
free_sections,
vec![FileApiFreeSpaceInfo {
version: 1,
metadata_size: 2,
total_space: 3,
}]
);
let dir = tempfile::tempdir().unwrap();
let delete_path = dir.path().join("delete-me.h5");
std::fs::write(&delete_path, b"temporary").unwrap();
H5Fdelete(delete_path.to_str().unwrap()).unwrap();
assert!(!delete_path.exists());
assert!(matches!(
H5Fdelete(delete_path.to_str().unwrap()),
Err(Error::Io(_))
));
assert!(!H5Fis_accessible("maybe.h5", 0).unwrap());
let mut file = FileApiState::default();
H5Fmount(&mut file, "/child").unwrap();
assert!(H5F_is_mount(&file, "/child"));
H5F_mount(&mut file, "/existing");
H5Funmount(&mut file, "/existing").unwrap();
assert!(!H5F_is_mount(&file, "/existing"));
let before_elink_cache = file.clone();
let err = H5Fclear_elink_file_cache(&mut file).unwrap_err();
assert_eq!(
err.to_string(),
"Unsupported: H5Fclear_elink_file_cache requires libhdf5 file-driver behavior not implemented in pure-Rust mode"
);
assert_eq!(file, before_elink_cache);
}
#[test]
fn public_async_file_apis_are_explicit_unsupported_boundaries() {
let mut pkg = H5F_init();
let mut file = FileApiState::default();
for err in [
H5Freopen_async(&file).unwrap_err(),
H5Fcreate_async(&mut pkg, "async.h5", 0).unwrap_err(),
H5Fopen_async(&file).unwrap_err(),
H5Fflush_async(&mut file).unwrap_err(),
H5Fclose_async(file).unwrap_err(),
] {
let message = err.to_string();
assert!(matches!(err, Error::Unsupported(_)));
assert!(
message.contains("libhdf5 file-driver behavior"),
"unexpected unsupported H5F async error: {message}"
);
}
}
#[test]
fn file_image_and_block_reads_support_caller_buffers() {
let file = FileApiState {
image: b"abcdefghijkl".to_vec(),
..FileApiState::default()
};
let mut image = vec![0; file.image.len()];
assert_eq!(
H5F__get_file_image_into(&file, &mut image).unwrap(),
file.image.len()
);
assert_eq!(image, file.image);
let mut short_image = *b"stale";
assert!(H5F__get_file_image_into(&file, &mut short_image).is_err());
assert_eq!(&short_image, b"stale");
assert!(H5Fget_file_image_into(&file, &mut short_image).is_err());
assert_eq!(&short_image, b"stale");
let mut block = [0; 3];
H5F_block_read_into(&file, 2, &mut block).unwrap();
assert_eq!(&block, b"cde");
let mut selected = [0; 5];
H5F_shared_select_read_into(&file, &[(0, 2), (9, 3)], &mut selected).unwrap();
assert_eq!(&selected, b"abjkl");
assert!(H5F_block_read_into(&file, 10, &mut selected).is_err());
let mut stale = *b"stale";
assert!(H5F_shared_select_read_into(&file, &[(0, 2), (11, 2)], &mut stale).is_err());
assert_eq!(&stale, b"stale");
assert!(H5F_shared_vector_read_into(&file, &[(0, 2), (2, 2)], &mut stale).is_err());
assert_eq!(&stale, b"stale");
}
#[test]
fn public_file_query_aliases_match_file_state() {
let mut file = FileApiState {
name: "logical.h5".into(),
actual_name: "resolved.h5".into(),
intent: 0x21,
flags: 0x40,
eof: 99,
eoa: 88,
fileno: 42,
image: b"HDF5 image".to_vec(),
..FileApiState::default()
};
assert_eq!(H5Fget_intent(&file), 0x21);
let mut intent = 0;
H5Fget_intent_into(&file, &mut intent);
assert_eq!(intent, 0x21);
assert_eq!(H5Fget_access_plist(&file), 0x21);
assert_eq!(H5Fget_create_plist(&file), 0x40);
assert_eq!(H5Fget_filesize(&file), 99);
let mut filesize = 0;
H5Fget_filesize_into(&file, &mut filesize);
assert_eq!(filesize, 99);
assert_eq!(H5Fget_eoa(&file), 88);
assert_eq!(H5Fget_freespace(&file), 0);
assert_eq!(H5Fget_fileno(&file), 42);
let mut fileno = 0;
H5Fget_fileno_into(&file, &mut fileno);
assert_eq!(fileno, 42);
assert_eq!(H5Fget_name(&file), "resolved.h5");
let mut name = String::new();
H5Fget_name_into(&file, &mut name);
assert_eq!(name, "resolved.h5");
let mut actual_name = String::from("stale");
H5F_get_actual_name_into(&file, &mut actual_name);
assert_eq!(actual_name, "resolved.h5");
let mut extpath = String::from("stale");
H5F_get_extpath_into(&file, &mut extpath);
assert_eq!(extpath, "logical.h5");
file.actual_name.clear();
assert_eq!(H5Fget_name(&file), "logical.h5");
H5Fget_name_into(&file, &mut name);
assert_eq!(name, "logical.h5");
H5F_get_actual_name_into(&file, &mut actual_name);
assert!(actual_name.is_empty());
assert_eq!(H5Fget_file_image_size(&file), file.image.len());
let mut image = vec![0; file.image.len()];
assert_eq!(
H5Fget_file_image_into(&file, &mut image).unwrap(),
file.image.len()
);
assert_eq!(image, file.image);
let mut copied = Vec::new();
assert_eq!(
H5Fget_file_image_vec_into(&file, &mut copied),
file.image.len()
);
assert_eq!(copied, file.image);
}
#[test]
fn public_file_info_queries_report_supported_state_and_zero_unknowns() {
let file = FileApiState {
eoa: 128,
super_ext_addr: Some(64),
sohm_addr: Some(96),
sohm_nindexes: 3,
..FileApiState::default()
};
let info = H5Fget_info2(&file);
assert_eq!(info.superblock.extension_size, H5F_sizeof_addr() as u64);
assert_eq!(info.free_space.total_space, 0);
assert_eq!(info.shared_messages.header_size, H5F_sizeof_addr() as u64);
assert_eq!(info.shared_messages.message_info_size, 3);
let mut info_out = FileApiInfo::default();
H5Fget_info2_into(&file, &mut info_out);
assert_eq!(info_out, info);
assert_eq!(H5Fget_info1(&file), info);
}
#[test]
fn object_id_helpers_support_reused_storage_and_visitors() {
let mut file = FileApiState::default();
file.object_ids.insert(7);
file.object_ids.insert(3);
let mut ids = vec![99];
assert_eq!(H5F_get_obj_ids_into(&file, &mut ids), 2);
assert_eq!(ids, vec![3, 7]);
ids.push(99);
assert_eq!(H5F__get_all_ids_cb_into(&file, &mut ids), 2);
assert_eq!(ids, vec![3, 7]);
let mut visited = Vec::new();
H5F_visit_obj_ids(&file, |id| visited.push(id));
assert_eq!(visited, vec![3, 7]);
visited.clear();
H5F__visit_objects(&file, |id| visited.push(id));
assert_eq!(visited, vec![3, 7]);
}
#[test]
fn path_prefix_helpers_borrow_or_reuse_storage() {
assert!(matches!(
H5F__build_name_cow("", "data.h5"),
Cow::Borrowed("data.h5")
));
assert_eq!(H5F__build_name_cow("/tmp", "data.h5"), "/tmp/data.h5");
let mut path = String::from("old-value");
H5F__build_name_into("/prefix", "child.h5", &mut path);
assert_eq!(path, "/prefix/child.h5");
H5F_prefix_open_file_into("", "child.h5", &mut path);
assert_eq!(path, "child.h5");
assert!(matches!(
H5F__getenv_prefix_name_cow(Some("/env")),
Some(Cow::Borrowed("/env"))
));
assert_eq!(H5F__getenv_prefix_name_cow(None), None);
}
#[test]
fn mount_traversal_helpers_support_reused_storage_and_visitors() {
let mut file = FileApiState::default();
H5F_mount(&mut file, "/z");
H5F_mount(&mut file, "/a");
let mut mounts = vec![String::from("stale")];
assert_eq!(H5F_traverse_mount_into(&file, &mut mounts), 2);
assert_eq!(mounts, vec![String::from("/a"), String::from("/z")]);
let mut visited = Vec::new();
H5F_visit_mounts(&file, |name| visited.push(name));
assert_eq!(visited, mounts);
}
#[test]
fn external_file_cache_tracks_open_release_and_limits() {
let mut cache = H5F__efc_create(1);
assert_eq!(H5F__efc_max_nfiles(&cache), 1);
let id = H5F__efc_open_file(&mut cache, "external-a.h5", 7).unwrap();
assert_eq!(id, 1);
assert!(cache.files.contains_key("external-a.h5"));
assert!(H5F__efc_open_file(&mut cache, "external-b.h5", 0).is_err());
let released = H5F__efc_release(&mut cache, "external-a.h5").unwrap();
assert_eq!(released.intent, 7);
assert_eq!(cache.close_attempts, 1);
assert!(cache.files.is_empty());
H5F__efc_open_file(&mut cache, "external-c.h5", 0).unwrap();
assert!(H5F__efc_try_close_tag1(&mut cache, "external-c.h5"));
assert_eq!(cache.close_attempts, 2);
H5F__efc_destroy(&mut cache);
assert!(cache.files.is_empty());
}
#[test]
fn superblock_cache_deserialize_validates_signature() {
let sb = Superblock {
version: 2,
sizeof_addr: 8,
sizeof_size: 8,
eof_addr: 64,
root_addr: 48,
..Superblock::default()
};
let mut image = Vec::new();
sb.write_v2(&mut image).unwrap();
image.extend_from_slice(b"trailing-file-data");
assert!(H5F__cache_superblock_verify_chksum(&image));
let cached = H5F__cache_superblock_deserialize(&image).unwrap();
assert_eq!(cached.superblock.version, 2);
assert_eq!(cached.superblock.root_addr, 48);
assert_eq!(cached.superblock.eof_addr, 64);
assert_eq!(H5F__cache_superblock_image_len(&cached), sb.size());
assert!(matches!(cached.raw, Cow::Borrowed(_)));
assert_eq!(&*cached.raw, &image[..sb.size()]);
H5F__cache_superblock_free_icr(cached);
let bad = b"not-hdf5";
assert!(!H5F__cache_superblock_verify_chksum(bad));
assert!(H5F__cache_superblock_deserialize(bad).is_err());
let truncated = &image[..sb.size() - 1];
assert!(H5F__cache_superblock_deserialize(truncated).is_err());
}
#[test]
fn driver_info_cache_block_uses_libhdf5_prefix_layout() {
let image = vec![
0, 0xaa, 0xbb, 0xcc, 3, 0, 0, 0, b's', b'e', b'c', b'2', 0, 0, 0, 0, b'a', b'b', b'c',
];
assert_eq!(H5F__cache_drvrinfo_get_initial_load_size(), 16);
assert_eq!(
H5F__cache_drvrinfo_get_final_load_size(&image[..16]).unwrap(),
image.len()
);
let block = H5F__cache_drvrinfo_deserialize(&image).unwrap();
assert_eq!(block.version, 0);
assert_eq!(&block.name, b"sec2\0\0\0\0");
assert!(matches!(block.data, Cow::Borrowed(_)));
assert_eq!(&*block.data, b"abc");
assert_eq!(H5F__cache_drvrinfo_image_len(&block).unwrap(), image.len());
let mut serialized = Vec::new();
assert_eq!(
H5F__cache_drvrinfo_serialize_into(&block, &mut serialized).unwrap(),
image.len()
);
assert_eq!(
serialized,
vec![0, 0, 0, 0, 3, 0, 0, 0, b's', b'e', b'c', b'2', 0, 0, 0, 0, b'a', b'b', b'c',]
);
H5F__cache_drvrinfo_free_icr(block);
}
#[test]
fn driver_info_cache_block_rejects_malformed_prefixes() {
let valid = vec![
0, 0, 0, 0, 1, 0, 0, 0, b's', b'e', b'c', b'2', 0, 0, 0, 0, b'x',
];
assert!(H5F__cache_drvrinfo_deserialize(&valid[..15]).is_err());
let mut bad_version = valid.clone();
bad_version[0] = 1;
assert!(H5F__cache_drvrinfo_get_final_load_size(&bad_version[..16]).is_err());
let truncated_payload = valid[..16].to_vec();
assert_eq!(
H5F__cache_drvrinfo_get_final_load_size(&truncated_payload).unwrap(),
17
);
assert!(H5F__cache_drvrinfo_deserialize(&truncated_payload).is_err());
let mut trailing = valid.clone();
trailing.push(b'y');
assert!(H5F__cache_drvrinfo_deserialize(&trailing).is_err());
}
#[test]
fn file_lock_env_parser_rejects_invalid_values() {
assert_eq!(H5F__parse_file_lock_env_var(None).unwrap(), None);
assert_eq!(
H5F__parse_file_lock_env_var(Some("0")).unwrap(),
Some(false)
);
assert_eq!(
H5F__parse_file_lock_env_var(Some("BEST_EFFORT")).unwrap(),
Some(true)
);
assert!(H5F__parse_file_lock_env_var(Some("maybe")).is_err());
}
}