diff --git a/src/bcf/header.rs b/src/bcf/header.rs index e1f29381c..c5121600d 100644 --- a/src/bcf/header.rs +++ b/src/bcf/header.rs @@ -88,6 +88,15 @@ impl Header { } } + /// Get a pointer to the raw header. + /// + /// # Safety + /// The caller must ensure that the pointer is not used after this `Header` + /// is dropped + pub unsafe fn inner_ptr(&self) -> *mut htslib::bcf_hdr_t { + self.inner + } + /// Create a new `Header` using the given `HeaderView` as the template. /// /// After construction, you can modify the header independently from the template `header`. @@ -276,10 +285,23 @@ unsafe impl Send for HeaderView {} unsafe impl Sync for HeaderView {} impl HeaderView { - pub(crate) fn new(inner: *mut htslib::bcf_hdr_t) -> Self { + /// Create a view from a raw pointer to a header. + /// + /// # Safety + /// The caller must ensure that the header is initialized. + pub unsafe fn from_ptr(inner: *mut htslib::bcf_hdr_t) -> Self { HeaderView { inner } } + /// Get a pointer to the underlying raw header. + /// + /// # Safety + /// The caller must ensure that the pointer is not used after this + /// `HeaderView` is dropped + pub unsafe fn as_ptr(&self) -> *mut htslib::bcf_hdr_t { + self.inner + } + #[inline] fn inner(&self) -> htslib::bcf_hdr_t { unsafe { *self.inner } @@ -559,7 +581,9 @@ pub enum TagLength { #[cfg(test)] mod tests { + use super::*; use crate::bcf::Reader; + use crate::htslib; #[test] fn test_header_view_empty_record() { @@ -576,4 +600,39 @@ mod tests { assert_eq!(record.pos(), 0); // No position set assert_eq!(record.qual(), 0.0); // No quality score set } + + #[test] + fn test_header_add_sample_via_raw_pointer() { + let sample_name = b"test-sample"; + + let header = Header::new(); + let sample = std::ffi::CString::new(sample_name).unwrap(); + + let view = unsafe { + let ptr = header.inner_ptr(); + // to avoid double free, as we will wrap this later in a HeaderView + std::mem::forget(header); + htslib::bcf_hdr_add_sample(ptr, sample.as_ptr()); + htslib::bcf_hdr_sync(ptr); + // When the HeaderView is dropped, the bcf_hdr is freed + HeaderView::from_ptr(ptr) + }; + + assert_eq!(view.samples(), vec![sample_name]); + } + + #[test] + fn test_header_view_version_via_raw_pointer() { + let vcf = Reader::from_path("test/test_string.vcf").expect("Error opening file"); + let hv = vcf.header.clone(); + + let version = unsafe { + // the header view will outlive this pointer + let ptr = hv.as_ptr(); + let version_charptr = htslib::bcf_hdr_get_version(ptr); + std::ffi::CStr::from_ptr(version_charptr).to_str().unwrap() + }; + + assert_eq!(version, "VCFv4.1"); + } } diff --git a/src/bcf/mod.rs b/src/bcf/mod.rs index c21a2771b..0a13a2ae6 100644 --- a/src/bcf/mod.rs +++ b/src/bcf/mod.rs @@ -203,7 +203,7 @@ impl Reader { let header = unsafe { htslib::bcf_hdr_read(htsfile) }; Ok(Reader { inner: htsfile, - header: Arc::new(HeaderView::new(header)), + header: Arc::new(unsafe { HeaderView::from_ptr(header) }), }) } } @@ -299,9 +299,11 @@ impl IndexedReader { } // 0: BCF_SR_REQUIRE_IDX // Attach a file with the path from the arguments. if unsafe { htslib::bcf_sr_add_reader(ser_reader, path.as_ptr()) } >= 0 { - let header = Arc::new(HeaderView::new(unsafe { - htslib::bcf_hdr_dup((*(*ser_reader).readers.offset(0)).header) - })); + let header = Arc::new(unsafe { + HeaderView::from_ptr(htslib::bcf_hdr_dup( + (*(*ser_reader).readers.offset(0)).header, + )) + }); Ok(IndexedReader { inner: ser_reader, header, @@ -501,9 +503,11 @@ pub mod synced { } let i = (self.reader_count() - 1) as isize; - let header = Arc::new(HeaderView::new(unsafe { - crate::htslib::bcf_hdr_dup((*(*self.inner).readers.offset(i)).header) - })); + let header = Arc::new(unsafe { + HeaderView::from_ptr(crate::htslib::bcf_hdr_dup( + (*(*self.inner).readers.offset(i)).header, + )) + }); self.headers.push(header); Ok(()) } @@ -708,9 +712,7 @@ impl Writer { unsafe { htslib::bcf_hdr_write(htsfile, header.inner) }; Ok(Writer { inner: htsfile, - header: Arc::new(HeaderView::new(unsafe { - htslib::bcf_hdr_dup(header.inner) - })), + header: Arc::new(unsafe { HeaderView::from_ptr(htslib::bcf_hdr_dup(header.inner)) }), subset: header.subset.clone(), }) }