///|
/// HDF5 metadata checksum: Bob Jenkins' public-domain lookup3 algorithm.
fn rotate(x : UInt32, n : Int32) -> UInt32 {
(x << n) | (x >> (32 - n))
}
///|
/// The mixing steps are kept explicit to preserve modular UInt arithmetic.
pub fn metadata_checksum(data : Bytes) -> UInt32 {
let mut a = 0xdeadbeefU + data.length().reinterpret_as_uint()
let mut b = a
let mut c = a
let mut pos = 0
while data.length() - pos > 12 {
for i = 0; i < 4; i = i + 1 {
a += data[pos + i].to_uint() << (8 * i)
b += data[pos + 4 + i].to_uint() << (8 * i)
c += data[pos + 8 + i].to_uint() << (8 * i)
}
a -= c
a = a ^ rotate(c, 4)
c += b
b -= a
b = b ^ rotate(a, 6)
a += c
c -= b
c = c ^ rotate(b, 8)
b += a
a -= c
a = a ^ rotate(c, 16)
c += b
b -= a
b = b ^ rotate(a, 19)
a += c
c -= b
c = c ^ rotate(b, 4)
b += a
pos += 12
}
let remaining = data.length() - pos
if remaining == 0 {
return c
}
for i = 0; i < remaining; i = i + 1 {
let x = data[pos + i].to_uint() << (8 * (i % 4))
if i < 4 {
a += x
} else if i < 8 {
b += x
} else {
c += x
}
}
c = c ^ b
c -= rotate(b, 14)
a = a ^ c
a -= rotate(c, 11)
b = b ^ a
b -= rotate(a, 25)
c = c ^ b
c -= rotate(b, 16)
a = a ^ c
a -= rotate(c, 4)
b = b ^ a
b -= rotate(a, 14)
c = c ^ b
c -= rotate(b, 24)
c
}
///|
fn verify_checksum(
data : Bytes,
start : Int32,
size : Int32,
) -> Unit raise H5Error {
if size < 4 {
raise Invalid("checksum block too small", start)
}
let expected = cursor(data, checked_add(start, size - 4), 4).uint(4).to_uint()
if metadata_checksum(bytes_at(data, start, size - 4)) != expected {
raise Invalid("metadata checksum mismatch", start)
}
}
///|
test "lookup3 public known empty vector and block sensitivity" {
assert_eq(metadata_checksum(b""), 0xdeadbeefU)
assert_eq(metadata_checksum(b"a"), 0x58d68708U)
assert_true(
metadata_checksum(b"abcdefghijkl") != metadata_checksum(b"abcdefghijkm"),
)
let b = buffer()
b.bytes(b"OHDR example")
b.uint(metadata_checksum(b.finish()).to_uint64(), 4)
verify_checksum(b.finish(), 0, b.length())
}