#![allow(dead_code, non_snake_case)]
use std::any::Any;
use std::borrow::Cow;
use std::cell::RefCell;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::fmt;
use std::path::Path;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrdering};
use std::sync::{Condvar, Mutex, Once};
use std::thread::{self, JoinHandle, ThreadId};
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use crate::engine::free_list::{FreeListManager, FreeListStats};
use crate::error::{Error, Result};
static LIBRARY_OPEN: AtomicBool = AtomicBool::new(false);
static LIBRARY_TERMINATING: AtomicBool = AtomicBool::new(false);
static NEXT_TLS_KEY: AtomicUsize = AtomicUsize::new(1);
thread_local! {
static TLS_VALUES: RefCell<HashMap<usize, Box<dyn Any>>> = RefCell::new(HashMap::new());
}
#[derive(Debug, Clone)]
pub struct H5Timer {
pub started: Option<Instant>,
pub elapsed: Duration,
}
impl Default for H5Timer {
fn default() -> Self {
Self {
started: None,
elapsed: Duration::ZERO,
}
}
}
#[derive(Debug, Default)]
pub struct H5TsMutex {
state: Mutex<H5TsMutexState>,
}
#[derive(Debug, Default)]
struct H5TsMutexState {
owner: Option<ThreadId>,
}
#[derive(Debug, Default)]
pub struct H5TsCond {
cond: Condvar,
}
#[derive(Debug, Default)]
pub struct H5TsSemaphore {
permits: Mutex<usize>,
cond: Condvar,
}
#[derive(Debug, Default)]
pub struct H5TsRwLock {
state: Mutex<H5TsRwLockState>,
cond: Condvar,
}
#[derive(Debug, Default)]
struct H5TsRwLockState {
readers: HashMap<ThreadId, usize>,
writer: Option<ThreadId>,
}
fn unsupported_support(name: &str) -> Error {
Error::Unsupported(format!(
"{name} requires platform/MPI behavior not implemented in pure-Rust mode"
))
}
pub fn H5_ITER_ERROR() -> i32 {
-1
}
pub fn H5_BEFORE_USER_CB() {}
pub fn H5_BEFORE_USER_CB_NOERR() {}
pub fn H5_mpi_comm_dup() -> Result<()> {
Err(unsupported_support("H5_mpi_comm_dup"))
}
pub fn H5_mpi_comm_free() -> Result<()> {
Err(unsupported_support("H5_mpi_comm_free"))
}
pub fn H5_mpi_info_dup() -> Result<()> {
Err(unsupported_support("H5_mpi_info_dup"))
}
pub fn H5_mpi_comm_cmp() -> Result<Ordering> {
Err(unsupported_support("H5_mpi_comm_cmp"))
}
pub fn H5_mpi_comm_cmp_into(_ordering: &mut Ordering) -> Result<()> {
Err(unsupported_support("H5_mpi_comm_cmp"))
}
pub fn H5_mpio_gatherv_alloc() -> Result<()> {
Err(unsupported_support("H5_mpio_gatherv_alloc"))
}
pub fn H5_mpio_gatherv_alloc_simple() -> Result<()> {
Err(unsupported_support("H5_mpio_gatherv_alloc_simple"))
}
pub fn H5_mpio_get_file_sync_required() -> bool {
false
}
pub fn H5A__open_common() -> Result<()> {
Err(Error::Unsupported(
"attribute open-common duplicate is tracked in the attribute API".into(),
))
}
pub fn H5_gmtime_r(time: SystemTime) -> u64 {
time.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
}
pub fn H5_localtime_r(time: SystemTime) -> u64 {
H5_gmtime_r(time)
}
pub fn H5_buffer_dump_into<W>(bytes: &[u8], out: &mut W) -> fmt::Result
where
W: fmt::Write + ?Sized,
{
for (idx, byte) in bytes.iter().enumerate() {
if idx > 0 {
out.write_char(' ')?;
}
write!(out, "{byte:02x}")?;
}
Ok(())
}
#[deprecated(note = "use H5_buffer_dump_into to reuse formatting storage")]
pub fn H5_buffer_dump(bytes: &[u8]) -> String {
let mut out = String::new();
H5_buffer_dump_into(bytes, &mut out).expect("writing to String cannot fail");
out
}
pub fn H5TS_api_lock() {}
pub fn H5_bandwidth(bytes: u64, elapsed: Duration) -> f64 {
let secs = elapsed.as_secs_f64();
if secs == 0.0 {
0.0
} else {
bytes as f64 / secs
}
}
pub fn H5_now() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
}
pub fn H5_now_usec() -> u128 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_micros()
}
pub fn H5_get_time() -> SystemTime {
SystemTime::now()
}
pub fn H5__timer_get_timevals() -> (u64, u128) {
(H5_now(), H5_now_usec())
}
pub fn H5_timer_init() -> H5Timer {
H5Timer::default()
}
pub fn H5_timer_start(timer: &mut H5Timer) {
timer.started = Some(Instant::now());
}
pub fn H5_timer_stop(timer: &mut H5Timer) -> Duration {
if let Some(started) = timer.started.take() {
timer.elapsed = timer.elapsed.saturating_add(started.elapsed());
}
timer.elapsed
}
pub fn H5_timer_get_times(timer: &H5Timer) -> Duration {
timer.elapsed
}
pub fn H5_timer_get_total_times(timer: &H5Timer) -> Duration {
timer.elapsed
}
pub fn H5_timer_get_time_string_fmt<W>(duration: Duration, out: &mut W) -> fmt::Result
where
W: fmt::Write + ?Sized,
{
write!(out, "{:.6}s", duration.as_secs_f64())
}
pub fn H5_timer_get_time_string_into(duration: Duration, out: &mut String) {
out.clear();
H5_timer_get_time_string_fmt(duration, out).expect("writing to String cannot fail");
}
#[deprecated(note = "use H5_timer_get_time_string_into to reuse String storage")]
pub fn H5_timer_get_time_string(duration: Duration) -> String {
let mut out = String::new();
H5_timer_get_time_string_into(duration, &mut out);
out
}
pub fn H5__init_package() {
LIBRARY_TERMINATING.store(false, AtomicOrdering::SeqCst);
LIBRARY_OPEN.store(true, AtomicOrdering::SeqCst);
}
pub fn H5_init_library() {
H5__init_package();
}
pub fn H5_term_library() {
LIBRARY_TERMINATING.store(true, AtomicOrdering::SeqCst);
LIBRARY_OPEN.store(false, AtomicOrdering::SeqCst);
}
pub fn H5dont_atexit() {}
pub fn H5garbage_collect() {}
pub fn H5get_free_list_sizes(lists: &FreeListManager) -> FreeListStats {
lists.get_free_list_sizes()
}
pub fn H5set_free_list_limits(
lists: &mut FreeListManager,
regular: usize,
block: usize,
array: usize,
factory: usize,
) {
lists.set_free_list_limits(regular, block, array, factory);
}
pub fn H5__debug_mask(mask: u64) -> u64 {
mask
}
pub fn H5__mpi_delete_cb() -> Result<()> {
Err(unsupported_support("H5__mpi_delete_cb"))
}
pub fn H5get_libversion() -> (u32, u32, u32) {
(0, 1, 0)
}
pub fn H5get_libversion_into(major: &mut u32, minor: &mut u32, release: &mut u32) {
(*major, *minor, *release) = H5get_libversion();
}
pub fn H5_check_version(major: u32, minor: u32, release: u32) -> bool {
H5get_libversion() == (major, minor, release)
}
pub fn H5check_version(major: u32, minor: u32, release: u32) -> bool {
H5_check_version(major, minor, release)
}
pub fn H5open() {
H5_init_library();
}
pub fn H5atclose() {}
pub fn H5close() {
H5_term_library();
}
pub fn H5allocate_memory(size: usize) -> Vec<u8> {
vec![0; size]
}
pub fn H5MM_malloc(size: usize) -> Vec<u8> {
H5allocate_memory(size)
}
pub fn H5MM_calloc(count: usize, size: usize) -> Result<Vec<u8>> {
let total = count
.checked_mul(size)
.ok_or_else(|| Error::InvalidFormat("H5MM calloc size overflow".into()))?;
Ok(H5allocate_memory(total))
}
pub fn H5resize_memory(mut bytes: Vec<u8>, size: usize) -> Vec<u8> {
bytes.resize(size, 0);
bytes
}
pub fn H5MM_realloc(bytes: Vec<u8>, size: usize) -> Vec<u8> {
H5resize_memory(bytes, size)
}
pub fn H5free_memory(_bytes: Vec<u8>) {}
pub fn H5MM_xfree(bytes: Vec<u8>) {
H5free_memory(bytes);
}
pub fn H5is_library_threadsafe() -> bool {
false
}
pub fn H5is_library_threadsafe_into(is_threadsafe: &mut bool) {
*is_threadsafe = H5is_library_threadsafe();
}
pub fn H5is_library_terminating() -> bool {
LIBRARY_TERMINATING.load(AtomicOrdering::SeqCst)
}
pub fn H5is_library_terminating_into(is_terminating: &mut bool) {
*is_terminating = H5is_library_terminating();
}
pub fn H5_user_cb_prepare() {}
pub fn H5_user_cb_restore() {}
pub fn H5_HAVE_PARALLEL() -> bool {
false
}
pub fn H5__checksum_crc_update(seed: u32, bytes: &[u8]) -> u32 {
bytes
.iter()
.fold(seed, |acc, byte| acc.rotate_left(5) ^ u32::from(*byte))
}
pub fn H5_checksum_crc(bytes: &[u8]) -> u32 {
H5__checksum_crc_update(0, bytes)
}
pub fn H5UC_create() -> usize {
1
}
pub fn H5UC_decr(count: &mut usize) -> usize {
*count = count.saturating_sub(1);
*count
}
pub fn H5TS_mutex_init() -> H5TsMutex {
H5TsMutex::default()
}
pub fn H5TS_mutex_trylock(mutex: &H5TsMutex) -> bool {
let current = thread::current().id();
if let Ok(mut state) = mutex.state.try_lock() {
if state.owner.is_none() {
state.owner = Some(current);
return true;
}
}
false
}
pub fn H5TS_mutex_unlock(mutex: &H5TsMutex) {
let current = thread::current().id();
let mut state = mutex.state.lock().expect("mutex poisoned");
match state.owner {
Some(owner) if owner == current => state.owner = None,
Some(_) => panic!("mutex unlocked from a non-owning thread"),
None => panic!("mutex unlocked while not locked"),
}
}
pub fn H5TS_mutex_destroy(_mutex: H5TsMutex) {}
pub fn H5TS_semaphore_init(permits: usize) -> H5TsSemaphore {
H5TsSemaphore {
permits: Mutex::new(permits),
cond: Condvar::new(),
}
}
pub fn H5TS_semaphore_destroy(_sem: H5TsSemaphore) {}
pub fn H5TS_once(once: &Once, f: fn()) {
once.call_once(f);
}
pub fn H5TS_cond_init() -> H5TsCond {
H5TsCond::default()
}
pub fn H5TS_cond_destroy(_cond: H5TsCond) {}
pub fn H5TS_semaphore_signal(sem: &H5TsSemaphore) {
if let Ok(mut permits) = sem.permits.lock() {
*permits = permits.saturating_add(1);
sem.cond.notify_one();
}
}
pub fn H5TS_semaphore_wait(sem: &H5TsSemaphore) {
if let Ok(mut permits) = sem.permits.lock() {
while *permits == 0 {
permits = sem.cond.wait(permits).expect("semaphore wait poisoned");
}
*permits = permits.saturating_sub(1);
}
}
pub fn H5TS_rwlock_init() -> H5TsRwLock {
H5TsRwLock::default()
}
pub fn H5TS_rwlock_destroy(_lock: H5TsRwLock) {}
pub fn H5TS_rwlock_rdlock(lock: &H5TsRwLock) {
let current = thread::current().id();
let mut state = lock.state.lock().expect("rwlock poisoned");
while state.writer.is_some() {
state = lock.cond.wait(state).expect("rwlock wait poisoned");
}
*state.readers.entry(current).or_insert(0) += 1;
}
pub fn H5TS_rwlock_rdunlock(lock: &H5TsRwLock) {
let current = thread::current().id();
let mut state = lock.state.lock().expect("rwlock poisoned");
let count = state
.readers
.get_mut(¤t)
.expect("rwlock read-unlocked by a thread without a read lock");
*count -= 1;
if *count == 0 {
state.readers.remove(¤t);
}
if state.readers.is_empty() {
lock.cond.notify_all();
}
}
pub fn H5TS_rwlock_wrlock(lock: &H5TsRwLock) {
let current = thread::current().id();
let mut state = lock.state.lock().expect("rwlock poisoned");
while state.writer.is_some() || !state.readers.is_empty() {
state = lock.cond.wait(state).expect("rwlock wait poisoned");
}
state.writer = Some(current);
}
pub fn H5TS_rwlock_trywrlock(lock: &H5TsRwLock) -> bool {
let current = thread::current().id();
if let Ok(mut state) = lock.state.try_lock() {
if state.writer.is_none() && state.readers.is_empty() {
state.writer = Some(current);
return true;
}
}
false
}
pub fn H5TS_rwlock_wrunlock(lock: &H5TsRwLock) {
let current = thread::current().id();
let mut state = lock.state.lock().expect("rwlock poisoned");
match state.writer {
Some(owner) if owner == current => {
state.writer = None;
lock.cond.notify_all();
}
Some(_) => panic!("rwlock write-unlocked from a non-owning thread"),
None => panic!("rwlock write-unlocked while not write-locked"),
}
}
pub fn H5TS_key_create() -> usize {
NEXT_TLS_KEY.fetch_add(1, AtomicOrdering::Relaxed)
}
pub fn H5TS_key_delete(key: usize) {
TLS_VALUES.with(|values| {
values.borrow_mut().remove(&key);
});
}
pub fn H5TS_key_set_value<T: 'static>(key: usize, value: T) -> T {
TLS_VALUES.with(|values| {
values.borrow_mut().insert(key, Box::new(()));
});
value
}
pub fn H5TS_key_get_value(key: usize) -> Option<()> {
TLS_VALUES.with(|values| values.borrow().contains_key(&key).then_some(()))
}
#[allow(non_snake_case)]
pub fn H5TS_pool_create() -> Result<()> {
Err(Error::Unsupported(
"thread-pool runtime is intentionally unsupported".into(),
))
}
#[allow(non_snake_case)]
pub fn H5TS_pool_add_task<T>(_task: &mut T) -> Result<()> {
Err(Error::Unsupported(
"thread-pool runtime is intentionally unsupported".into(),
))
}
pub fn H5_trace_args_bool_ref(value: bool) -> &'static str {
if value {
"true"
} else {
"false"
}
}
pub fn H5_trace_args_bool_into(value: bool, out: &mut String) {
out.clear();
out.push_str(H5_trace_args_bool_ref(value));
}
#[deprecated(note = "use H5_trace_args_bool_into to reuse String storage")]
pub fn H5_trace_args_bool(value: bool) -> String {
let mut out = String::new();
H5_trace_args_bool_into(value, &mut out);
out
}
pub fn H5_trace_args_cset_fmt<W>(value: u8, out: &mut W) -> fmt::Result
where
W: fmt::Write + ?Sized,
{
write!(out, "{value}")
}
pub fn H5_trace_args_cset_into(value: u8, out: &mut String) {
out.clear();
H5_trace_args_cset_fmt(value, out).expect("writing to String cannot fail");
}
#[deprecated(note = "use H5_trace_args_cset_into to reuse String storage")]
pub fn H5_trace_args_cset(value: u8) -> String {
let mut out = String::new();
H5_trace_args_cset_into(value, &mut out);
out
}
pub fn H5_trace_args_close_degree_fmt<W>(value: u8, out: &mut W) -> fmt::Result
where
W: fmt::Write + ?Sized,
{
H5_trace_args_cset_fmt(value, out)
}
pub fn H5_trace_args_close_degree_into(value: u8, out: &mut String) {
out.clear();
H5_trace_args_close_degree_fmt(value, out).expect("writing to String cannot fail");
}
#[deprecated(note = "use H5_trace_args_close_degree_into to reuse String storage")]
pub fn H5_trace_args_close_degree(value: u8) -> String {
let mut out = String::new();
H5_trace_args_close_degree_into(value, &mut out);
out
}
pub fn H5_trace_args_fmt<S, W>(args: &[S], out: &mut W) -> fmt::Result
where
S: AsRef<str>,
W: fmt::Write + ?Sized,
{
for (idx, arg) in args.iter().enumerate() {
if idx > 0 {
out.write_str(", ")?;
}
out.write_str(arg.as_ref())?;
}
Ok(())
}
pub fn H5_trace_args_into<S>(args: &[S], out: &mut String)
where
S: AsRef<str>,
{
out.clear();
H5_trace_args_fmt(args, out).expect("writing to String cannot fail");
}
pub fn H5_trace_args_iter<S>(args: &[S]) -> impl Iterator<Item = &str>
where
S: AsRef<str>,
{
args.iter().map(AsRef::as_ref)
}
#[deprecated(note = "use H5_trace_args_iter or H5_trace_args_into")]
pub fn H5_trace_args(args: &[String]) -> String {
let mut out = String::new();
H5_trace_args_into(args, &mut out);
out
}
pub fn H5_trace_ref(message: &str) -> &str {
message
}
#[deprecated(note = "use H5_trace_ref to borrow the trace message")]
pub fn H5_trace(message: &str) -> String {
H5_trace_ref(message).to_string()
}
pub fn H5TS_thread_create<F>(f: F) -> JoinHandle<()>
where
F: FnOnce() + Send + 'static,
{
thread::spawn(f)
}
pub fn H5TS_thread_join(handle: JoinHandle<()>) -> Result<()> {
handle
.join()
.map_err(|_| Error::InvalidFormat("thread panicked".into()))
}
pub fn H5TS_thread_detach(_handle: JoinHandle<()>) {}
pub fn H5TS_thread_yield() {
thread::yield_now();
}
pub fn H5FD__copy_plist() -> Result<()> {
Err(Error::Unsupported(
"VFD property-list copy duplicate is tracked in the VFD API".into(),
))
}
pub fn H5FDdriver_query(_driver_id: u64) -> Result<u64> {
Err(unsupported_support("H5FDdriver_query"))
}
pub fn H5FDdriver_query_into(_driver_id: u64, _flags: &mut u64) -> Result<()> {
Err(unsupported_support("H5FDdriver_query"))
}
pub fn H5PLappend(_path: &str) -> Result<()> {
Err(unsupported_support("H5PLappend"))
}
pub fn H5PLprepend(_path: &str) -> Result<()> {
Err(unsupported_support("H5PLprepend"))
}
pub fn H5PLreplace(_path: &str, _index: usize) -> Result<()> {
Err(unsupported_support("H5PLreplace"))
}
pub fn H5PLinsert(_path: &str, _index: usize) -> Result<()> {
Err(unsupported_support("H5PLinsert"))
}
pub fn H5PLremove(_index: usize) -> Result<()> {
Err(unsupported_support("H5PLremove"))
}
pub fn H5_subfiling_dump_iovecs() -> Result<()> {
Err(unsupported_support("H5_subfiling_dump_iovecs"))
}
pub fn H5_make_time(secs: u64) -> SystemTime {
UNIX_EPOCH + Duration::from_secs(secs)
}
pub fn H5_get_localtime_str_fmt<W>(time: SystemTime, out: &mut W) -> fmt::Result
where
W: fmt::Write + ?Sized,
{
write!(out, "{}", H5_gmtime_r(time))
}
pub fn H5_get_localtime_str_into(time: SystemTime, out: &mut String) {
out.clear();
H5_get_localtime_str_fmt(time, out).expect("writing to String cannot fail");
}
#[deprecated(note = "use H5_get_localtime_str_into to reuse String storage")]
pub fn H5_get_localtime_str(time: SystemTime) -> String {
H5_gmtime_r(time).to_string()
}
pub fn H5_get_win32_times() -> Result<()> {
Err(unsupported_support("H5_get_win32_times"))
}
pub fn H5_get_win32_times_into(
_created: &mut SystemTime,
_accessed: &mut SystemTime,
_modified: &mut SystemTime,
) -> Result<()> {
Err(unsupported_support("H5_get_win32_times"))
}
pub fn H5_get_utf16_str_into(bytes: &[u16], out: &mut String) -> Result<()> {
let mut decoded = String::new();
for item in std::char::decode_utf16(bytes.iter().copied()) {
let ch = item.map_err(|_| Error::InvalidFormat("invalid UTF-16 string".into()))?;
decoded.push(ch);
}
out.try_reserve(decoded.len())
.map_err(|err| Error::InvalidFormat(format!("UTF-16 output allocation failed: {err}")))?;
out.clear();
out.push_str(&decoded);
Ok(())
}
#[deprecated(note = "use H5_get_utf16_str_into to reuse String storage")]
pub fn H5_get_utf16_str(bytes: &[u16]) -> Result<String> {
let mut out = String::new();
H5_get_utf16_str_into(bytes, &mut out)?;
Ok(out)
}
pub fn H5_build_extpath_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 H5_build_extpath_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);
}
}
#[deprecated(note = "use H5_build_extpath_cow or H5_build_extpath_into")]
pub fn H5_build_extpath(prefix: &str, name: &str) -> String {
H5_build_extpath_cow(prefix, name).into_owned()
}
pub fn H5_nanosleep(nanos: u64) {
thread::sleep(Duration::from_nanos(nanos));
}
pub fn H5_expand_windows_env_vars_ref(value: &str) -> &str {
value
}
#[deprecated(note = "use H5_expand_windows_env_vars_ref to borrow the unchanged string")]
pub fn H5_expand_windows_env_vars(value: &str) -> String {
H5_expand_windows_env_vars_ref(value).to_string()
}
pub fn H5_strndup_cow(value: &str, len: usize) -> Cow<'_, str> {
let mut char_indices = value.char_indices();
match char_indices.nth(len) {
Some((end, _)) => Cow::Borrowed(&value[..end]),
None => Cow::Borrowed(value),
}
}
pub fn H5_strndup_into(value: &str, len: usize, out: &mut String) {
out.clear();
out.push_str(&H5_strndup_cow(value, len));
}
#[deprecated(note = "use H5_strndup_cow or H5_strndup_into")]
pub fn H5_strndup(value: &str, len: usize) -> String {
H5_strndup_cow(value, len).into_owned()
}
pub fn H5_dirname_cow(value: &str) -> Cow<'_, str> {
Path::new(value)
.parent()
.map(Path::to_string_lossy)
.unwrap_or(Cow::Borrowed("."))
}
pub fn H5_dirname_into(value: &str, out: &mut String) {
out.clear();
out.push_str(&H5_dirname_cow(value));
}
#[deprecated(note = "use H5_dirname_cow or H5_dirname_into")]
pub fn H5_dirname(value: &str) -> String {
H5_dirname_cow(value).into_owned()
}
pub fn H5_basename_cow(value: &str) -> Cow<'_, str> {
Path::new(value)
.file_name()
.map(|path| path.to_string_lossy())
.unwrap_or(Cow::Borrowed(value))
}
pub fn H5_basename_into(value: &str, out: &mut String) {
out.clear();
out.push_str(&H5_basename_cow(value));
}
#[deprecated(note = "use H5_basename_cow or H5_basename_into")]
pub fn H5_basename(value: &str) -> String {
H5_basename_cow(value).into_owned()
}
pub fn H5_get_option<T: Copy>(value: Option<T>, default: T) -> T {
value.unwrap_or(default)
}
pub fn H5_strcasestr(haystack: &str, needle: &str) -> Option<usize> {
let needle = needle.as_bytes();
if needle.is_empty() {
return Some(0);
}
haystack
.as_bytes()
.windows(needle.len())
.position(|window| {
window
.iter()
.zip(needle)
.all(|(left, right)| left.eq_ignore_ascii_case(right))
})
}
pub fn is_host_little_endian() -> bool {
cfg!(target_endian = "little")
}
pub fn H5EA__dblock_create() -> Result<()> {
Err(Error::Unsupported(
"extensible-array data block creation is not implemented".into(),
))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn strcasestr_searches_without_changing_offsets() {
assert_eq!(H5_strcasestr("prefixDATA", "data"), Some(6));
assert_eq!(H5_strcasestr("abcDEFghi", "Def"), Some(3));
assert_eq!(H5_strcasestr("abc", ""), Some(0));
assert_eq!(H5_strcasestr("abc", "abcd"), None);
assert_eq!(H5_strcasestr("abc", "z"), None);
}
#[test]
fn support_owned_compat_wrappers_route_through_new_apis() {
let mut out = String::new();
H5_buffer_dump_into(&[0, 10, 255], &mut out).expect("formatting should succeed");
assert_eq!(out, "00 0a ff");
H5_trace_args_into(&["one", "two"], &mut out);
assert_eq!(out, "one, two");
H5_strndup_into("abcdef", 3, &mut out);
assert_eq!(out, "abc");
H5_build_extpath_into("dir", "file.h5", &mut out);
assert_eq!(out, "dir/file.h5");
}
#[test]
fn utf16_decode_replaces_output_only_after_successful_decode() {
let mut out = String::from("stale");
H5_get_utf16_str_into(&[0x0041, 0xD83D, 0xDE00], &mut out)
.expect("valid UTF-16 should decode");
assert_eq!(out, "A😀");
let err = H5_get_utf16_str_into(&[0x0042, 0xD800], &mut out)
.expect_err("unpaired surrogate should fail");
assert!(matches!(err, Error::InvalidFormat(_)));
assert_eq!(out, "A😀");
H5_get_utf16_str_into(&[], &mut out).expect("empty UTF-16 should decode");
assert!(out.is_empty());
}
#[test]
fn free_list_public_wrappers_query_and_set_limits() {
let mut lists = FreeListManager::new();
lists.blk_free(vec![0; 4]);
lists.blk_free(vec![0; 8]);
lists.arr_free(vec![0; 16]);
let stats = H5get_free_list_sizes(&lists);
assert_eq!(stats.block_bytes, 12);
assert_eq!(stats.array_bytes, 16);
H5set_free_list_limits(&mut lists, usize::MAX, 1, 0, usize::MAX);
let stats = H5get_free_list_sizes(&lists);
assert_eq!(stats.block_bytes, 4);
assert_eq!(stats.array_bytes, 0);
}
#[test]
fn h5mm_allocation_wrappers_zero_resize_and_reject_overflow() {
let mut bytes = H5MM_malloc(4);
assert_eq!(bytes, vec![0; 4]);
bytes[0] = 9;
bytes = H5MM_realloc(bytes, 6);
assert_eq!(&bytes[..4], &[9, 0, 0, 0]);
assert_eq!(&bytes[4..], &[0, 0]);
bytes = H5MM_realloc(bytes, 2);
assert_eq!(bytes, vec![9, 0]);
let calloc = H5MM_calloc(3, 2).unwrap();
assert_eq!(calloc, vec![0; 6]);
assert!(H5MM_calloc(usize::MAX, 2).is_err());
H5MM_xfree(calloc);
}
#[test]
fn public_h5_out_parameter_wrappers_match_convenience_queries() {
let mut major = u32::MAX;
let mut minor = u32::MAX;
let mut release = u32::MAX;
H5get_libversion_into(&mut major, &mut minor, &mut release);
assert_eq!((major, minor, release), H5get_libversion());
let mut is_threadsafe = false;
H5is_library_threadsafe_into(&mut is_threadsafe);
assert_eq!(is_threadsafe, H5is_library_threadsafe());
H5open();
let mut is_terminating = true;
H5is_library_terminating_into(&mut is_terminating);
assert!(!is_terminating);
H5close();
H5is_library_terminating_into(&mut is_terminating);
assert!(is_terminating);
}
#[test]
fn support_thread_wrappers_track_mutex_rwlock_and_tls_state() {
let mutex = H5TS_mutex_init();
assert!(H5TS_mutex_trylock(&mutex));
assert!(!H5TS_mutex_trylock(&mutex));
H5TS_mutex_unlock(&mutex);
assert!(H5TS_mutex_trylock(&mutex));
H5TS_mutex_unlock(&mutex);
let lock = H5TS_rwlock_init();
H5TS_rwlock_rdlock(&lock);
assert!(!H5TS_rwlock_trywrlock(&lock));
H5TS_rwlock_rdunlock(&lock);
assert!(H5TS_rwlock_trywrlock(&lock));
H5TS_rwlock_wrunlock(&lock);
let key = H5TS_key_create();
assert_eq!(H5TS_key_get_value(key), None);
assert_eq!(H5TS_key_set_value(key, "value"), "value");
assert_eq!(H5TS_key_get_value(key), Some(()));
H5TS_key_delete(key);
assert_eq!(H5TS_key_get_value(key), None);
}
#[test]
#[should_panic(expected = "mutex unlocked while not locked")]
fn support_mutex_unlock_without_lock_panics() {
let mutex = H5TS_mutex_init();
H5TS_mutex_unlock(&mutex);
}
#[test]
#[should_panic(expected = "rwlock read-unlocked by a thread without a read lock")]
fn support_rwlock_read_unlock_without_lock_panics() {
let lock = H5TS_rwlock_init();
H5TS_rwlock_rdunlock(&lock);
}
#[test]
#[should_panic(expected = "rwlock write-unlocked while not write-locked")]
fn support_rwlock_write_unlock_without_lock_panics() {
let lock = H5TS_rwlock_init();
H5TS_rwlock_wrunlock(&lock);
}
#[test]
fn support_runtime_boundaries_are_explicitly_unsupported() {
let mut ordering = Ordering::Less;
let err = H5_mpi_comm_cmp_into(&mut ordering)
.expect_err("MPI communicator comparison remains unsupported");
assert!(matches!(err, Error::Unsupported(_)));
assert_eq!(ordering, Ordering::Less);
for err in [
H5_mpi_comm_dup().unwrap_err(),
H5_mpi_comm_free().unwrap_err(),
H5_mpi_info_dup().unwrap_err(),
H5_mpi_comm_cmp().unwrap_err(),
H5_mpio_gatherv_alloc().unwrap_err(),
H5_mpio_gatherv_alloc_simple().unwrap_err(),
H5FD__copy_plist().unwrap_err(),
H5FDdriver_query(0).unwrap_err(),
{
let mut flags = 0xfeed;
let err = H5FDdriver_query_into(0, &mut flags).unwrap_err();
assert_eq!(flags, 0xfeed);
err
},
H5PLappend("/tmp/hdf5-plugins").unwrap_err(),
H5PLprepend("/tmp/hdf5-plugins").unwrap_err(),
H5PLreplace("/tmp/hdf5-plugins", 0).unwrap_err(),
H5PLinsert("/tmp/hdf5-plugins", 0).unwrap_err(),
H5PLremove(0).unwrap_err(),
H5_subfiling_dump_iovecs().unwrap_err(),
H5_get_win32_times().unwrap_err(),
{
let original_created = UNIX_EPOCH + Duration::from_secs(11);
let original_accessed = UNIX_EPOCH + Duration::from_secs(22);
let original_modified = UNIX_EPOCH + Duration::from_secs(33);
let mut created = original_created;
let mut accessed = original_accessed;
let mut modified = original_modified;
let err = H5_get_win32_times_into(&mut created, &mut accessed, &mut modified)
.unwrap_err();
assert_eq!(created, original_created);
assert_eq!(accessed, original_accessed);
assert_eq!(modified, original_modified);
err
},
H5EA__dblock_create().unwrap_err(),
H5TS_pool_create().unwrap_err(),
{
let mut task_state = String::from("queued");
let err = H5TS_pool_add_task(&mut task_state).unwrap_err();
assert_eq!(task_state, "queued");
err
},
] {
assert!(matches!(err, Error::Unsupported(_)));
}
}
}