use std::io::{Read, Write};
use bitnuc::BitSize;
use byteorder::{ByteOrder, LittleEndian};
use crate::error::{HeaderError, ReadError, Result};
#[allow(clippy::unreadable_literal)]
const MAGIC: u32 = 0x51455356;
#[allow(clippy::unreadable_literal)]
const BLOCK_MAGIC: u64 = 0x5145534B434F4C42;
const FORMAT: u8 = 1;
pub const SIZE_HEADER: usize = 32;
pub const SIZE_BLOCK_HEADER: usize = 32;
pub const BLOCK_SIZE: u64 = 128 * 1024;
pub const RESERVED_BYTES: [u8; 13] = [42; 13];
pub const RESERVED_BYTES_BLOCK: [u8; 12] = [42; 12];
#[derive(Default, Debug, Clone, Copy)]
pub struct FileHeaderBuilder {
qual: Option<bool>,
block: Option<u64>,
compressed: Option<bool>,
paired: Option<bool>,
bitsize: Option<BitSize>,
headers: Option<bool>,
flags: Option<bool>,
}
impl FileHeaderBuilder {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn qual(mut self, qual: bool) -> Self {
self.qual = Some(qual);
self
}
#[must_use]
pub fn block(mut self, block: u64) -> Self {
self.block = Some(block);
self
}
#[must_use]
pub fn compressed(mut self, compressed: bool) -> Self {
self.compressed = Some(compressed);
self
}
#[must_use]
pub fn paired(mut self, paired: bool) -> Self {
self.paired = Some(paired);
self
}
#[must_use]
pub fn bitsize(mut self, bitsize: BitSize) -> Self {
self.bitsize = Some(bitsize);
self
}
#[must_use]
pub fn headers(mut self, headers: bool) -> Self {
self.headers = Some(headers);
self
}
#[must_use]
pub fn flags(mut self, flags: bool) -> Self {
self.flags = Some(flags);
self
}
#[must_use]
pub fn build(self) -> FileHeader {
FileHeader::with_capacity(
self.block.unwrap_or(BLOCK_SIZE),
self.qual.unwrap_or(false),
self.compressed.unwrap_or(false),
self.paired.unwrap_or(false),
self.bitsize.unwrap_or_default(),
self.headers.unwrap_or(false),
self.flags.unwrap_or(false),
)
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct FileHeader {
pub magic: u32,
pub format: u8,
pub block: u64,
pub qual: bool,
pub compressed: bool,
pub paired: bool,
pub bits: BitSize,
pub headers: bool,
pub flags: bool,
pub reserved: [u8; 13],
}
impl Default for FileHeader {
fn default() -> Self {
Self::with_capacity(
BLOCK_SIZE,
false,
false,
false,
BitSize::default(),
false,
false,
)
}
}
impl FileHeader {
#[must_use]
pub fn new(
qual: bool,
compressed: bool,
paired: bool,
bitsize: BitSize,
headers: bool,
flags: bool,
) -> Self {
Self::with_capacity(
BLOCK_SIZE, qual, compressed, paired, bitsize, headers, flags,
)
}
#[must_use]
pub fn with_capacity(
block: u64,
qual: bool,
compressed: bool,
paired: bool,
bitsize: BitSize,
headers: bool,
flags: bool,
) -> Self {
Self {
magic: MAGIC,
format: FORMAT,
block,
qual,
compressed,
paired,
headers,
flags,
bits: bitsize,
reserved: RESERVED_BYTES,
}
}
pub fn set_bitsize(&mut self, bits: BitSize) {
self.bits = bits;
}
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 block = LittleEndian::read_u64(&buffer[5..13]);
let qual = buffer[13] != 0;
let compressed = buffer[14] != 0;
let paired = buffer[15] != 0;
let bits = match buffer[16] {
0 | 2 | 42 => BitSize::Two,
4 => BitSize::Four,
x => return Err(HeaderError::InvalidBitSize(x).into()),
};
let headers = match buffer[17] {
0 | 42 => false, _ => true,
};
let flags = buffer[18] != 0;
let Ok(reserved) = buffer[19..32].try_into() else {
return Err(HeaderError::InvalidReservedBytes.into());
};
Ok(Self {
magic,
format,
block,
qual,
compressed,
paired,
bits,
headers,
flags,
reserved,
})
}
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_u64(&mut buffer[5..13], self.block);
buffer[13] = self.qual.into();
buffer[14] = self.compressed.into();
buffer[15] = self.paired.into();
buffer[16] = self.bits.into();
buffer[17] = self.headers.into();
buffer[18] = self.flags.into();
buffer[19..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)
}
#[must_use]
pub fn is_paired(&self) -> bool {
self.paired
}
}
#[derive(Clone, Copy, Debug)]
pub struct BlockHeader {
pub magic: u64,
pub size: u64,
pub records: u32,
pub reserved: [u8; 12],
}
impl BlockHeader {
#[must_use]
pub fn new(size: u64, records: u32) -> Self {
Self {
magic: BLOCK_MAGIC,
size,
records,
reserved: RESERVED_BYTES_BLOCK,
}
}
#[must_use]
pub fn empty() -> Self {
Self {
magic: BLOCK_MAGIC,
size: 0,
records: 0,
reserved: RESERVED_BYTES_BLOCK,
}
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.size == 0 && self.records == 0
}
pub fn write_bytes<W: Write>(&self, writer: &mut W) -> Result<()> {
let mut buffer = [0u8; SIZE_BLOCK_HEADER];
LittleEndian::write_u64(&mut buffer[0..8], self.magic);
LittleEndian::write_u64(&mut buffer[8..16], self.size);
LittleEndian::write_u32(&mut buffer[16..20], self.records);
buffer[20..].copy_from_slice(&self.reserved);
writer.write_all(&buffer)?;
Ok(())
}
pub fn from_bytes(buffer: &[u8; SIZE_BLOCK_HEADER]) -> Result<Self> {
let magic = LittleEndian::read_u64(&buffer[0..8]);
if magic != BLOCK_MAGIC {
return Err(ReadError::InvalidBlockMagicNumber(magic, 0).into());
}
let size = LittleEndian::read_u64(&buffer[8..16]);
let records = LittleEndian::read_u32(&buffer[16..20]);
Ok(Self::new(size, records))
}
#[must_use]
pub fn size_with_header(&self) -> usize {
self.size as usize + SIZE_BLOCK_HEADER
}
}