///|
/// Errors raised by HDF5 reading and writing operations.
#derive(Eq, @debug.Debug)
pub(all) suberror Hdf5Error {
UnexpectedEof(Int64)
InvalidSignature
UnsupportedSuperblockVersion(Int32)
UnsupportedAddressSize(Int32)
UnsupportedFeature(String)
InvalidArgument(String)
InvalidField(String)
}
///|
pub extend Hdf5Error with Eq::{not_equal, equal}
///|
pub extend Hdf5Error with @debug.Debug::{to_repr}
///|
pub fn Hdf5Error::message(self : Hdf5Error) -> String {
match self {
UnexpectedEof(offset) =>
"unexpected end of file at byte " + offset.to_string()
InvalidSignature => "not an HDF5 file"
UnsupportedSuperblockVersion(version) =>
"unsupported superblock version " + version.to_string()
UnsupportedAddressSize(size) =>
"unsupported address size " + size.to_string()
UnsupportedFeature(feature) => "unsupported feature: " + feature
InvalidArgument(reason) => "invalid argument: " + reason
InvalidField(reason) => "invalid HDF5 data: " + reason
}
}
///|
/// A bounds-checked cursor over an immutable byte buffer.
priv struct Reader {
bytes : Bytes
mut pos : Int32
limit : Int32
}
///|
fn Reader::new(bytes : Bytes) -> Reader {
{ bytes, pos: 0, limit: bytes.length(), }
}
///|
fn Reader::at(bytes : Bytes, offset : Int32) -> Reader raise Hdf5Error {
guard offset >= 0 && offset <= bytes.length() else {
raise InvalidField("offset outside input")
}
{ bytes, pos: offset, limit: bytes.length(), }
}
///|
fn Reader::position(self : Reader) -> Int32 {
self.pos
}
///|
fn Reader::remaining(self : Reader) -> Int32 {
self.limit - self.pos
}
///|
fn Reader::sub(
self : Reader,
offset : Int32,
length : Int32,
) -> Reader raise Hdf5Error {
guard offset >= 0 &&
length >= 0 &&
offset.to_int64() + length.to_int64() <= self.limit.to_int64() else {
raise InvalidField("sub-reader outside input")
}
{ bytes: self.bytes, pos: offset, limit: offset + length, }
}
///|
fn Reader::require(self : Reader, n : Int32) -> Unit raise Hdf5Error {
let end = self.pos.to_int64() + n.to_int64()
guard n >= 0 && end <= self.limit.to_int64() else {
raise UnexpectedEof(self.pos.to_int64())
}
}
///|
fn Reader::skip(self : Reader, n : Int32) -> Unit raise Hdf5Error {
self.require(n)
self.pos = self.pos + n
}
///|
fn Reader::read_u8(self : Reader) -> Byte raise Hdf5Error {
self.require(1)
let value = self.bytes.unsafe_get(self.pos)
self.pos = self.pos + 1
value
}
///|
fn Reader::read_u16_le(self : Reader) -> UInt16 raise Hdf5Error {
self.require(2)
let value = self.bytes.unsafe_read_uint16_le(self.pos)
self.pos = self.pos + 2
value
}
///|
fn Reader::read_u32_le(self : Reader) -> UInt32 raise Hdf5Error {
self.require(4)
let value = self.bytes.unsafe_read_uint32_le(self.pos)
self.pos = self.pos + 4
value
}
///|
fn Reader::read_u64_le(self : Reader) -> UInt64 raise Hdf5Error {
self.require(8)
let value = self.bytes.unsafe_read_uint64_le(self.pos)
self.pos = self.pos + 8
value
}
///|
/// Reads an unsigned little-endian integer of 1, 2, 4 or 8 bytes.
fn Reader::read_uint_le(self : Reader, size : Int32) -> UInt64 raise Hdf5Error {
match size {
1 => self.read_u8().to_uint64()
2 => self.read_u16_le().to_uint64()
4 => self.read_u32_le().to_uint64()
8 => self.read_u64_le()
_ => raise UnsupportedAddressSize(size)
}
}
///|
fn Reader::read_var_uint_le(
self : Reader,
width : Int32,
) -> UInt64 raise Hdf5Error {
guard width >= 1 && width <= 8 else { raise UnsupportedAddressSize(width) }
let mut value = 0UL
for byte_index in 0.. Bytes raise Hdf5Error {
self.require(n)
let view = self.bytes.get_view(start=self.pos, end=self.pos + n)
let result = match view {
Some(view) => view.to_owned()
None => raise UnexpectedEof(self.pos.to_int64())
}
self.pos = self.pos + n
result
}