Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 60 additions & 1 deletion src/bcf/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down Expand Up @@ -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 }
Expand Down Expand Up @@ -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() {
Expand All @@ -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");
}
}
22 changes: 12 additions & 10 deletions src/bcf/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) }),
})
}
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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(())
}
Expand Down Expand Up @@ -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(),
})
}
Expand Down
Loading