//! File address and length → in-memory index conversion. //! //! HDF5 addresses and lengths are 64-bit; the file is parsed through a //! `&[u8]` indexed by `usize`. On a 64-bit target every `u64` fits, but on a //! 32-bit one (`wasm32`, `i686`, `thumbv7em`) an address past `usize::MAX` //! used to be truncated by an `as usize` cast — silently pointing at another //! part of the file — or to panic. [`to_usize`] is the one conversion the //! parsers use instead: such an address is a clean //! [`FormatError::Overflow`]. It cannot be inside the data anyway: no slice //! is longer than `isize::MAX` bytes. #[cfg(not(feature = "std"))] use alloc::format; use crate::error::FormatError; /// A file address, offset or length from the file as a `usize` index. /// /// Fails with [`FormatError::Overflow`] when the value does not fit this /// platform's `usize` (only possible on targets narrower than 64 bits). #[inline] pub fn to_usize(value: u64) -> Result { usize::try_from(value).map_err(|_| too_large(value)) } #[cold] #[inline(never)] fn too_large(value: u64) -> FormatError { FormatError::Overflow(format!( "file address or length {value:#x} exceeds this platform's address space" )) } #[cfg(test)] mod tests { use super::*; #[test] fn values_that_fit_convert_exactly() { assert_eq!(to_usize(0), Ok(0)); assert_eq!(to_usize(0x1234), Ok(0x1234)); assert_eq!(to_usize(usize::MAX as u64), Ok(usize::MAX)); } #[test] fn values_past_usize_max_are_an_error_not_truncated() { // Only reachable where usize is narrower than u64; on a 64-bit host // every u64 fits, which the first branch checks instead. if let Some(past) = (usize::MAX as u64).checked_add(1) { let err = to_usize(past).unwrap_err(); assert!(matches!(err, FormatError::Overflow(_)), "{err:?}"); // The value an `as usize` cast would have produced is not returned. assert!(to_usize(u64::MAX).is_err()); assert!(to_usize(past + 0x10).is_err()); } else { assert_eq!(to_usize(u64::MAX), Ok(u64::MAX as usize)); } } }