///|
/// The HDF5 on-disk signature.
let hdf5_signature : Bytes = b"\x89HDF\r\n\x1a\n"
///|
fn signature_at(bytes : Bytes, offset : Int32) -> Bool {
guard offset >= 0 && offset + 8 <= bytes.length() else { false }
match bytes.get_view(start=offset, end=offset + 8) {
Some(view) => view.equal_to_bytes(hdf5_signature)
None => false
}
}
///|
/// Locates the HDF5 superblock, allowing for a user block before it.
///
/// HDF5 places the superblock at byte 0 or at a power-of-two offset of at
/// least 512 bytes. The scan stops after 1 MiB, matching libhdf5's practical
/// user-block limit.
fn find_superblock_offset(bytes : Bytes) -> Int32 raise Hdf5Error {
if signature_at(bytes, 0) {
return 0
}
let mut offset = 512
while offset <= 1048576 && offset + 8 <= bytes.length() {
if signature_at(bytes, offset) {
return offset
}
offset = offset * 2
}
raise InvalidSignature
}
///|
/// Parsed HDF5 superblock.
priv struct Superblock {
_superblock_offset : Int32
superblock_end : Int32
version : Int32
offset_size : Int32
length_size : Int32
file_consistency_flags : UInt32
base_address : UInt64
free_space_address : UInt64
end_of_file_address : UInt64
driver_info_address : UInt64
superblock_extension_address : UInt64
root_group_address : UInt64
root_group_object_header : UInt64
group_leaf_node_k : UInt16
group_internal_node_k : UInt16
superblock_checksum : UInt32
}
///|
fn validate_address_sizes(
offset_size : Int32,
length_size : Int32,
) -> Unit raise Hdf5Error {
guard offset_size == 2 || offset_size == 4 || offset_size == 8 else {
raise UnsupportedAddressSize(offset_size)
}
guard length_size == 2 || length_size == 4 || length_size == 8 else {
raise UnsupportedAddressSize(length_size)
}
}
///|
fn parse_superblock_v0(
reader : Reader,
offset : Int32,
version : Int32,
) -> Superblock raise Hdf5Error {
let _free_storage_version = reader.read_u8()
let _root_group_version = reader.read_u8()
let _reserved_0 = reader.read_u8()
let _shared_header_version = reader.read_u8()
let offset_size = reader.read_u8().to_int()
let length_size = reader.read_u8().to_int()
let _reserved_1 = reader.read_u8()
validate_address_sizes(offset_size, length_size)
let group_leaf_node_k = reader.read_u16_le()
let group_internal_node_k = reader.read_u16_le()
let file_consistency_flags = reader.read_u32_le()
let base_address = reader.read_uint_le(offset_size)
let free_space_address = reader.read_uint_le(offset_size)
let end_of_file_address = reader.read_uint_le(offset_size)
let driver_info_address = reader.read_uint_le(offset_size)
let _root_link_name_offset = reader.read_uint_le(offset_size)
let root_group_object_header = reader.read_uint_le(offset_size)
let _root_cache_type = reader.read_u32_le()
let _root_reserved = reader.read_u32_le()
let _root_scratch = reader.read_bytes(16)
{
_superblock_offset: offset,
superblock_end: reader.position(),
version,
offset_size,
length_size,
file_consistency_flags,
base_address,
free_space_address,
end_of_file_address,
driver_info_address,
superblock_extension_address: 0UL,
root_group_address: root_group_object_header,
root_group_object_header,
group_leaf_node_k,
group_internal_node_k,
superblock_checksum: 0U,
}
}
///|
fn parse_superblock_v2(
reader : Reader,
offset : Int32,
version : Int32,
) -> Superblock raise Hdf5Error {
let offset_size = reader.read_u8().to_int()
let length_size = reader.read_u8().to_int()
let file_consistency_flags = reader.read_u8().to_int().reinterpret_as_uint()
validate_address_sizes(offset_size, length_size)
let base_address = reader.read_uint_le(offset_size)
let superblock_extension_address = reader.read_uint_le(offset_size)
let end_of_file_address = reader.read_uint_le(offset_size)
let root_group_address = reader.read_uint_le(offset_size)
let superblock_checksum = reader.read_u32_le()
verify_metadata_checksum(
reader.bytes,
offset,
reader.position() - metadata_checksum_size,
"superblock",
)
{
_superblock_offset: offset,
superblock_end: reader.position(),
version,
offset_size,
length_size,
file_consistency_flags,
base_address,
free_space_address: 0UL,
end_of_file_address,
driver_info_address: 0UL,
superblock_extension_address,
root_group_address,
root_group_object_header: root_group_address,
group_leaf_node_k: 0,
group_internal_node_k: 0,
superblock_checksum,
}
}
///|
/// Parses the HDF5 superblock, including a leading user block if present.
fn parse_superblock(bytes : Bytes) -> Superblock raise Hdf5Error {
let offset = find_superblock_offset(bytes)
let reader = Reader::at(bytes, offset)
let _signature = reader.read_bytes(8)
let version = reader.read_u8().to_int()
match version {
0 | 1 => parse_superblock_v0(reader, offset, version)
2 | 3 => parse_superblock_v2(reader, offset, version)
_ => raise UnsupportedSuperblockVersion(version)
}
}