use crate::hts_ffi::{
bam_destroy1, bam_init1,
htslib_obj::{Bam, KString},
sam_format1, BAM_FDUP, BAM_FMREVERSE, BAM_FMUNMAP, BAM_FPROPER_PAIR, BAM_FQCFAIL, BAM_FREAD1,
BAM_FREAD2, BAM_FREVERSE, BAM_FUNMAP,
};
use crate::{
alloc::{MemoryPoolObject, PooledObject},
HtsResult,
};
use std::{
borrow::Cow,
fmt::{Display, Formatter, Result},
ptr::NonNull,
};
use super::AlignmentFile;
impl MemoryPoolObject for Bam {
fn alloc() -> Option<NonNull<Self>> {
unsafe { NonNull::new(bam_init1()) }
}
fn dealloc(obj: NonNull<Self>) {
unsafe { bam_destroy1(obj.as_ptr()) }
}
}
pub struct Alignment<'a> {
inner: PooledObject<'a, Bam>,
file: &'a AlignmentFile,
}
impl<'a> Display for Alignment<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let mut buf = KString::new();
if unsafe { sam_format1(self.file.header(), self.inner() as _, &mut buf as _) } < 0 {
return Err(std::fmt::Error);
}
extern "C" {
fn free(s: *mut std::ffi::c_void);
}
let s = unsafe { std::ffi::CStr::from_ptr(buf.s) }
.to_str()
.unwrap_or("<Enocding Error>");
write!(f, "{}", s)?;
unsafe {
free(buf.s as _);
}
Ok(())
}
}
impl<'a> Alignment<'a> {
pub(super) fn from_hts_object<'b>(
inner: PooledObject<'b, Bam>,
file: &'b AlignmentFile,
) -> Alignment<'b> {
Alignment { inner, file }
}
pub(crate) fn inner(&self) -> &Bam {
&self.inner
}
#[allow(dead_code)]
pub(crate) fn inner_mut(&mut self) -> &mut Bam {
&mut self.inner
}
pub fn begin(&self) -> i64 {
self.inner().core.pos
}
pub fn mate_chrom_id(&self) -> usize {
self.inner().core.mtid as usize
}
pub fn insert_size(&self) -> i64 {
self.inner().core.isize_
}
pub fn mate_begin(&self) -> i64 {
self.inner().core.mpos
}
pub fn chrom_id(&self) -> usize {
self.inner().core.tid as usize
}
pub fn chrom_name(&self) -> Option<&str> {
self.file.get_chrom_name_by_id(self.chrom_id()).ok()
}
pub fn flags(&self) -> u16 {
self.inner().core.flag
}
pub fn query_name(&self) -> Cow<str> {
let name_cstr = unsafe { std::ffi::CStr::from_ptr(self.inner().data as _) };
name_cstr.to_string_lossy()
}
pub fn mapping_qual(&self) -> u8 {
self.inner().core.qual
}
pub fn query_len(&self) -> i32 {
self.inner().core.l_qseq
}
pub fn is_mapped(&self) -> bool {
(self.flags() & BAM_FUNMAP as u16) == 0
}
pub fn is_mate_mapped(&self) -> bool {
(self.flags() & BAM_FMUNMAP as u16) == 0
}
pub fn is_first_mate(&self) -> bool {
(self.flags() & BAM_FREAD1 as u16) != 0
}
pub fn is_second_mate(&self) -> bool {
(self.flags() & BAM_FREAD2 as u16) != 0
}
pub fn is_reverse_strand(&self) -> bool {
(self.flags() & BAM_FREVERSE as u16) != 0
}
pub fn is_mate_reverse_strand(&self) -> bool {
(self.flags() & BAM_FMREVERSE as u16) != 0
}
pub fn is_proper_pair(&self) -> bool {
(self.flags() & BAM_FPROPER_PAIR as u16) != 0
}
pub fn is_dup(&self) -> bool {
(self.flags() & BAM_FDUP as u16) != 0
}
pub fn is_failed_qc(&self) -> bool {
(self.flags() & BAM_FQCFAIL as u16) != 0
}
pub(crate) fn raw_seq(&self) -> (&[u8], bool) {
(
unsafe {
std::slice::from_raw_parts(
self.inner().data.offset(
self.inner().core.l_qname as isize + self.inner().core.n_cigar as isize * 4,
),
(self.inner().core.l_qseq + 1) as usize / 2,
)
},
self.inner().core.l_qseq % 2 == 0,
)
}
pub(crate) fn raw_per_base_qual(&self) -> &[u8] {
unsafe {
std::slice::from_raw_parts(
self.inner().data.offset(
self.inner().core.l_qname as isize
+ self.inner().core.n_cigar as isize * 4
+ (self.inner().core.l_qseq + 1) as isize / 2,
),
self.inner().core.l_qseq as usize,
)
}
}
pub(crate) fn raw_cigar_seq(&self) -> &[u32] {
unsafe {
std::slice::from_raw_parts(
self.inner().data.offset(self.inner().core.l_qname as isize) as *const u32,
self.inner().core.n_cigar as usize,
)
}
}
pub(crate) fn raw_aux(&self) -> &[u8] {
unsafe {
let begin = self.inner().data.offset(
self.inner().core.l_qname as isize
+ self.inner().core.n_cigar as isize * 4
+ (self.inner().core.l_qseq + 1) as isize / 2
+ self.inner().core.l_qseq as isize,
);
let end = self.inner().data.offset(self.inner().l_data as isize);
std::slice::from_raw_parts(begin, end.offset_from(begin) as usize)
}
}
}
pub struct AlignmentIter<'a, T: AlignmentReader + ?Sized>(&'a T);
impl<'a, T: AlignmentReader> Iterator for AlignmentIter<'a, T> {
type Item = Alignment<'a>;
fn next(&mut self) -> Option<Self::Item> {
(self.0).read_next().ok()?
}
}
pub trait AlignmentReader {
fn read_next(&self) -> HtsResult<Option<Alignment>>;
fn alignment_iter(&self) -> AlignmentIter<'_, Self> {
AlignmentIter(self)
}
}