use crate::block::padded_len;
use crate::error::FitsError;
use crate::error::Result;
use crate::header::Header;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HduKind {
Primary,
Image,
AsciiTable,
BinTable,
CompressedImage,
CompressedTable,
RandomGroups,
Other,
}
impl HduKind {
pub(crate) fn classify(header: &Header) -> HduKind {
if let Some(xtension) = header.get_text("XTENSION") {
match xtension {
"IMAGE" => HduKind::Image,
"TABLE" => HduKind::AsciiTable,
"BINTABLE" if header.get_logical("ZIMAGE") == Some(true) => {
HduKind::CompressedImage
}
"BINTABLE" if header.get_logical("ZTABLE") == Some(true) => {
HduKind::CompressedTable
}
"BINTABLE" => HduKind::BinTable,
_ => HduKind::Other,
}
} else if header.get_logical("GROUPS") == Some(true) {
HduKind::RandomGroups
} else {
HduKind::Primary
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct DataExtent {
pub(crate) data_bytes: u64,
pub(crate) padded_bytes: u64,
}
pub(crate) fn data_extent(header: &Header) -> Result<DataExtent> {
let elem = header.bitpix()?.elem_size() as u64;
let axes = header.axes()?;
let pcount = match header.get_integer("PCOUNT") {
Some(p) if p < 0 => return Err(FitsError::KeywordOutOfRange { name: "PCOUNT" }),
Some(p) => p as u64,
None => 0,
};
let gcount = match header.get_integer("GCOUNT") {
Some(g) if g < 1 => return Err(FitsError::KeywordOutOfRange { name: "GCOUNT" }),
Some(g) => g as u64,
None => 1,
};
let random_groups = header.get_logical("GROUPS") == Some(true);
let array_elems = if axes.is_empty() {
0
} else {
let array_axes: &[usize] = if random_groups { &axes[1..] } else { &axes };
array_axes
.iter()
.try_fold(1u64, |acc, &n| acc.checked_mul(n as u64))
.ok_or(FitsError::DataUnitOverflow)?
};
let group_size = pcount
.checked_add(array_elems)
.ok_or(FitsError::DataUnitOverflow)?;
let data_bytes = elem
.checked_mul(gcount)
.and_then(|n| n.checked_mul(group_size))
.ok_or(FitsError::DataUnitOverflow)?;
Ok(DataExtent {
data_bytes,
padded_bytes: padded_len(data_bytes),
})
}
#[cfg(test)]
mod tests;