Range reads M0/M1 (indexed lookups, Storage trait), ZFP, in-place editing #17

Merged
osobh merged 52 commits from feat/p3-range-zfp-edit into main 2026-09-26 20:42:22 +00:00
Showing only changes of commit 5b3d32b37d - Show all commits
+43 -12
View File
@@ -20,7 +20,16 @@ use crate::error::FormatError;
/// platform's `usize` (only possible on targets narrower than 64 bits).
#[inline]
pub fn to_usize(value: u64) -> Result<usize, FormatError> {
usize::try_from(value).map_err(|_| too_large(value))
to_index::<usize>(value)
}
/// [`to_usize`] for an index type of any width. `usize` is 64 bits wide on
/// the hosts CI tests on, where the error path cannot be reached through
/// `usize`; tests run the same code with `u32` in its place, as on a 32-bit
/// target.
#[inline]
fn to_index<T: TryFrom<u64>>(value: u64) -> Result<T, FormatError> {
T::try_from(value).map_err(|_| too_large(value))
}
/// A count or offset into an in-memory buffer (a codec's progress counter,
@@ -33,7 +42,14 @@ pub fn to_usize(value: u64) -> Result<usize, FormatError> {
/// read from the file uses [`to_usize`].
#[inline]
pub fn saturating_usize(value: u64) -> usize {
usize::try_from(value).unwrap_or(usize::MAX)
saturating_index(value, usize::MAX)
}
/// [`saturating_usize`] for an index type of any width, whose largest
/// value is `max` (see [`to_index`]).
#[inline]
fn saturating_index<T: TryFrom<u64>>(value: u64, max: T) -> T {
T::try_from(value).unwrap_or(max)
}
#[cold]
@@ -66,16 +82,31 @@ mod tests {
#[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));
// Reachable through `usize` only where it is narrower than u64 (no
// such target runs tests in CI), so the same conversion is run with
// u32 standing in for a 32-bit usize.
let max = u64::from(u32::MAX);
assert_eq!(to_index::<u32>(max), Ok(u32::MAX));
for past in [max + 1, max + 0x10, 0x1_0000_1234, u64::MAX] {
let err = to_index::<u32>(past).unwrap_err();
assert!(
matches!(err, FormatError::Overflow(_)),
"{past:#x}: {err:?}"
);
}
// Where an `as` cast would have wrapped to a small, valid-looking
// index, it is not returned.
assert_eq!(0x1_0000_1234_u64 as u32, 0x1234);
assert!(to_index::<u32>(0x1_0000_1234).is_err());
assert_eq!(saturating_index(max + 1, u32::MAX), u32::MAX);
assert_eq!(saturating_index(0x1_0000_1234, u32::MAX), u32::MAX);
assert_eq!(saturating_index(0x1234, u32::MAX), 0x1234);
// And through `usize` itself, whichever width it has here.
match (usize::MAX as u64).checked_add(1) {
Some(past) => assert!(matches!(to_usize(past), Err(FormatError::Overflow(_)))),
None => assert_eq!(to_usize(u64::MAX), Ok(usize::MAX)),
}
}
}