format: no truncating u64 -> usize casts
Every `u64 as usize` cast in clawhdf5-format (115 on wasm32) now goes through addr::to_usize for values read from the file — addresses, lengths, counts, dimensions: FormatError::Overflow where the value does not fit instead of wrapping onto another part of the file on a 32-bit target — or addr::saturating_usize for counts bounded by something in memory (codec progress counters, writer sizes), which fail a bounds check or allocation rather than wrap. A chunk whose offset does not fit lies outside the dataset and is skipped; partial reads treat such an offset as out of the buffers. On 64-bit targets nothing changes. scripts/check-32bit-casts.sh (run by ci-test.sh) lints the wasm32 build with clippy's cast_possible_truncation and fails on any u64 -> usize finding; before this commit it listed 115. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
@@ -23,6 +23,19 @@ pub fn to_usize(value: u64) -> Result<usize, FormatError> {
|
||||
usize::try_from(value).map_err(|_| too_large(value))
|
||||
}
|
||||
|
||||
/// A count or offset into an in-memory buffer (a codec's progress counter,
|
||||
/// a size the writer computed from data it holds) as a `usize`, saturating
|
||||
/// at `usize::MAX` instead of truncating.
|
||||
///
|
||||
/// For values that are bounded by the length of something in memory, so
|
||||
/// always fit; if one ever did not, a saturated index fails its bounds check
|
||||
/// or allocation instead of silently addressing the wrong bytes. A value
|
||||
/// read from the file uses [`to_usize`].
|
||||
#[inline]
|
||||
pub fn saturating_usize(value: u64) -> usize {
|
||||
usize::try_from(value).unwrap_or(usize::MAX)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn too_large(value: u64) -> FormatError {
|
||||
@@ -42,6 +55,15 @@ mod tests {
|
||||
assert_eq!(to_usize(usize::MAX as u64), Ok(usize::MAX));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saturating_conversion_never_wraps() {
|
||||
assert_eq!(saturating_usize(0), 0);
|
||||
assert_eq!(saturating_usize(0x1234), 0x1234);
|
||||
assert_eq!(saturating_usize(usize::MAX as u64), usize::MAX);
|
||||
// Past usize::MAX (32-bit targets) or at u64::MAX: saturates.
|
||||
assert_eq!(saturating_usize(u64::MAX), 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
|
||||
|
||||
Reference in New Issue
Block a user