#![allow(dead_code, non_snake_case)]
use std::borrow::Cow;
use std::cmp::Ordering;
use std::fmt;
use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
use std::sync::{Condvar, Mutex, Once};
use std::thread::{self, JoinHandle};
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use crate::error::{Error, Result};
static LIBRARY_OPEN: AtomicBool = AtomicBool::new(false);
static LIBRARY_TERMINATING: AtomicBool = AtomicBool::new(false);
#[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 {
locked: Mutex<bool>,
}
#[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<(usize, bool)>,
cond: Condvar,
}
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_comm_cmp() -> Result<Ordering> {
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_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 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 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 H5resize_memory(mut bytes: Vec<u8>, size: usize) -> Vec<u8> {
bytes.resize(size, 0);
bytes
}
pub fn H5free_memory(_bytes: Vec<u8>) {}
pub fn H5is_library_threadsafe() -> bool {
true
}
pub fn H5is_library_terminating() -> bool {
LIBRARY_TERMINATING.load(AtomicOrdering::SeqCst)
}
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 {
if let Ok(mut locked) = mutex.locked.try_lock() {
if !*locked {
*locked = true;
return true;
}
}
false
}
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 mut state = lock.state.lock().expect("rwlock poisoned");
while state.1 {
state = lock.cond.wait(state).expect("rwlock wait poisoned");
}
state.0 = state.0.saturating_add(1);
}
pub fn H5TS_rwlock_rdunlock(lock: &H5TsRwLock) {
if let Ok(mut state) = lock.state.lock() {
state.0 = state.0.saturating_sub(1);
if state.0 == 0 {
lock.cond.notify_all();
}
}
}
pub fn H5TS_rwlock_wrlock(lock: &H5TsRwLock) {
let mut state = lock.state.lock().expect("rwlock poisoned");
while state.1 || state.0 != 0 {
state = lock.cond.wait(state).expect("rwlock wait poisoned");
}
state.1 = true;
}
pub fn H5TS_rwlock_trywrlock(lock: &H5TsRwLock) -> bool {
if let Ok(mut state) = lock.state.try_lock() {
if !state.1 && state.0 == 0 {
state.1 = true;
return true;
}
}
false
}
pub fn H5TS_rwlock_wrunlock(lock: &H5TsRwLock) {
if let Ok(mut state) = lock.state.lock() {
state.1 = false;
lock.cond.notify_all();
}
}
pub fn H5TS_key_create() -> usize {
0
}
pub fn H5TS_key_delete(_key: usize) {}
pub fn H5TS_key_set_value<T>(_key: usize, value: T) -> T {
value
}
pub fn H5TS_key_get_value(_key: usize) -> Option<()> {
None
}
#[allow(non_snake_case)]
pub fn H5TS_pool_create() -> 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 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_utf16_str_into(bytes: &[u16], out: &mut String) -> Result<()> {
out.clear();
for item in std::char::decode_utf16(bytes.iter().copied()) {
let ch = item.map_err(|_| Error::InvalidFormat("invalid UTF-16 string".into()))?;
out.push(ch);
}
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");
}
}