Skip to content

Commit cd60c9a

Browse files
authored
refactor: Drop MBox for the storage behing TableCollection. (#539)
* add sys::TableCollection
1 parent ea72c28 commit cd60c9a

File tree

6 files changed

+158
-88
lines changed

6 files changed

+158
-88
lines changed

src/sys/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use thiserror::Error;
66
pub mod bindings;
77

88
pub mod flags;
9+
mod table_collection;
910
mod tables;
1011
mod tree;
1112
mod treeseq;
@@ -16,6 +17,7 @@ mod treeseq;
1617
/// "Null" identifier value.
1718
pub(crate) const TSK_NULL: bindings::tsk_id_t = -1;
1819

20+
pub use table_collection::*;
1921
pub use tables::*;
2022
pub use tree::LLTree;
2123
pub use treeseq::LLTreeSeq;

src/sys/table_collection.rs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
use super::bindings::tsk_edge_table_t;
2+
use super::bindings::tsk_individual_table_t;
3+
use super::bindings::tsk_migration_table_t;
4+
use super::bindings::tsk_mutation_table_t;
5+
use super::bindings::tsk_node_table_t;
6+
use super::bindings::tsk_population_table_t;
7+
#[cfg(feature = "provenance")]
8+
use super::bindings::tsk_provenance_table_t;
9+
use super::bindings::tsk_site_table_t;
10+
use super::bindings::tsk_table_collection_free;
11+
use super::bindings::tsk_table_collection_init;
12+
use super::bindings::tsk_table_collection_t;
13+
use super::tskbox::TskBox;
14+
15+
pub struct TableCollection(TskBox<tsk_table_collection_t>);
16+
17+
impl TableCollection {
18+
pub fn new(sequence_length: f64) -> Self {
19+
let mut tsk = TskBox::new(
20+
|tc: *mut tsk_table_collection_t| unsafe { tsk_table_collection_init(tc, 0) },
21+
tsk_table_collection_free,
22+
);
23+
tsk.as_mut().sequence_length = sequence_length;
24+
Self(tsk)
25+
}
26+
27+
// # Safety
28+
//
29+
// The returned value is uninitialized.
30+
// Using the object prior to initilization is likely to trigger UB.
31+
pub unsafe fn new_uninit() -> Self {
32+
let tsk = unsafe { TskBox::new_uninit(tsk_table_collection_free) };
33+
Self(tsk)
34+
}
35+
36+
pub fn copy(&self) -> (i32, TableCollection) {
37+
// SAFETY: the C API requires that the destiniation of a copy be uninitalized.
38+
// Copying into it will initialize the object.
39+
let mut dest = unsafe { TskBox::new_uninit(tsk_table_collection_free) };
40+
// SAFETY: self.as_ptr() is not null and dest matches the input
41+
// expectations of the C API.
42+
let rv = unsafe {
43+
super::bindings::tsk_table_collection_copy(self.as_ptr(), dest.as_mut_ptr(), 0)
44+
};
45+
(rv, Self(dest))
46+
}
47+
48+
pub fn sequence_length(&self) -> f64 {
49+
self.0.as_ref().sequence_length
50+
}
51+
52+
pub fn as_ptr(&self) -> *const tsk_table_collection_t {
53+
self.0.as_ptr()
54+
}
55+
56+
pub fn as_mut_ptr(&mut self) -> *mut tsk_table_collection_t {
57+
self.0.as_mut_ptr()
58+
}
59+
60+
pub fn individuals_mut(&mut self) -> &mut tsk_individual_table_t {
61+
// SAFETY: self pointer is not null
62+
unsafe { &mut (*self.as_mut_ptr()).individuals }
63+
}
64+
65+
pub fn nodes_mut(&mut self) -> &mut tsk_node_table_t {
66+
// SAFETY: self pointer is not null
67+
unsafe { &mut (*self.as_mut_ptr()).nodes }
68+
}
69+
70+
pub fn edges_mut(&mut self) -> &mut tsk_edge_table_t {
71+
// SAFETY: self pointer is not null
72+
unsafe { &mut (*self.as_mut_ptr()).edges }
73+
}
74+
75+
pub fn migrations_mut(&mut self) -> &mut tsk_migration_table_t {
76+
// SAFETY: self pointer is not null
77+
unsafe { &mut (*self.as_mut_ptr()).migrations }
78+
}
79+
80+
pub fn mutations_mut(&mut self) -> &mut tsk_mutation_table_t {
81+
// SAFETY: self pointer is not null
82+
unsafe { &mut (*self.as_mut_ptr()).mutations }
83+
}
84+
85+
pub fn populations_mut(&mut self) -> &mut tsk_population_table_t {
86+
// SAFETY: self pointer is not null
87+
unsafe { &mut (*self.as_mut_ptr()).populations }
88+
}
89+
90+
#[cfg(feature = "provenance")]
91+
pub fn provenances_mut(&mut self) -> &mut tsk_provenance_table_t {
92+
// SAFETY: self pointer is not null
93+
unsafe { &mut (*self.as_mut_ptr()).provenances }
94+
}
95+
96+
pub fn sites_mut(&mut self) -> &mut tsk_site_table_t {
97+
// SAFETY: self pointer is not null
98+
unsafe { &mut (*self.as_mut_ptr()).sites }
99+
}
100+
}

src/sys/tskbox.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,18 @@ impl<T> TskBox<T> {
2020
init: F,
2121
teardown: unsafe extern "C" fn(*mut T) -> i32,
2222
) -> Self {
23+
// SAFETY: we will initialize it next
24+
let mut uninit = unsafe { Self::new_uninit(teardown) };
25+
let _ = init(uninit.as_mut());
26+
uninit
27+
}
28+
29+
// # Safety
30+
//
31+
// The returned value is uninitialized.
32+
// Using the object prior to initilization is likely to trigger UB.
33+
pub unsafe fn new_uninit(teardown: unsafe extern "C" fn(*mut T) -> i32) -> Self {
2334
let x = unsafe { libc::malloc(std::mem::size_of::<T>()) as *mut T };
24-
let _ = init(x);
2535
let tsk = NonNull::new(x).unwrap();
2636
Self {
2737
tsk,

src/table_collection.rs

Lines changed: 29 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::vec;
33

44
use crate::error::TskitError;
55
use crate::sys::bindings as ll_bindings;
6+
use crate::sys::TableCollection as LLTableCollection;
67
use crate::types::Bookmark;
78
use crate::IndividualTableSortOptions;
89
use crate::Position;
@@ -17,8 +18,6 @@ use crate::TskReturnValue;
1718
use crate::{EdgeId, NodeId};
1819
use ll_bindings::tsk_id_t;
1920
use ll_bindings::tsk_size_t;
20-
use ll_bindings::tsk_table_collection_free;
21-
use mbox::MBox;
2221

2322
/// A table collection.
2423
///
@@ -54,31 +53,11 @@ use mbox::MBox;
5453
/// ```
5554
///
5655
pub struct TableCollection {
57-
inner: MBox<ll_bindings::tsk_table_collection_t>,
56+
inner: LLTableCollection,
5857
idmap: Vec<NodeId>,
5958
views: crate::table_views::TableViews,
6059
}
6160

62-
impl Drop for TableCollection {
63-
fn drop(&mut self) {
64-
let rv = unsafe { tsk_table_collection_free(self.as_mut_ptr()) };
65-
assert_eq!(rv, 0);
66-
}
67-
}
68-
69-
/// Returns a pointer to an uninitialized tsk_table_collection_t
70-
pub(crate) fn uninit_table_collection() -> MBox<ll_bindings::tsk_table_collection_t> {
71-
let temp = unsafe {
72-
libc::malloc(std::mem::size_of::<ll_bindings::tsk_table_collection_t>())
73-
as *mut ll_bindings::tsk_table_collection_t
74-
};
75-
let nonnull = match std::ptr::NonNull::<ll_bindings::tsk_table_collection_t>::new(temp) {
76-
Some(x) => x,
77-
None => panic!("out of memory"),
78-
};
79-
unsafe { MBox::from_non_null_raw(nonnull) }
80-
}
81-
8261
impl TableCollection {
8362
/// Create a new table collection with a sequence length.
8463
///
@@ -101,26 +80,13 @@ impl TableCollection {
10180
expected: "sequence_length >= 0.0".to_string(),
10281
});
10382
}
104-
let mut mbox = uninit_table_collection();
105-
let rv = unsafe { ll_bindings::tsk_table_collection_init(&mut *mbox, 0) };
106-
if rv < 0 {
107-
return Err(crate::error::TskitError::ErrorCode { code: rv });
108-
}
109-
let views = crate::table_views::TableViews::new_from_mbox_table_collection(&mut mbox)?;
110-
// AHA?
111-
assert!(std::ptr::eq(
112-
&mbox.as_ref().edges as *const ll_bindings::tsk_edge_table_t,
113-
views.edges().as_ref() as *const ll_bindings::tsk_edge_table_t
114-
));
115-
let mut tables = Self {
116-
inner: mbox,
83+
let mut inner = LLTableCollection::new(sequence_length.into());
84+
let views = crate::table_views::TableViews::new_from_ll_table_collection(&mut inner)?;
85+
Ok(Self {
86+
inner,
11787
idmap: vec![],
11888
views,
119-
};
120-
unsafe {
121-
(*tables.as_mut_ptr()).sequence_length = f64::from(sequence_length);
122-
}
123-
Ok(tables)
89+
})
12490
}
12591

12692
/// # Safety
@@ -130,31 +96,23 @@ impl TableCollection {
13096
/// Or, it may be initialized and about to be used in a part of the C API
13197
/// requiring an uninitialized table collection.
13298
/// Consult the C API docs before using!
133-
pub(crate) unsafe fn new_from_mbox(
134-
mbox: MBox<ll_bindings::tsk_table_collection_t>,
135-
) -> Result<Self, TskitError> {
136-
let mut mbox = mbox;
137-
let views = crate::table_views::TableViews::new_from_mbox_table_collection(&mut mbox)?;
99+
pub(crate) unsafe fn new_from_ll(lltables: LLTableCollection) -> Result<Self, TskitError> {
100+
let mut inner = lltables;
101+
let views = crate::table_views::TableViews::new_from_ll_table_collection(&mut inner)?;
138102
Ok(Self {
139-
inner: mbox,
103+
inner,
140104
idmap: vec![],
141105
views,
142106
})
143107
}
144108

145109
pub(crate) fn into_raw(self) -> Result<*mut ll_bindings::tsk_table_collection_t, TskitError> {
146110
let mut tables = self;
147-
// rust won't let use move inner out b/c this type implements Drop.
148-
// So we have to replace the existing pointer with a new one.
149-
let table_ptr = unsafe {
150-
libc::malloc(std::mem::size_of::<ll_bindings::tsk_table_collection_t>())
151-
as *mut ll_bindings::tsk_table_collection_t
152-
};
153-
let rv = unsafe { ll_bindings::tsk_table_collection_init(table_ptr, 0) };
154-
155-
let mut temp = unsafe { MBox::from_raw(table_ptr) };
111+
let mut temp = crate::sys::TableCollection::new(1.);
156112
std::mem::swap(&mut temp, &mut tables.inner);
157-
handle_tsk_return_value!(rv, MBox::into_raw(temp))
113+
let ptr = temp.as_mut_ptr();
114+
std::mem::forget(temp);
115+
handle_tsk_return_value!(0, ptr)
158116
}
159117

160118
/// Load a table collection from a file.
@@ -214,7 +172,7 @@ impl TableCollection {
214172
/// assert_eq!(tables.sequence_length(), 100.0);
215173
/// ```
216174
pub fn sequence_length(&self) -> Position {
217-
unsafe { (*self.as_ptr()).sequence_length }.into()
175+
self.inner.sequence_length().into()
218176
}
219177

220178
edge_table_add_row!(
@@ -789,15 +747,12 @@ impl TableCollection {
789747

790748
/// Return a "deep" copy of the tables.
791749
pub fn deepcopy(&self) -> Result<TableCollection, TskitError> {
792-
// The output is UNINITIALIZED tables,
793-
// else we leak memory
794-
let mut inner = uninit_table_collection();
795-
796-
let rv = unsafe { ll_bindings::tsk_table_collection_copy(self.as_ptr(), &mut *inner, 0) };
750+
let (rv, inner) = self.inner.copy();
797751

798752
// SAFETY: we just initialized it.
799753
// The C API doesn't free NULL pointers.
800-
handle_tsk_return_value!(rv, unsafe { Self::new_from_mbox(inner)? })
754+
let tables = unsafe { TableCollection::new_from_ll(inner) }?;
755+
handle_tsk_return_value!(rv, tables)
801756
}
802757

803758
/// Return a [`crate::TreeSequence`] based on the tables.
@@ -984,7 +939,7 @@ impl TableCollection {
984939
// to create with null pointers.
985940
let rv = unsafe {
986941
ll_bindings::tsk_edge_table_set_columns(
987-
&mut self.inner.edges,
942+
self.inner.edges_mut(),
988943
(*edges.as_ptr()).num_rows,
989944
(*edges.as_ptr()).left,
990945
(*edges.as_ptr()).right,
@@ -1021,7 +976,7 @@ impl TableCollection {
1021976
// to create with null pointers.
1022977
let rv = unsafe {
1023978
ll_bindings::tsk_node_table_set_columns(
1024-
&mut self.inner.nodes,
979+
self.inner.nodes_mut(),
1025980
(*nodes.as_ptr()).num_rows,
1026981
(*nodes.as_ptr()).flags,
1027982
(*nodes.as_ptr()).time,
@@ -1058,7 +1013,7 @@ impl TableCollection {
10581013
// to create with null pointers.
10591014
let rv = unsafe {
10601015
ll_bindings::tsk_site_table_set_columns(
1061-
&mut self.inner.sites,
1016+
self.inner.sites_mut(),
10621017
(*sites.as_ptr()).num_rows,
10631018
(*sites.as_ptr()).position,
10641019
(*sites.as_ptr()).ancestral_state,
@@ -1094,7 +1049,7 @@ impl TableCollection {
10941049
// to create with null pointers.
10951050
let rv = unsafe {
10961051
ll_bindings::tsk_mutation_table_set_columns(
1097-
&mut self.inner.mutations,
1052+
self.inner.mutations_mut(),
10981053
(*mutations.as_ptr()).num_rows,
10991054
(*mutations.as_ptr()).site,
11001055
(*mutations.as_ptr()).node,
@@ -1137,7 +1092,7 @@ impl TableCollection {
11371092
// to create with null pointers.
11381093
let rv = unsafe {
11391094
ll_bindings::tsk_individual_table_set_columns(
1140-
&mut self.inner.individuals,
1095+
self.inner.individuals_mut(),
11411096
(*individuals.as_ptr()).num_rows,
11421097
(*individuals.as_ptr()).flags,
11431098
(*individuals.as_ptr()).location,
@@ -1175,7 +1130,7 @@ impl TableCollection {
11751130
// to create with null pointers.
11761131
let rv = unsafe {
11771132
ll_bindings::tsk_migration_table_set_columns(
1178-
&mut self.inner.migrations,
1133+
self.inner.migrations_mut(),
11791134
(*migrations.as_ptr()).num_rows,
11801135
(*migrations.as_ptr()).left,
11811136
(*migrations.as_ptr()).right,
@@ -1216,7 +1171,7 @@ impl TableCollection {
12161171
// to create with null pointers.
12171172
let rv = unsafe {
12181173
ll_bindings::tsk_population_table_set_columns(
1219-
&mut self.inner.populations,
1174+
self.inner.populations_mut(),
12201175
(*populations.as_ptr()).num_rows,
12211176
(*populations.as_ptr()).metadata,
12221177
(*populations.as_ptr()).metadata_offset,
@@ -1257,7 +1212,7 @@ impl TableCollection {
12571212
// to create with null pointers.
12581213
let rv = unsafe {
12591214
ll_bindings::tsk_provenance_table_set_columns(
1260-
&mut self.inner.provenances,
1215+
self.inner.provenances_mut(),
12611216
(*provenances.as_ptr()).num_rows,
12621217
(*provenances.as_ptr()).timestamp,
12631218
(*provenances.as_ptr()).timestamp_offset,
@@ -1279,11 +1234,11 @@ impl TableCollection {
12791234

12801235
/// Pointer to the low-level C type.
12811236
pub fn as_ptr(&self) -> *const ll_bindings::tsk_table_collection_t {
1282-
&*self.inner
1237+
self.inner.as_ptr()
12831238
}
12841239

12851240
/// Mutable pointer to the low-level C type.
12861241
pub fn as_mut_ptr(&mut self) -> *mut ll_bindings::tsk_table_collection_t {
1287-
&mut *self.inner
1242+
self.inner.as_mut_ptr()
12881243
}
12891244
}

0 commit comments

Comments
 (0)