use bitnuc::BitSize;
use byteorder::{ByteOrder, LittleEndian};
use std::io::{Read, Write};
use crate::error::{BuilderError, HeaderError, Result};
#[allow(clippy::unreadable_literal)]
const MAGIC: u32 = 0x51455342;
const FORMAT: u8 = 1;
pub const SIZE_HEADER: usize = 32;
pub const RESERVED: [u8; 17] = [42; 17];
#[derive(Debug, Clone, Copy)]
pub struct FileHeaderBuilder {
slen: Option<u32>,
xlen: Option<u32>,
bitsize: Option<BitSize>,
flags: Option<bool>,
}
impl Default for FileHeaderBuilder {
fn default() -> Self {
Self::new()
}
}
impl FileHeaderBuilder {
#[must_use]
pub fn new() -> Self {
FileHeaderBuilder {
slen: None,
xlen: None,
bitsize: None,
flags: None,
}
}
#[must_use]
pub fn slen(mut self, slen: u32) -> Self {
self.slen = Some(slen);
self
}
#[must_use]
pub fn xlen(mut self, xlen: u32) -> Self {
self.xlen = Some(xlen);
self
}
#[must_use]
pub fn bitsize(mut self, bitsize: BitSize) -> Self {
self.bitsize = Some(bitsize);
self
}
#[must_use]
pub fn flags(mut self, flags: bool) -> Self {
self.flags = Some(flags);
self
}
pub fn build(self) -> Result<FileHeader> {
Ok(FileHeader {
magic: MAGIC,
format: FORMAT,
slen: if let Some(slen) = self.slen {
slen
} else {
return Err(BuilderError::MissingSlen.into());
},
xlen: self.xlen.unwrap_or(0),
bits: self.bitsize.unwrap_or_default(),
flags: self.flags.unwrap_or(false),
reserved: RESERVED,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FileHeader {
pub magic: u32,
pub format: u8,
pub slen: u32,
pub xlen: u32,
pub bits: BitSize,
pub flags: bool,
pub reserved: [u8; 17],
}
impl FileHeader {
#[must_use]
pub fn new(bits: BitSize, slen: u32, flags: bool) -> Self {
Self {
magic: MAGIC,
format: FORMAT,
slen,
xlen: 0,
bits,
flags,
reserved: RESERVED,
}
}
#[must_use]
pub fn new_extended(bits: BitSize, slen: u32, xlen: u32, flags: bool) -> Self {
Self {
magic: MAGIC,
format: FORMAT,
slen,
xlen,
bits,
flags,
reserved: RESERVED,
}
}
pub fn set_bitsize(&mut self, bits: BitSize) {
self.bits = bits;
}
#[must_use]
pub fn is_paired(&self) -> bool {
self.xlen > 0
}
pub fn from_bytes(buffer: &[u8; SIZE_HEADER]) -> Result<Self> {
let magic = LittleEndian::read_u32(&buffer[0..4]);
if magic != MAGIC {
return Err(HeaderError::InvalidMagicNumber(magic).into());
}
let format = buffer[4];
if format != FORMAT {
return Err(HeaderError::InvalidFormatVersion(format).into());
}
let slen = LittleEndian::read_u32(&buffer[5..9]);
let xlen = LittleEndian::read_u32(&buffer[9..13]);
let bits = match buffer[13] {
0 | 2 | 42 => BitSize::Two,
4 => BitSize::Four,
x => return Err(HeaderError::InvalidBitSize(x).into()),
};
let flags = buffer[14] != 0;
let Ok(reserved) = buffer[15..32].try_into() else {
return Err(HeaderError::InvalidReservedBytes.into());
};
Ok(Self {
magic,
format,
slen,
xlen,
bits,
flags,
reserved,
})
}
pub fn from_buffer(buffer: &[u8]) -> Result<Self> {
let mut bytes = [0u8; SIZE_HEADER];
if buffer.len() < SIZE_HEADER {
return Err(HeaderError::InvalidSize(buffer.len(), SIZE_HEADER).into());
}
bytes.copy_from_slice(&buffer[..SIZE_HEADER]);
Self::from_bytes(&bytes)
}
pub fn write_bytes<W: Write>(&self, writer: &mut W) -> Result<()> {
let mut buffer = [0u8; SIZE_HEADER];
LittleEndian::write_u32(&mut buffer[0..4], self.magic);
buffer[4] = self.format;
LittleEndian::write_u32(&mut buffer[5..9], self.slen);
LittleEndian::write_u32(&mut buffer[9..13], self.xlen);
buffer[13] = self.bits.into();
buffer[14] = self.flags.into();
buffer[15..32].copy_from_slice(&self.reserved);
writer.write_all(&buffer)?;
Ok(())
}
pub fn from_reader<R: Read>(reader: &mut R) -> Result<Self> {
let mut buffer = [0u8; SIZE_HEADER];
reader.read_exact(&mut buffer)?;
Self::from_bytes(&buffer)
}
}