use crate::error::Result;
pub fn scan_bgzf_blocks<P: AsRef<std::path::Path>>(path: P) -> Result<Vec<u64>> {
use std::fs::File;
use std::io::{BufReader, Read};
let file = File::open(path)?;
let file_size = file.metadata()?.len();
let mut reader = BufReader::with_capacity(1024 * 1024, file);
const GZIP_MAGIC: [u8; 2] = [0x1f, 0x8b];
const GZIP_METHOD_DEFLATE: u8 = 8;
const GZIP_FLAG_FEXTRA: u8 = 0x04;
let mut blocks = Vec::new();
let mut pos: u64 = 0;
while pos < file_size {
let mut header = [0u8; 10];
match reader.read_exact(&mut header) {
Ok(_) => {
if header[0] == GZIP_MAGIC[0]
&& header[1] == GZIP_MAGIC[1]
&& header[2] == GZIP_METHOD_DEFLATE
&& (header[3] & GZIP_FLAG_FEXTRA) != 0
{
blocks.push(pos);
let mut xlen_buf = [0u8; 2];
if reader.read_exact(&mut xlen_buf).is_ok() {
let xlen = u16::from_le_bytes(xlen_buf) as usize;
let mut extra = vec![0u8; xlen];
if reader.read_exact(&mut extra).is_ok() {
if let Ok(bsize) = parse_bgzf_bsize(&extra) {
let remaining = (bsize + 1) as i64 - (12 + xlen as i64);
if remaining > 0 {
let mut skip_buf = vec![0u8; remaining as usize];
if reader.read_exact(&mut skip_buf).is_ok() {
pos += (bsize + 1) as u64;
continue;
}
}
}
}
}
pos += 1;
} else {
pos += 1;
}
}
Err(_) => break, }
}
Ok(blocks)
}
fn parse_bgzf_bsize(extra: &[u8]) -> Result<usize> {
let mut pos = 0;
while pos + 4 <= extra.len() {
let si1 = extra[pos];
let si2 = extra[pos + 1];
let slen = u16::from_le_bytes([extra[pos + 2], extra[pos + 3]]) as usize;
if si1 == 66 && si2 == 67 {
if pos + 4 + slen > extra.len() || slen != 2 {
return Err(crate::error::Error::InvalidInput(
"Invalid BGZF extra field".to_string()
));
}
let bsize = u16::from_le_bytes([extra[pos + 4], extra[pos + 5]]) as usize;
return Ok(bsize);
}
pos += 4 + slen;
}
Err(crate::error::Error::InvalidInput(
"BGZF subfield not found".to_string()
))
}