Skip to content

Commit ebd7d99

Browse files
authored
fix: map unmapped reads (tid=-1) to correct index in slow_idxstats (#511)
When iterating CRAM records, unmapped reads have tid == -1. Casting this to usize wraps to usize::MAX, causing an index-out-of-bounds panic. Map tid == -1 to the designated unmapped slot at index nref.
1 parent 0168666 commit ebd7d99

4 files changed

Lines changed: 23 additions & 6 deletions

File tree

src/bam/mod.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -846,8 +846,11 @@ impl IndexedReader {
846846
return Err(Error::InvalidTid { tid });
847847
}
848848

849+
// Map unmapped reads (tid == -1) to the last slot (nref) to avoid usize wrapping.
850+
let count_idx = if tid == -1 { nref } else { tid as usize };
851+
849852
if tid != last_tid {
850-
if (last_tid >= -1) && (counts[tid as usize][0] + counts[tid as usize][1]) > 0 {
853+
if (last_tid >= -1) && (counts[count_idx][0] + counts[count_idx][1]) > 0 {
851854
return Err(Error::BamUnsorted);
852855
}
853856
last_tid = tid;
@@ -858,7 +861,7 @@ impl IndexedReader {
858861
} else {
859862
0
860863
};
861-
counts[(*b).core.tid as usize][idx] += 1;
864+
counts[count_idx][idx] += 1;
862865
}
863866

864867
if ret == -1 {
@@ -1029,8 +1032,8 @@ impl Read for IndexedReader {
10291032
impl Drop for IndexedReader {
10301033
fn drop(&mut self) {
10311034
unsafe {
1032-
if self.itr.is_some() {
1033-
htslib::hts_itr_destroy(self.itr.unwrap());
1035+
if let Some(itr) = self.itr {
1036+
htslib::hts_itr_destroy(itr);
10341037
}
10351038
htslib::hts_close(self.htsfile);
10361039
}
@@ -3166,6 +3169,20 @@ CCCCCCCCCCCCCCCCCCC"[..],
31663169
assert_eq!(expected, actual);
31673170
}
31683171

3172+
#[test]
3173+
fn test_slow_idxstats_cram_unmapped() {
3174+
let mut reader = IndexedReader::from_path("test/test_cram_unmapped.cram").unwrap();
3175+
reader.set_reference("test/test_cram.fa").unwrap();
3176+
let expected = vec![
3177+
(0, 120, 2, 0),
3178+
(1, 120, 2, 0),
3179+
(2, 120, 2, 0),
3180+
(-1, 0, 0, 2),
3181+
];
3182+
let actual = reader.index_stats().unwrap();
3183+
assert_eq!(expected, actual);
3184+
}
3185+
31693186
// #[test]
31703187
// fn test_number_mapped_and_unmapped_cram() {
31713188
// let mut reader = IndexedReader::from_path("test/test_cram.cram").unwrap();

src/tbx/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,8 +316,8 @@ impl Read for Reader {
316316
impl Drop for Reader {
317317
fn drop(&mut self) {
318318
unsafe {
319-
if self.itr.is_some() {
320-
htslib::hts_itr_destroy(self.itr.unwrap());
319+
if let Some(itr) = self.itr {
320+
htslib::hts_itr_destroy(itr);
321321
}
322322
htslib::tbx_destroy(self.tbx);
323323
htslib::hts_close(self.hts_file);

test/test_cram_unmapped.cram

1.66 KB
Binary file not shown.

test/test_cram_unmapped.cram.crai

75 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)