///|
fn round_up_to_eight(n : Int32) -> Int32 {
(n + 7) / 8 * 8
}
///|
/// Reads one object from an HDF5 global heap collection.
fn read_global_heap_object(
bytes : Bytes,
heap_address : UInt64,
object_index : Int32,
_offset_size : Int32,
length_size : Int32,
base_address : UInt64,
) -> Bytes raise Hdf5Error {
let sources : Array[&Hdf5Source] = [bytes]
read_source_global_heap_object(
sources[0],
heap_address,
object_index,
length_size,
base_address,
)
}
///|
fn read_source_global_heap_object(
source : &Hdf5Source,
heap_address : UInt64,
object_index : Int32,
length_size : Int32,
base_address : UInt64,
) -> Bytes raise Hdf5Error {
let offset = source_address_to_offset(heap_address, base_address)
let header_size = 8 + length_size
let reader = Reader::new(source.read_at(offset, header_size))
guard reader.read_bytes(4) == b"GCOL" else {
raise InvalidField("expected GCOL signature")
}
let version = reader.read_u8().to_int()
guard version == 1 else {
raise UnsupportedFeature("unsupported global heap version")
}
reader.skip(3)
let collection_size = reader.read_uint_le(length_size)
guard collection_size >= header_size.to_uint64() &&
offset <= source.length() &&
collection_size <= source.length() - offset else {
raise InvalidField("invalid global heap collection size")
}
let object_header_size = 8 + length_size
let mut cursor = header_size.to_uint64()
while cursor <= collection_size &&
object_header_size.to_uint64() <= collection_size - cursor {
let object_header = Reader::new(
source.read_at(offset + cursor, object_header_size),
)
let index = object_header.read_u16_le().to_int()
let _reference_count = object_header.read_u16_le()
let _reserved = object_header.read_u32_le()
let size = object_header.read_uint_le(length_size)
guard size <= 0x7FFFFFFFUL else {
raise InvalidField("global heap object is too large")
}
cursor = cursor + object_header_size.to_uint64()
if index == 0 {
break
}
guard size <= collection_size - cursor else {
raise InvalidField("global heap object extends past its collection")
}
let padding = (8 - size.to_int() % 8) % 8
guard padding.to_uint64() <= collection_size - cursor - size else {
raise InvalidField("global heap padding extends past its collection")
}
if index == object_index {
return source.read_at(offset + cursor, size.to_int())
}
cursor = cursor + size + padding.to_uint64()
}
raise InvalidField("global heap object not found")
}