///|
/// Returns new storage, preserving the source even if validation fails.
pub fn ArrayValue::with_selection(
self : ArrayValue,
selection : Selection,
replacement : ArrayValue,
) -> ArrayValue raise H5Error {
if self.space != selection.source || replacement.space != selection.result {
raise Invalid("replacement/selection extent mismatch", 0)
}
if self.dtype != replacement.dtype {
raise Invalid("replacement datatype differs", 0)
}
if self.raw.length() > selection.limits.max_file_bytes {
raise Limit("patched array bytes")
}
let spans = selection.spans(self.dtype)
let out = self.raw.to_array()
for span in spans {
for i = 0; i < span.length; i = i + 1 {
out[span.source_byte + i] = replacement.raw[span.output_byte + i]
}
}
ArrayValue::new(
self.dtype,
self.space,
Bytes::from_array(out),
limits=selection.limits,
)
}
///|
/// Exchange byte order only; floating payloads and integer bit patterns survive.
pub fn ArrayValue::with_byte_order(
self : ArrayValue,
order : ByteOrder,
) -> ArrayValue raise H5Error {
let (dtype, old) = match self.dtype {
Signed(width, old) => (Signed(width, order), old)
Unsigned(width, old) => (Unsigned(width, order), old)
Float(width, old) => (Float(width, order), old)
FixedString(_, _, _) => return self
}
if old == order {
return self
}
let width = dtype.width()
let out = self.raw.to_array()
for at = 0; at < out.length(); at = at + width {
for j = 0; j < width; j = j + 1 {
out[at + j] = self.raw[at + width - 1 - j]
}
}
// The source has already been validated; no new extent is introduced.
{ ..self, dtype, raw: Bytes::from_array(out), }
}
///|
test "functional hyperslab update is atomic and preserves unselected cells" {
let a = ArrayValue::from_signed(
Simple([3, 4]),
Array::makei(12, i => i.to_int64()),
)
let s = Selection::new(a.space, [
{ start: 0, stride: 2, count: 2, block: 1, },
{ start: 1, stride: 1, count: 2, block: 1, },
])
let replacement = ArrayValue::from_signed(Simple([2, 2]), [40L, 41L, 42L, 43L])
let updated = a.with_selection(s, replacement)
assert_eq(a.signed_at(1), 1L)
assert_eq(updated.signed_at(1), 40L)
assert_eq(updated.signed_at(2), 41L)
assert_eq(updated.signed_at(9), 42L)
assert_eq(updated.signed_at(0), 0L)
assert_eq(updated.select(s).bytes(), replacement.bytes())
assert_true(
try {
ignore(a.with_selection(s, ArrayValue::from_signed(Scalar, [1L])))
false
} catch {
_ => true
},
)
}
///|
test "byte order change preserves negative zero NaN and large integers" {
let bits = ArrayValue::new(
Float(8, Little),
Simple([2]),
b"\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00\x00\x00\xf8\x7f",
)
let big = bits.with_byte_order(Big)
assert_eq(big.float_at(0).reinterpret_as_uint64(), 0x8000000000000000UL)
assert_eq(big.float_at(1).reinterpret_as_uint64(), 0x7ff8000000000001UL)
assert_eq(big.with_byte_order(Little).bytes(), bits.bytes())
let integer = ArrayValue::from_unsigned(Scalar, [0xfedcba9876543210UL])
assert_eq(integer.with_byte_order(Big).unsigned_at(0), 0xfedcba9876543210UL)
}