use crate::Mode;
const OFFSET_NX: usize = 0;
const OFFSET_NY: usize = 4;
const OFFSET_NZ: usize = 8;
const OFFSET_MODE: usize = 12;
const OFFSET_NXSTART: usize = 16;
const OFFSET_NYSTART: usize = 20;
const OFFSET_NZSTART: usize = 24;
const OFFSET_MX: usize = 28;
const OFFSET_MY: usize = 32;
const OFFSET_MZ: usize = 36;
const OFFSET_XLEN: usize = 40;
const OFFSET_YLEN: usize = 44;
const OFFSET_ZLEN: usize = 48;
const OFFSET_ALPHA: usize = 52;
const OFFSET_BETA: usize = 56;
const OFFSET_GAMMA: usize = 60;
const OFFSET_MAPC: usize = 64;
const OFFSET_MAPR: usize = 68;
const OFFSET_MAPS: usize = 72;
const OFFSET_DMIN: usize = 76;
const OFFSET_DMAX: usize = 80;
const OFFSET_DMEAN: usize = 84;
const OFFSET_ISPG: usize = 88;
const OFFSET_NSYMBT: usize = 92;
const OFFSET_EXTRA: usize = 96;
const OFFSET_EXTTYP: usize = 104; const OFFSET_NVERSION: usize = 108; const OFFSET_ORIGIN: usize = 196;
const OFFSET_MAP: usize = 208;
const OFFSET_MACHST: usize = 212;
const OFFSET_RMS: usize = 216;
const OFFSET_NLABL: usize = 220;
const OFFSET_LABEL: usize = 224;
#[repr(C, align(4))]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Header {
pub nx: i32,
pub ny: i32,
pub nz: i32,
pub mode: i32,
pub nxstart: i32,
pub nystart: i32,
pub nzstart: i32,
pub mx: i32,
pub my: i32,
pub mz: i32,
pub xlen: f32,
pub ylen: f32,
pub zlen: f32,
pub alpha: f32,
pub beta: f32,
pub gamma: f32,
pub mapc: i32,
pub mapr: i32,
pub maps: i32,
pub dmin: f32,
pub dmax: f32,
pub dmean: f32,
pub ispg: i32,
pub nsymbt: i32,
pub extra: [u8; 100],
pub origin: [f32; 3],
pub map: [u8; 4],
pub machst: [u8; 4],
pub rms: f32,
pub nlabl: i32,
pub label: [u8; 800],
}
impl Default for Header {
fn default() -> Self {
Self::new()
}
}
impl Header {
#[inline]
pub const fn new() -> Self {
Self {
nx: 0,
ny: 0,
nz: 0,
mode: 2, nxstart: 0,
nystart: 0,
nzstart: 0,
mx: 0,
my: 0,
mz: 0,
xlen: 1.0, ylen: 1.0,
zlen: 1.0,
alpha: 90.0,
beta: 90.0,
gamma: 90.0,
mapc: 1, mapr: 2, maps: 3, dmin: f32::INFINITY, dmax: f32::NEG_INFINITY, dmean: f32::NEG_INFINITY, ispg: 1, nsymbt: 0,
extra: [0u8; 100], origin: [0.0; 3],
map: *b"MAP ",
machst: [0x44, 0x44, 0x00, 0x00], rms: -1.0, nlabl: 0,
label: [0; 800],
}
}
#[inline]
pub const fn data_offset(&self) -> usize {
1024 + self.nsymbt as usize
}
#[inline]
pub fn data_size(&self) -> usize {
let n = (self.nx as usize) * (self.ny as usize) * (self.nz as usize);
match Mode::from_i32(self.mode) {
Some(mode) => {
let byte_size = mode.byte_size();
match mode {
Mode::Packed4Bit => n.div_ceil(2), _ => n * byte_size,
}
}
None => 0, }
}
#[inline]
pub fn validate(&self) -> bool {
self.nx > 0
&& self.ny > 0
&& self.nz > 0
&& Mode::from_i32(self.mode).is_some()
&& self.validate_map()
&& (self.ispg == 0 || (self.ispg >= 1 && self.ispg <= 230) || (self.ispg >= 400 && self.ispg <= 630))
&& matches!(self.mapc, 1..=3)
&& matches!(self.mapr, 1..=3)
&& matches!(self.maps, 1..=3)
&& self.mapc != self.mapr
&& self.mapc != self.maps
&& self.mapr != self.maps
&& self.nsymbt >= 0
&& self.nlabl >= 0 && self.nlabl <= 10
}
#[inline]
fn validate_map(&self) -> bool {
if self.map == *b"MAP " {
return true;
}
if &self.map[..3] == b"MAP"
&& (self.map[3] == b' ' || self.map[3] == 0 || self.map[3] == b'I')
{
return true;
}
if self.map == [0; 4] {
return true;
}
false
}
#[inline]
pub fn exttyp(&self) -> [u8; 4] {
[
self.extra[OFFSET_EXTTYP - OFFSET_EXTRA],
self.extra[OFFSET_EXTTYP - OFFSET_EXTRA + 1],
self.extra[OFFSET_EXTTYP - OFFSET_EXTRA + 2],
self.extra[OFFSET_EXTTYP - OFFSET_EXTRA + 3],
]
}
#[inline]
pub fn set_exttyp(&mut self, value: [u8; 4]) {
let start = OFFSET_EXTTYP - OFFSET_EXTRA;
self.extra[start..start + 4].copy_from_slice(&value);
}
#[inline]
pub fn exttyp_str(&self) -> Result<&str, core::str::Utf8Error> {
let start = OFFSET_EXTTYP - OFFSET_EXTRA;
core::str::from_utf8(&self.extra[start..start + 4])
}
#[inline]
pub fn set_exttyp_str(&mut self, value: &str) -> Result<(), &'static str> {
if value.len() != 4 {
return Err("EXTTYP must be exactly 4 characters");
}
let bytes = value.as_bytes();
let start = OFFSET_EXTTYP - OFFSET_EXTRA;
self.extra[start..start + 4].copy_from_slice(bytes);
Ok(())
}
#[inline]
pub fn nversion(&self) -> i32 {
use crate::engine::codec::EndianCodec;
let file_endian = self.detect_endian();
let start = OFFSET_NVERSION - OFFSET_EXTRA;
i32::decode(&self.extra[start..start + 4], 0, file_endian)
}
#[inline]
pub fn set_nversion(&mut self, value: i32) {
use crate::engine::codec::EndianCodec;
let file_endian = self.detect_endian();
let start = OFFSET_NVERSION - OFFSET_EXTRA;
value.encode(&mut self.extra[start..start + 4], 0, file_endian);
}
#[inline]
pub fn detect_endian(&self) -> crate::FileEndian {
crate::FileEndian::from_machst(&self.machst)
}
#[inline]
pub fn set_file_endian(&mut self, endian: crate::FileEndian) {
self.machst = endian.to_machst();
}
pub fn decode_from_bytes(bytes: &[u8; 1024]) -> Self {
use crate::engine::codec::EndianCodec;
use crate::engine::endian::FileEndian;
let machst = [bytes[OFFSET_MACHST], bytes[OFFSET_MACHST + 1], bytes[OFFSET_MACHST + 2], bytes[OFFSET_MACHST + 3]];
let file_endian = FileEndian::from_machst(&machst);
let mut header = Self::new();
header.nx = i32::decode(bytes, OFFSET_NX, file_endian);
header.ny = i32::decode(bytes, OFFSET_NY, file_endian);
header.nz = i32::decode(bytes, OFFSET_NZ, file_endian);
header.mode = i32::decode(bytes, OFFSET_MODE, file_endian);
header.nxstart = i32::decode(bytes, OFFSET_NXSTART, file_endian);
header.nystart = i32::decode(bytes, OFFSET_NYSTART, file_endian);
header.nzstart = i32::decode(bytes, OFFSET_NZSTART, file_endian);
header.mx = i32::decode(bytes, OFFSET_MX, file_endian);
header.my = i32::decode(bytes, OFFSET_MY, file_endian);
header.mz = i32::decode(bytes, OFFSET_MZ, file_endian);
header.xlen = f32::decode(bytes, OFFSET_XLEN, file_endian);
header.ylen = f32::decode(bytes, OFFSET_YLEN, file_endian);
header.zlen = f32::decode(bytes, OFFSET_ZLEN, file_endian);
header.alpha = f32::decode(bytes, OFFSET_ALPHA, file_endian);
header.beta = f32::decode(bytes, OFFSET_BETA, file_endian);
header.gamma = f32::decode(bytes, OFFSET_GAMMA, file_endian);
header.mapc = i32::decode(bytes, OFFSET_MAPC, file_endian);
header.mapr = i32::decode(bytes, OFFSET_MAPR, file_endian);
header.maps = i32::decode(bytes, OFFSET_MAPS, file_endian);
header.dmin = f32::decode(bytes, OFFSET_DMIN, file_endian);
header.dmax = f32::decode(bytes, OFFSET_DMAX, file_endian);
header.dmean = f32::decode(bytes, OFFSET_DMEAN, file_endian);
header.ispg = i32::decode(bytes, OFFSET_ISPG, file_endian);
header.nsymbt = i32::decode(bytes, OFFSET_NSYMBT, file_endian);
header.extra.copy_from_slice(&bytes[OFFSET_EXTRA..OFFSET_ORIGIN]);
header.origin[0] = f32::decode(bytes, OFFSET_ORIGIN, file_endian);
header.origin[1] = f32::decode(bytes, OFFSET_ORIGIN + 4, file_endian);
header.origin[2] = f32::decode(bytes, OFFSET_ORIGIN + 8, file_endian);
header.map.copy_from_slice(&bytes[OFFSET_MAP..OFFSET_MACHST]);
header.machst.copy_from_slice(&bytes[OFFSET_MACHST..OFFSET_RMS]);
header.rms = f32::decode(bytes, OFFSET_RMS, file_endian);
header.nlabl = i32::decode(bytes, OFFSET_NLABL, file_endian);
header.label.copy_from_slice(&bytes[OFFSET_LABEL..1024]);
header
}
pub fn encode_to_bytes(&self, out: &mut [u8; 1024]) {
use crate::engine::codec::EndianCodec;
let file_endian = self.detect_endian();
self.nx.encode(out, OFFSET_NX, file_endian);
self.ny.encode(out, OFFSET_NY, file_endian);
self.nz.encode(out, OFFSET_NZ, file_endian);
self.mode.encode(out, OFFSET_MODE, file_endian);
self.nxstart.encode(out, OFFSET_NXSTART, file_endian);
self.nystart.encode(out, OFFSET_NYSTART, file_endian);
self.nzstart.encode(out, OFFSET_NZSTART, file_endian);
self.mx.encode(out, OFFSET_MX, file_endian);
self.my.encode(out, OFFSET_MY, file_endian);
self.mz.encode(out, OFFSET_MZ, file_endian);
self.xlen.encode(out, OFFSET_XLEN, file_endian);
self.ylen.encode(out, OFFSET_YLEN, file_endian);
self.zlen.encode(out, OFFSET_ZLEN, file_endian);
self.alpha.encode(out, OFFSET_ALPHA, file_endian);
self.beta.encode(out, OFFSET_BETA, file_endian);
self.gamma.encode(out, OFFSET_GAMMA, file_endian);
self.mapc.encode(out, OFFSET_MAPC, file_endian);
self.mapr.encode(out, OFFSET_MAPR, file_endian);
self.maps.encode(out, OFFSET_MAPS, file_endian);
self.dmin.encode(out, OFFSET_DMIN, file_endian);
self.dmax.encode(out, OFFSET_DMAX, file_endian);
self.dmean.encode(out, OFFSET_DMEAN, file_endian);
self.ispg.encode(out, OFFSET_ISPG, file_endian);
self.nsymbt.encode(out, OFFSET_NSYMBT, file_endian);
out[OFFSET_EXTRA..OFFSET_ORIGIN].copy_from_slice(&self.extra);
self.origin[0].encode(out, OFFSET_ORIGIN, file_endian);
self.origin[1].encode(out, OFFSET_ORIGIN + 4, file_endian);
self.origin[2].encode(out, OFFSET_ORIGIN + 8, file_endian);
out[OFFSET_MAP..OFFSET_MACHST].copy_from_slice(&self.map);
out[OFFSET_MACHST..OFFSET_RMS].copy_from_slice(&self.machst);
self.rms.encode(out, OFFSET_RMS, file_endian);
self.nlabl.encode(out, OFFSET_NLABL, file_endian);
out[OFFSET_LABEL..1024].copy_from_slice(&self.label);
}
}
#[derive(Debug, Clone, Copy)]
pub struct ExtHeader<'a> {
bytes: &'a [u8],
}
impl<'a> ExtHeader<'a> {
#[inline]
pub fn new(bytes: &'a [u8]) -> Self {
Self { bytes }
}
#[inline]
pub fn len(&self) -> usize {
self.bytes.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.bytes.is_empty()
}
#[inline]
pub fn as_bytes(&self) -> &'a [u8] {
self.bytes
}
}
#[derive(Debug)]
pub struct ExtHeaderMut<'a> {
bytes: &'a mut [u8],
}
impl<'a> ExtHeaderMut<'a> {
#[inline]
pub fn new(bytes: &'a mut [u8]) -> Self {
Self { bytes }
}
#[inline]
pub fn len(&self) -> usize {
self.bytes.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.bytes.is_empty()
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
self.bytes
}
#[inline]
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
self.bytes
}
}