Skip to content

Commit 0699c44

Browse files
committed
fix!: make the inner header pointer accessible again, although only through unsafe
1 parent d743a34 commit 0699c44

2 files changed

Lines changed: 72 additions & 11 deletions

File tree

src/bcf/header.rs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,15 @@ impl Header {
8888
}
8989
}
9090

91+
/// Get a pointer to the raw header.
92+
///
93+
/// # Safety
94+
/// The caller must ensure that the pointer is not used after this `Header`
95+
/// is dropped
96+
pub unsafe fn inner_ptr(&self) -> *mut htslib::bcf_hdr_t {
97+
self.inner
98+
}
99+
91100
/// Create a new `Header` using the given `HeaderView` as the template.
92101
///
93102
/// After construction, you can modify the header independently from the template `header`.
@@ -276,10 +285,23 @@ unsafe impl Send for HeaderView {}
276285
unsafe impl Sync for HeaderView {}
277286

278287
impl HeaderView {
279-
pub(crate) fn new(inner: *mut htslib::bcf_hdr_t) -> Self {
288+
/// Create a view from a raw pointer to a header.
289+
///
290+
/// # Safety
291+
/// The caller must ensure that the header is initialized.
292+
pub unsafe fn from_ptr(inner: *mut htslib::bcf_hdr_t) -> Self {
280293
HeaderView { inner }
281294
}
282295

296+
/// Get a pointer to the underlying raw header.
297+
///
298+
/// # Safety
299+
/// The caller must ensure that the pointer is not used after this
300+
/// `HeaderView` is dropped
301+
pub unsafe fn as_ptr(&self) -> *mut htslib::bcf_hdr_t {
302+
self.inner
303+
}
304+
283305
#[inline]
284306
fn inner(&self) -> htslib::bcf_hdr_t {
285307
unsafe { *self.inner }
@@ -559,7 +581,9 @@ pub enum TagLength {
559581

560582
#[cfg(test)]
561583
mod tests {
584+
use super::*;
562585
use crate::bcf::Reader;
586+
use crate::htslib;
563587

564588
#[test]
565589
fn test_header_view_empty_record() {
@@ -576,4 +600,39 @@ mod tests {
576600
assert_eq!(record.pos(), 0); // No position set
577601
assert_eq!(record.qual(), 0.0); // No quality score set
578602
}
603+
604+
#[test]
605+
fn test_header_add_sample_via_raw_pointer() {
606+
let sample_name = b"test-sample";
607+
608+
let header = Header::new();
609+
let sample = std::ffi::CString::new(sample_name).unwrap();
610+
611+
let view = unsafe {
612+
let ptr = header.inner_ptr();
613+
// to avoid double free, as we will wrap this later in a HeaderView
614+
std::mem::forget(header);
615+
htslib::bcf_hdr_add_sample(ptr, sample.as_ptr());
616+
htslib::bcf_hdr_sync(ptr);
617+
// When the HeaderView is dropped, the bcf_hdr is freed
618+
HeaderView::from_ptr(ptr)
619+
};
620+
621+
assert_eq!(view.samples(), vec![sample_name]);
622+
}
623+
624+
#[test]
625+
fn test_header_view_version_via_raw_pointer() {
626+
let vcf = Reader::from_path("test/test_string.vcf").expect("Error opening file");
627+
let hv = vcf.header.clone();
628+
629+
let version = unsafe {
630+
// the header view will outlive this pointer
631+
let ptr = hv.as_ptr();
632+
let version_charptr = htslib::bcf_hdr_get_version(ptr);
633+
std::ffi::CStr::from_ptr(version_charptr).to_str().unwrap()
634+
};
635+
636+
assert_eq!(version, "VCFv4.1");
637+
}
579638
}

src/bcf/mod.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ impl Reader {
203203
let header = unsafe { htslib::bcf_hdr_read(htsfile) };
204204
Ok(Reader {
205205
inner: htsfile,
206-
header: Arc::new(HeaderView::new(header)),
206+
header: Arc::new(unsafe { HeaderView::from_ptr(header) }),
207207
})
208208
}
209209
}
@@ -299,9 +299,11 @@ impl IndexedReader {
299299
} // 0: BCF_SR_REQUIRE_IDX
300300
// Attach a file with the path from the arguments.
301301
if unsafe { htslib::bcf_sr_add_reader(ser_reader, path.as_ptr()) } >= 0 {
302-
let header = Arc::new(HeaderView::new(unsafe {
303-
htslib::bcf_hdr_dup((*(*ser_reader).readers.offset(0)).header)
304-
}));
302+
let header = Arc::new(unsafe {
303+
HeaderView::from_ptr(htslib::bcf_hdr_dup(
304+
(*(*ser_reader).readers.offset(0)).header,
305+
))
306+
});
305307
Ok(IndexedReader {
306308
inner: ser_reader,
307309
header,
@@ -501,9 +503,11 @@ pub mod synced {
501503
}
502504

503505
let i = (self.reader_count() - 1) as isize;
504-
let header = Arc::new(HeaderView::new(unsafe {
505-
crate::htslib::bcf_hdr_dup((*(*self.inner).readers.offset(i)).header)
506-
}));
506+
let header = Arc::new(unsafe {
507+
HeaderView::from_ptr(crate::htslib::bcf_hdr_dup(
508+
(*(*self.inner).readers.offset(i)).header,
509+
))
510+
});
507511
self.headers.push(header);
508512
Ok(())
509513
}
@@ -708,9 +712,7 @@ impl Writer {
708712
unsafe { htslib::bcf_hdr_write(htsfile, header.inner) };
709713
Ok(Writer {
710714
inner: htsfile,
711-
header: Arc::new(HeaderView::new(unsafe {
712-
htslib::bcf_hdr_dup(header.inner)
713-
})),
715+
header: Arc::new(unsafe { HeaderView::from_ptr(htslib::bcf_hdr_dup(header.inner)) }),
714716
subset: header.subset.clone(),
715717
})
716718
}

0 commit comments

Comments
 (0)