use std::fmt;
pub const ROSALIND_INDEX_MAGIC: [u8; 8] = *b"ROSALIND";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IndexVersion {
V1 = 1,
}
impl IndexVersion {
pub fn from_u16(v: u16) -> Option<Self> {
match v {
1 => Some(IndexVersion::V1),
_ => None,
}
}
}
#[derive(Clone, Copy)]
pub struct IndexHeader {
pub magic: [u8; 8],
pub version: u16,
pub endian: u8,
pub reserved0: u8,
pub flags: u32,
pub contig_count: u32,
pub sa_sample_rate: u32,
pub header_bytes: u64,
pub section_table_offset: u64,
pub section_table_bytes: u64,
pub reference_blake3: [u8; 32],
}
impl fmt::Debug for IndexHeader {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("IndexHeader")
.field("magic", &self.magic)
.field("version", &self.version)
.field("endian", &self.endian)
.field("flags", &self.flags)
.field("contig_count", &self.contig_count)
.field("sa_sample_rate", &self.sa_sample_rate)
.field("header_bytes", &self.header_bytes)
.field("section_table_offset", &self.section_table_offset)
.field("section_table_bytes", &self.section_table_bytes)
.field("reference_blake3", &hex32(&self.reference_blake3))
.finish()
}
}
impl IndexHeader {
pub const ENDIAN_LITTLE: u8 = 1;
pub const FIXED_SIZE: usize = 8 + 2 + 1 + 1 + 4 + 4 + 4 + 8 + 8 + 8 + 32;
pub fn new_v1(contig_count: u32, sa_sample_rate: u32, reference_blake3: [u8; 32]) -> Self {
Self {
magic: ROSALIND_INDEX_MAGIC,
version: IndexVersion::V1 as u16,
endian: Self::ENDIAN_LITTLE,
reserved0: 0,
flags: 0,
contig_count,
sa_sample_rate: sa_sample_rate.max(1),
header_bytes: Self::FIXED_SIZE as u64,
section_table_offset: 0,
section_table_bytes: 0,
reference_blake3,
}
}
}
fn hex32(bytes: &[u8; 32]) -> String {
const HEX: &[u8; 16] = b"0123456789abcdef";
let mut out = vec![0u8; 64];
for (i, b) in bytes.iter().enumerate() {
out[2 * i] = HEX[(b >> 4) as usize];
out[2 * i + 1] = HEX[(b & 0x0f) as usize];
}
String::from_utf8_lossy(&out).into_owned()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum SectionKind {
Contigs = 1,
Reference = 2,
SaSamples = 3,
Reference2bit = 4,
FmMeta = 5,
Boundaries = 6,
Blocks = 7,
}
impl SectionKind {
pub fn from_u32(v: u32) -> Option<Self> {
match v {
1 => Some(SectionKind::Contigs),
2 => Some(SectionKind::Reference),
3 => Some(SectionKind::SaSamples),
4 => Some(SectionKind::Reference2bit),
5 => Some(SectionKind::FmMeta),
6 => Some(SectionKind::Boundaries),
7 => Some(SectionKind::Blocks),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SectionEntry {
pub kind: SectionKind,
pub offset: u64,
pub bytes: u64,
}
impl SectionEntry {
pub const FIXED_SIZE: usize = 4 + 8 + 8;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn section_kind_discriminants_round_trip() {
for (raw, kind) in [
(1u32, SectionKind::Contigs),
(2, SectionKind::Reference),
(3, SectionKind::SaSamples),
(4, SectionKind::Reference2bit),
(5, SectionKind::FmMeta),
(6, SectionKind::Boundaries),
(7, SectionKind::Blocks),
] {
assert_eq!(SectionKind::from_u32(raw), Some(kind));
assert_eq!(kind as u32, raw);
}
assert_eq!(SectionKind::from_u32(8), None);
assert_eq!(SectionKind::from_u32(0), None);
}
}