use std::io::Read;
use std::io::Write;
use crate::bitpix::Bitpix;
use crate::error::FitsError;
use crate::error::Result;
use super::be_to_i64_into;
pub(super) const DEFAULT_GZIP_LEVEL: u32 = 1;
pub(super) fn gzip_encode(raw: &[u8], level: u32) -> Vec<u8> {
let mut enc = flate2::write::GzEncoder::new(Vec::new(), flate2::Compression::new(level));
enc.write_all(raw).expect("gzip into a Vec cannot fail");
enc.finish().expect("gzip finish into a Vec cannot fail")
}
pub(super) fn gzip2_encode(raw: &[u8], width: usize, level: u32) -> Vec<u8> {
gzip_encode(&shuffle_bytes(raw, width), level)
}
pub(super) fn shuffle_bytes(raw: &[u8], width: usize) -> Vec<u8> {
if width <= 1 {
return raw.to_vec();
}
let n = raw.len() / width;
let mut out = vec![0u8; raw.len()];
for p in 0..width {
for i in 0..n {
out[p * n + i] = raw[i * width + p];
}
}
out
}
pub(super) fn unshuffle_bytes(shuffled: &[u8], width: usize) -> Vec<u8> {
if width <= 1 {
return shuffled.to_vec();
}
let n = shuffled.len() / width;
let mut out = vec![0u8; shuffled.len()];
for p in 0..width {
for i in 0..n {
out[i * width + p] = shuffled[p * n + i];
}
}
out
}
pub(super) fn gunzip(bytes: &[u8], max_out: usize) -> Result<Vec<u8>> {
let mut out = Vec::new();
flate2::read::GzDecoder::new(bytes)
.take(max_out.saturating_add(1) as u64)
.read_to_end(&mut out)?;
if out.len() > max_out {
return Err(FitsError::UnsupportedCompression {
name: "gzip tile expands beyond its declared tile size".to_string(),
});
}
Ok(out)
}
pub(super) fn gzip_tile_into(
bytes: &[u8],
bitpix: Bitpix,
tile_elems: usize,
out: &mut Vec<i64>,
) -> Result<()> {
let raw = gunzip(bytes, tile_elems.saturating_mul(bitpix.elem_size()))?;
be_to_i64_into(&raw, bitpix, out);
Ok(())
}
pub(super) fn gzip2_tile_into(
bytes: &[u8],
bitpix: Bitpix,
tile_elems: usize,
out: &mut Vec<i64>,
) -> Result<()> {
let raw = unshuffle_bytes(
&gunzip(bytes, tile_elems.saturating_mul(bitpix.elem_size()))?,
bitpix.elem_size(),
);
be_to_i64_into(&raw, bitpix, out);
Ok(())
}