///|
pub(all) enum ByteOrder {
Little
Big
} derive(Debug, Eq)
///|
pub extend ByteOrder with @debug.Debug::{to_repr}
///|
pub extend ByteOrder with Eq::{equal, not_equal}
///|
pub(all) enum StringPadding {
NullTerminated
NullPadded
SpacePadded
} derive(Debug, Eq)
///|
pub extend StringPadding with @debug.Debug::{to_repr}
///|
pub extend StringPadding with Eq::{equal, not_equal}
///|
/// Only fixed-size atomic types belong to the initial profile.
pub(all) enum DataType {
Signed(Int32, ByteOrder)
Unsigned(Int32, ByteOrder)
Float(Int32, ByteOrder)
FixedString(Int32, StringPadding, Bool)
} derive(Debug, Eq)
///|
pub extend DataType with @debug.Debug::{to_repr}
///|
pub extend DataType with Eq::{equal, not_equal}
///|
pub fn DataType::width(self : DataType) -> Int32 raise H5Error {
match self {
Signed(n, _) | Unsigned(n, _) if n == 1 || n == 2 || n == 4 || n == 8 => n
Float(n, _) if n == 4 || n == 8 => n
FixedString(n, _, _) if n > 0 && n <= 65535 => n
_ => raise Unsupported("unsupported atomic width", 0)
}
}
///|
fn DataType::encode(self : DataType) -> Bytes raise H5Error {
let n = self.width()
let b = buffer()
match self {
Signed(_, order) | Unsigned(_, order) => {
b.u8(0x10)
let sign = match self {
Signed(_, _) => 8
_ => 0
}
b.u8(sign | (if order == Big { 1 } else { 0 }))
b.uint(0UL, 2)
b.uint(n.to_uint64(), 4)
b.uint(0UL, 2)
b.uint((n * 8).to_uint64(), 2)
}
Float(_, order) => {
b.u8(0x11)
b.u8(0x20 | (if order == Big { 1 } else { 0 }))
b.u8(if n == 4 { 31 } else { 63 })
b.u8(0)
b.uint(n.to_uint64(), 4)
b.uint(0UL, 2)
b.uint((n * 8).to_uint64(), 2)
b.u8(if n == 4 { 23 } else { 52 })
b.u8(if n == 4 { 8 } else { 11 })
b.u8(0)
b.u8(if n == 4 { 23 } else { 52 })
b.uint(if n == 4 { 127UL } else { 1023UL }, 4)
}
FixedString(_, padding, utf8) => {
b.u8(0x13)
let pad = match padding {
NullTerminated => 0
NullPadded => 1
SpacePadded => 2
}
b.u8(pad | (if utf8 { 16 } else { 0 }))
b.uint(0UL, 2)
b.uint(n.to_uint64(), 4)
}
}
b.finish()
}
///|
fn decode_type(c : Cursor) -> DataType raise H5Error {
let start = c.pos
let tag = c.u8()
if tag >> 4 < 1 || tag >> 4 > 4 {
raise Unsupported("datatype version", start)
}
let flags = c.index(2)
let flag2 = c.u8()
let n = c.index(4)
let order = if (flags & 1) == 1 { Big } else { Little }
let dtype = match tag & 15 {
0 => {
if (flags & 0xfff6) != 0 ||
flag2 != 0 ||
c.index(2) != 0 ||
c.index(2) != checked_mul(n, 8) {
raise Unsupported("noncanonical integer datatype", start)
}
if (flags & 8) != 0 {
Signed(n, order)
} else {
Unsigned(n, order)
}
}
1 => {
let tail = c.bytes(12)
let candidate = Float(n, order)
let expected = candidate.encode()
if flags !=
(
0x20 |
(if order == Big { 1 } else { 0 }) |
((if n == 4 { 31 } else { 63 }) << 8)
) ||
flag2 != 0 ||
tail != expected[8:].to_owned() {
raise Unsupported("non-IEEE floating datatype", start)
}
candidate
}
3 => {
if flag2 != 0 || flags > 0x12 || (flags & 15) > 2 {
raise Unsupported("string charset or padding", start)
}
FixedString(
n,
match flags & 15 {
0 => NullTerminated
1 => NullPadded
_ => SpacePadded
},
(flags & 16) != 0,
)
}
_ => raise Unsupported("compound/vlen/reference datatype", start)
}
ignore(dtype.width())
dtype
}
///|
test "atomic datatypes roundtrip without losing endian or padding" {
let cases = [
Signed(8, Little),
Unsigned(2, Big),
Float(4, Little),
Float(8, Big),
FixedString(17, SpacePadded, true),
]
for dtype in cases {
let bytes = dtype.encode()
assert_eq(decode_type(cursor(bytes, 0, bytes.length())), dtype)
}
assert_true(
try {
ignore(Signed(3, Little).width())
false
} catch {
_ => true
},
)
}