use crate::AhdaVersion;
use crate::AhdaFormatVersion;
use crate::compression::BitmapType;
use crate::compression::MetadataCompression;
use std::io::Read;
use bincode::{Encode, Decode};
use bincode::encode_into_std_write;
use bincode::decode_from_slice;
use crate::compression::gzwrapper::deflate_bytes;
use crate::compression::gzwrapper::inflate_bytes;
type E = Box<dyn std::error::Error>;
use crate::errors::AhdaHeaderError;
#[derive(Clone, Debug, Decode, Encode, PartialEq)]
pub struct FileHeader {
pub ahda_header: [u8; 6],
pub file_format: u8,
pub metadata_compression: u8,
pub fields_present: u16,
pub n_targets: u32,
pub n_queries: u32,
pub bitmap_type: u16,
pub block_size: u32,
pub flags_len: u64,
}
impl FileHeader {
pub fn promises_query_names(
&self,
) -> bool {
(self.fields_present & crate::MASK_QUERIES) != 0
}
pub fn promises_query_ids(
&self,
) -> bool {
(self.fields_present & crate::MASK_QUERY_IDS) != 0
}
}
impl Default for FileHeader {
fn default() -> FileHeader {
FileHeader {
ahda_header: build_ahda_header().unwrap(),
file_format: AhdaFormatVersion::default().to_u8(),
metadata_compression: MetadataCompression::default().to_u8(),
fields_present: crate::MASK_QUERY_IDS | crate::MASK_QUERIES,
n_targets: 0_u32,
n_queries: 0_u32,
bitmap_type: BitmapType::default().to_u16(),
block_size: 0_u32,
flags_len: 0_u64,
}
}
}
#[non_exhaustive]
#[derive(Clone, Debug, Decode, Default, Encode, PartialEq)]
pub struct FileFlags {
pub query_name: Vec<u8>,
pub target_names: Vec<Vec<u8>>,
}
pub fn build_ahda_header() -> Result<[u8; 6], E> {
const VERSION: &str = env!("CARGO_PKG_VERSION");
let mut header = [97_u8, 104, 100, 97, 0, 0];
let version: u16 = VERSION.parse::<AhdaVersion>()?.to_u16();
let version_bytes: [u8; 2] = version.to_le_bytes();
header[4] = version_bytes[0];
header[5] = version_bytes[1];
Ok(header)
}
pub fn check_ahda_header(
bytes: [u8; 6],
) -> Result<AhdaVersion, E> {
let mut is_ahda = true;
is_ahda &= bytes[0] == 97;
is_ahda &= bytes[1] == 104;
is_ahda &= bytes[2] == 100;
is_ahda &= bytes[3] == 97;
let version_bytes: [u8; 2] = [bytes[4], bytes[5]];
let try_version = AhdaVersion::from_le_bytes(version_bytes);
if is_ahda {
try_version
} else {
Err(Box::new(AhdaHeaderError))
}
}
pub fn build_file_header_and_flags(
targets: &[Vec<u8>],
n_queries: usize,
query_name: &[u8],
flags_compression: &MetadataCompression,
) -> Result<(FileHeader, FileFlags), E> {
let n_targets = targets.len();
let bitmap_size = (n_targets as u64) * (n_queries as u64);
let bitmap_type = if bitmap_size < u32::MAX as u64 { BitmapType::Roaring32 } else { BitmapType::Roaring64 };
let block_size: u32 = match bitmap_type {
BitmapType::Roaring32 => {
let mut block_size = ((u32::MAX as u64) / (n_targets.max(1_usize) as u64)).min(65537_u64) as u32;
block_size = if block_size == 1 { 2 } else { block_size - 1 };
block_size
},
BitmapType::Roaring64 => {
262144_u32
},
};
let flags = FileFlags{ target_names: targets.to_vec(), query_name: query_name.to_vec() };
let flags_bytes = encode_file_flags(&flags, flags_compression)?;
let header = FileHeader{
ahda_header: build_ahda_header()?,
file_format: AhdaFormatVersion::V1_0_0.to_u8(),
metadata_compression: flags_compression.to_u8(),
fields_present: 0,
n_targets: n_targets as u32,
n_queries: n_queries as u32,
bitmap_type: bitmap_type.to_u16(),
block_size,
flags_len: flags_bytes.len() as u64,
};
Ok((header, flags))
}
pub fn encode_file_header_and_flags(
header: &mut FileHeader,
flags: &FileFlags,
) -> Result<Vec<u8>, E> {
let mut flags_bytes: Vec<u8> = encode_file_flags(flags, &MetadataCompression::from_u8(header.metadata_compression)?)?;
let flags_len: u64 = flags_bytes.len() as u64;
header.flags_len = flags_len;
let mut bytes: Vec<u8> = encode_file_header(header)?;
bytes.append(&mut flags_bytes);
Ok(bytes)
}
pub fn decode_file_header_and_flags(
bytes: &[u8],
) -> Result<(FileHeader, FileFlags), E> {
let header = decode_file_header(&bytes[0..32])?;
assert!(bytes.len() >= 32 + header.flags_len as usize);
let flags = decode_file_flags(&bytes[32..(32 + header.flags_len as usize)], &MetadataCompression::from_u8(header.metadata_compression)?)?;
Ok((header, flags))
}
pub fn encode_file_header(
header: &FileHeader,
) -> Result<Vec<u8>, E> {
let mut bytes: Vec<u8> = Vec::with_capacity(32);
let nbytes = encode_into_std_write(
header,
&mut bytes,
bincode::config::standard().with_fixed_int_encoding(),
)?;
assert_eq!(nbytes, 32);
Ok(bytes)
}
pub fn decode_file_header(
header_bytes: &[u8],
) -> Result<FileHeader, E> {
assert_eq!(header_bytes.len(), 32);
let mut bytes_start: [u8; 6] = [0; 6];
bytes_start[0] = header_bytes[0];
bytes_start[1] = header_bytes[1];
bytes_start[2] = header_bytes[2];
bytes_start[3] = header_bytes[3];
bytes_start[4] = header_bytes[4];
bytes_start[5] = header_bytes[5];
let _ = check_ahda_header(bytes_start)?;
Ok(decode_from_slice(header_bytes, bincode::config::standard().with_fixed_int_encoding())?.0)
}
pub fn read_file_header<R: Read>(
conn: &mut R,
) -> Result<FileHeader, E> {
let mut header_bytes: [u8; 32] = [0_u8; 32];
conn.read_exact(&mut header_bytes)?;
let res = decode_file_header(&header_bytes)?;
Ok(res)
}
pub fn read_file_flags<R: Read>(
header: &FileHeader,
conn: &mut R,
) -> Result<FileFlags, E> {
let mut flags_bytes: Vec<u8> = vec![0; header.flags_len as usize];
conn.read_exact(&mut flags_bytes)?;
let res = decode_file_flags(&flags_bytes, &MetadataCompression::from_u8(header.metadata_compression)?)?;
Ok(res)
}
pub fn read_file_header_and_flags<R: Read>(
conn: &mut R,
) -> Result<(FileHeader, FileFlags), E> {
let header = read_file_header(conn)?;
let flags = read_file_flags(&header, conn)?;
Ok((header, flags))
}
pub fn encode_file_flags(
flags: &FileFlags,
compression: &MetadataCompression
) -> Result<Vec<u8>, E> {
let mut bytes: Vec<u8> = Vec::new();
match compression {
MetadataCompression::BincodeStandard => {
let _ = encode_into_std_write(
flags,
&mut bytes,
bincode::config::standard(),
)?;
},
MetadataCompression::Flate2 => {
let _ = encode_into_std_write(
flags,
&mut bytes,
bincode::config::standard(),
)?;
bytes = deflate_bytes(&bytes)?;
},
}
Ok(bytes)
}
pub fn decode_file_flags(
bytes: &[u8],
compression: &MetadataCompression
) -> Result<FileFlags, E> {
let flags = match compression {
MetadataCompression::BincodeStandard => {
decode_from_slice(
bytes,
bincode::config::standard(),
)?.0
},
MetadataCompression::Flate2 => {
let inflated = inflate_bytes(bytes)?;
decode_from_slice(
&inflated,
bincode::config::standard(),
)?.0
},
};
Ok(flags)
}
#[cfg(test)]
mod tests {
#[test]
fn build_file_header_and_flags() {
use crate::AhdaFormatVersion;
use crate::compression::MetadataCompression;
use crate::compression::BitmapType;
use super::build_ahda_header;
use super::build_file_header_and_flags;
use super::encode_file_flags;
use super::FileHeader;
use super::FileFlags;
let targets = vec!["a".as_bytes().to_vec(), "b".as_bytes().to_vec(), "c".as_bytes().to_vec()];
let queries = vec!["1".as_bytes().to_vec(), "2".as_bytes().to_vec(), "3".as_bytes().to_vec(), "4".as_bytes().to_vec(), "5".as_bytes().to_vec()];
let sample = "sample".as_bytes().to_vec();
let expected_flags = FileFlags { query_name: sample.clone(), target_names: targets.clone() };
let compression = MetadataCompression::BincodeStandard;
let nbytes = encode_file_flags(&expected_flags, &compression).unwrap().len();
let expected_header = FileHeader {
ahda_header: build_ahda_header().unwrap(),
file_format: AhdaFormatVersion::V1_0_0.to_u8(),
metadata_compression: compression.to_u8(),
fields_present: 0,
n_targets: targets.len() as u32,
n_queries: queries.len() as u32,
bitmap_type: BitmapType::Roaring32.to_u16(),
block_size: ((u32::MAX as u64) / (targets.len() as u64)).min(65536_u64) as u32,
flags_len: nbytes as u64,
};
let (got_header, got_flags) = build_file_header_and_flags(&targets, queries.len(), &sample, &compression).unwrap();
assert_eq!(got_header, expected_header);
assert_eq!(got_flags, expected_flags);
}
#[test]
fn encode_file_header_and_flags() {
use crate::AhdaFormatVersion;
use crate::compression::MetadataCompression;
use crate::compression::BitmapType;
use super::build_ahda_header;
use super::encode_file_flags;
use super::encode_file_header_and_flags;
use super::FileHeader;
use super::FileFlags;
let targets = vec!["a".as_bytes().to_vec(), "b".as_bytes().to_vec(), "c".as_bytes().to_vec()];
let queries = vec!["1".as_bytes().to_vec(), "2".as_bytes().to_vec(), "3".as_bytes().to_vec(), "4".as_bytes().to_vec(), "5".as_bytes().to_vec()];
let sample = "sample";
let flags = FileFlags { query_name: sample.as_bytes().to_vec(), target_names: targets.clone() };
let compression = MetadataCompression::BincodeStandard;
let nbytes = encode_file_flags(&flags, &compression).unwrap().len();
let mut header = FileHeader {
ahda_header: build_ahda_header().unwrap(),
file_format: AhdaFormatVersion::V1_0_0.to_u8(),
metadata_compression: compression.to_u8(),
fields_present: 0,
n_targets: targets.len() as u32,
n_queries: queries.len() as u32,
bitmap_type: BitmapType::Roaring32.to_u16(),
block_size: ((u32::MAX as u64) / (targets.len() as u64)).min(65537_u64) as u32,
flags_len: nbytes as u64,
};
let expected: Vec<u8> = vec![97, 104, 100, 97, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 5, 0, 0, 0, 0, 0, 1, 0, 1, 0, 14, 0, 0, 0, 0, 0, 0, 0, 6, 115, 97, 109, 112, 108, 101, 3, 1, 97, 1, 98, 1, 99];
let got = encode_file_header_and_flags(&mut header, &flags).unwrap();
assert_eq!(got, expected);
}
#[test]
fn encode_file_header() {
use crate::AhdaFormatVersion;
use crate::compression::MetadataCompression;
use crate::compression::BitmapType;
use super::build_ahda_header;
use super::encode_file_flags;
use super::encode_file_header;
use super::FileHeader;
use super::FileFlags;
let targets = vec!["a".as_bytes().to_vec(), "b".as_bytes().to_vec(), "c".as_bytes().to_vec()];
let queries = vec!["1".as_bytes().to_vec(), "2".as_bytes().to_vec(), "3".as_bytes().to_vec(), "4".as_bytes().to_vec(), "5".as_bytes().to_vec()];
let sample = "sample";
let flags = FileFlags { query_name: sample.as_bytes().to_vec(), target_names: targets.clone() };
let compression = MetadataCompression::BincodeStandard;
let nbytes = encode_file_flags(&flags, &compression).unwrap().len();
let header = FileHeader {
ahda_header: build_ahda_header().unwrap(),
file_format: AhdaFormatVersion::V1_0_0.to_u8(),
metadata_compression: compression.to_u8(),
fields_present: 0,
n_targets: targets.len() as u32,
n_queries: queries.len() as u32,
bitmap_type: BitmapType::Roaring32.to_u16(),
block_size: ((u32::MAX as u64) / (targets.len() as u64)).min(65537_u64) as u32,
flags_len: nbytes as u64,
};
let expected: Vec<u8> = vec![97, 104, 100, 97, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 5, 0, 0, 0, 0, 0, 1, 0, 1, 0, 14, 0, 0, 0, 0, 0, 0, 0];
let got = encode_file_header(&header).unwrap();
assert_eq!(got, expected);
}
#[test]
fn encode_file_flags_default() {
use crate::compression::MetadataCompression;
use super::encode_file_flags;
use super::FileFlags;
let targets = vec!["a".as_bytes().to_vec(), "b".as_bytes().to_vec(), "c".as_bytes().to_vec()];
let sample = "sample";
let flags = FileFlags { query_name: sample.as_bytes().to_vec(), target_names: targets.clone() };
let expected: Vec<u8> = vec![6, 115, 97, 109, 112, 108, 101, 3, 1, 97, 1, 98, 1, 99];
let compression = MetadataCompression::BincodeStandard;
let got = encode_file_flags(&flags, &compression).unwrap();
assert_eq!(got, expected);
}
#[test]
fn encode_file_flags_flate2() {
use crate::compression::MetadataCompression;
use super::encode_file_flags;
use super::FileFlags;
let targets = vec!["a".as_bytes().to_vec(), "b".as_bytes().to_vec(), "c".as_bytes().to_vec()];
let sample = "sample";
let flags = FileFlags { query_name: sample.as_bytes().to_vec(), target_names: targets.clone() };
let expected: Vec<u8> = vec![31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 99, 43, 78, 204, 45, 200, 73, 101, 102, 76, 100, 76, 98, 76, 6, 0, 217, 110, 76, 178, 14, 0, 0, 0];
let got = encode_file_flags(&flags, &MetadataCompression::Flate2).unwrap();
assert_eq!(got, expected);
}
#[test]
fn decode_file_header() {
use crate::AhdaFormatVersion;
use crate::compression::MetadataCompression;
use crate::compression::BitmapType;
use super::build_ahda_header;
use super::decode_file_header;
use super::FileHeader;
let data: Vec<u8> = vec![97, 104, 100, 97, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 5, 0, 0, 0, 0, 0, 1, 0, 1, 0, 14, 0, 0, 0, 0, 0, 0, 0];
let compression = MetadataCompression::BincodeStandard;
let expected = FileHeader {
ahda_header: build_ahda_header().unwrap(),
file_format: AhdaFormatVersion::V1_0_0.to_u8(),
metadata_compression: compression.to_u8(),
fields_present: 0,
n_targets: 3_u32,
n_queries: 5_u32,
bitmap_type: BitmapType::Roaring32.to_u16(),
block_size: ((u32::MAX as u64) / (7_u64)).min(65537_u64) as u32,
flags_len: 14_u64,
};
let got = decode_file_header(&data).unwrap();
assert_eq!(got, expected);
}
#[test]
fn decode_file_flags_default() {
use crate::compression::MetadataCompression;
use super::decode_file_flags;
use super::FileFlags;
let targets = vec!["a".as_bytes().to_vec(), "b".as_bytes().to_vec(), "c".as_bytes().to_vec()];
let sample = "sample";
let data: Vec<u8> = vec![6, 115, 97, 109, 112, 108, 101, 3, 1, 97, 1, 98, 1, 99];
let expected = FileFlags { query_name: sample.as_bytes().to_vec(), target_names: targets.clone() };
let compression = MetadataCompression::BincodeStandard;
let got = decode_file_flags(&data, &compression).unwrap();
assert_eq!(got, expected);
}
#[test]
fn decode_file_flags_flate2() {
use crate::compression::MetadataCompression;
use super::decode_file_flags;
use super::FileFlags;
let targets = vec!["a".as_bytes().to_vec(), "b".as_bytes().to_vec(), "c".as_bytes().to_vec()];
let sample = "sample";
let data: Vec<u8> = vec![31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 99, 43, 78, 204, 45, 200, 73, 101, 102, 76, 100, 76, 98, 76, 6, 0, 217, 110, 76, 178, 14, 0, 0, 0];
let expected = FileFlags { query_name: sample.as_bytes().to_vec(), target_names: targets.clone() };
let got = decode_file_flags(&data, &MetadataCompression::Flate2).unwrap();
assert_eq!(got, expected);
}
#[test]
fn read_file_header() {
use crate::AhdaFormatVersion;
use crate::compression::MetadataCompression;
use crate::compression::BitmapType;
use super::build_ahda_header;
use super::read_file_header;
use super::FileHeader;
use std::io::Cursor;
let data_bytes: Vec<u8> = vec![97, 104, 100, 97, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 5, 0, 0, 0, 0, 0, 1, 0, 1, 0, 16, 0, 0, 0, 0, 0, 0, 0];
let mut data: Cursor<Vec<u8>> = Cursor::new(data_bytes);
let compression = MetadataCompression::BincodeStandard;
let expected = FileHeader {
ahda_header: build_ahda_header().unwrap(),
file_format: AhdaFormatVersion::V1_0_0.to_u8(),
metadata_compression: compression.to_u8(),
fields_present: 0,
n_targets: 3_u32,
n_queries: 5_u32,
bitmap_type: BitmapType::Roaring32.to_u16(),
block_size: ((u32::MAX as u64) / (5_u64)).min(65537_u64) as u32,
flags_len: 16_u64,
};
let got = read_file_header(&mut data).unwrap();
assert_eq!(got, expected);
}
#[test]
fn read_file_flags() {
use crate::AhdaFormatVersion;
use crate::compression::MetadataCompression;
use crate::compression::BitmapType;
use super::build_ahda_header;
use super::read_file_flags;
use super::FileHeader;
use super::FileFlags;
use std::io::Cursor;
let targets = vec!["a".as_bytes().to_vec(), "b".as_bytes().to_vec(), "c".as_bytes().to_vec()];
let sample = "sample";
let compression = MetadataCompression::BincodeStandard;
let header = FileHeader {
ahda_header: build_ahda_header().unwrap(),
file_format: AhdaFormatVersion::V1_0_0.to_u8(),
metadata_compression: compression.to_u8(),
fields_present: 0,
n_targets: 3_u32,
n_queries: 5_u32,
bitmap_type: BitmapType::Roaring32.to_u16(),
block_size: ((u32::MAX as u64) / (5_u64)).min(65537_u64) as u32,
flags_len: 14_u64,
};
let data_bytes: Vec<u8> = vec![6, 115, 97, 109, 112, 108, 101, 3, 1, 97, 1, 98, 1, 99];
let mut data: Cursor<Vec<u8>> = Cursor::new(data_bytes);
let expected = FileFlags { query_name: sample.as_bytes().to_vec(), target_names: targets.clone() };
let got = read_file_flags(&header, &mut data).unwrap();
assert_eq!(got, expected);
}
#[test]
fn read_file_header_and_flags() {
use crate::AhdaFormatVersion;
use crate::compression::MetadataCompression;
use crate::compression::BitmapType;
use super::build_ahda_header;
use super::read_file_header_and_flags;
use super::FileHeader;
use super::FileFlags;
use std::io::Cursor;
let targets = vec!["a".as_bytes().to_vec(), "b".as_bytes().to_vec(), "c".as_bytes().to_vec()];
let queries = vec!["1".as_bytes().to_vec(), "2".as_bytes().to_vec(), "3".as_bytes().to_vec(), "4".as_bytes().to_vec(), "5".as_bytes().to_vec()];
let sample = "sample";
let compression = MetadataCompression::BincodeStandard;
let expected_flags = FileFlags { query_name: sample.as_bytes().to_vec(), target_names: targets.clone() };
let expected_header = FileHeader {
ahda_header: build_ahda_header().unwrap(),
file_format: AhdaFormatVersion::V1_0_0.to_u8(),
metadata_compression: compression.to_u8(),
fields_present: 0,
n_targets: targets.len() as u32,
n_queries: queries.len() as u32,
bitmap_type: BitmapType::Roaring32.to_u16(),
block_size: ((u32::MAX as u64) / (targets.len() as u64)).min(65537_u64) as u32,
flags_len: 14_u64,
};
let data_bytes: Vec<u8> = vec![97, 104, 100, 97, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 5, 0, 0, 0, 0, 0, 1, 0, 1, 0, 14, 0, 0, 0, 0, 0, 0, 0, 6, 115, 97, 109, 112, 108, 101, 3, 1, 97, 1, 98, 1, 99];
let mut data: Cursor<Vec<u8>> = Cursor::new(data_bytes);
let (got_header, got_flags) = read_file_header_and_flags(&mut data).unwrap();
assert_eq!(got_header, expected_header);
assert_eq!(got_flags, expected_flags);
}
}