diff --git a/src/_macros.rs b/src/_macros.rs index 8c7a321f3..a6df895a3 100644 --- a/src/_macros.rs +++ b/src/_macros.rs @@ -380,56 +380,6 @@ macro_rules! handle_metadata_return { }; } -macro_rules! build_owned_tables { - ($name: ty, $deref: ident, $lltype: ty, $tsktable: ty) => { - impl $name { - fn new() -> Self { - let table = <$lltype>::new(); - Self { table } - } - - /// Clear the table. - pub fn clear(&mut self) -> $crate::TskReturnValue { - self.table.clear().map_err(|e| e.into()) - } - } - - impl Default for $name { - fn default() -> Self { - Self::new() - } - } - - impl std::ops::Deref for $name { - type Target = $deref; - - fn deref(&self) -> &Self::Target { - // SAFETY: that T* and &T have same layout, - // and Target is repr(transparent). - unsafe { std::mem::transmute(&self.table) } - } - } - - impl std::ops::DerefMut for $name { - fn deref_mut(&mut self) -> &mut Self::Target { - // SAFETY: that T* and &T have same layout, - // and Target is repr(transparent). - unsafe { std::mem::transmute(&mut self.table) } - } - } - - impl $name { - pub fn as_ptr(&self) -> *const $tsktable { - self.table.as_ptr() - } - - pub fn as_mut_ptr(&mut self) -> *mut $tsktable { - self.table.as_mut_ptr() - } - } - }; -} - macro_rules! node_table_add_row_details { ($flags: ident, $time: ident, @@ -929,25 +879,6 @@ macro_rules! provenance_table_add_row { }; } -macro_rules! build_owned_table_type { - ($(#[$attr:meta])* => $name: ident, - $deref_type: ident, - $lltype: ty, - $tsktable: ty) => { - $(#[$attr])* - pub struct $name { - table: $lltype - } - - build_owned_tables!( - $name, - $deref_type, - $lltype, - $tsktable - ); - }; -} - macro_rules! raw_metadata_getter_for_tables { ($idtype: ty) => { fn raw_metadata>(&self, row: I) -> Option<&[u8]> { diff --git a/src/edge_table.rs b/src/edge_table.rs index acc3bd2a2..18b9c3efb 100644 --- a/src/edge_table.rs +++ b/src/edge_table.rs @@ -137,22 +137,74 @@ impl<'a> streaming_iterator::StreamingIterator for EdgeTableRowView<'a> { } } -/// An immutable view of an edge table. +/// An edge table. /// -/// These are not created directly but are accessed -/// by types implementing [`std::ops::Deref`] to -/// [`crate::table_views::TableViews`] +/// # Examples +/// +/// ## Standalone tables +/// +/// ``` +/// use tskit::EdgeTable; +/// +/// let mut edges = EdgeTable::default(); +/// let rowid = edges.add_row(1., 2., 0, 1).unwrap(); +/// assert_eq!(rowid, 0); +/// assert_eq!(edges.num_rows(), 1); +/// +/// edges.clear().unwrap(); +/// assert_eq!(edges.num_rows(), 0); +/// ``` +/// +/// An example with metadata. +/// This requires the cargo feature `"derive"` for `tskit`. +/// +/// ``` +/// # #[cfg(any(feature="doc", feature="derive"))] { +/// use tskit::EdgeTable; +/// +/// #[derive(serde::Serialize, +/// serde::Deserialize, +/// tskit::metadata::EdgeMetadata)] +/// #[serializer("serde_json")] +/// struct EdgeMetadata { +/// value: i32, +/// } +/// +/// let metadata = EdgeMetadata{value: 42}; +/// +/// let mut edges = EdgeTable::default(); +/// +/// let rowid = edges.add_row_with_metadata(0., 1., 5, 10, &metadata).unwrap(); +/// assert_eq!(rowid, 0); +/// +/// match edges.metadata::(rowid) { +/// // rowid is in range, decoding succeeded +/// Some(Ok(decoded)) => assert_eq!(decoded.value, 42), +/// // rowid is in range, decoding failed +/// Some(Err(e)) => panic!("error decoding metadata: {:?}", e), +/// None => panic!("row id out of range") +/// } +/// # } +/// ``` #[repr(transparent)] #[derive(Debug)] pub struct EdgeTable { - table_: sys::LLEdgeTableRef, + table_: sys::LLEdgeTable, } impl EdgeTable { + pub(crate) fn as_ptr(&self) -> *const ll_bindings::tsk_edge_table_t { + self.table_.as_ptr() + } + + pub(crate) fn as_mut_ptr(&mut self) -> *mut ll_bindings::tsk_edge_table_t { + self.table_.as_mut_ptr() + } + pub(crate) fn new_from_table( edges: *mut ll_bindings::tsk_edge_table_t, ) -> Result { - let table_ = sys::LLEdgeTableRef::new_from_table(edges)?; + let table_ = sys::LLEdgeTable::new_non_owning(edges)?; Ok(EdgeTable { table_ }) } @@ -160,6 +212,11 @@ impl EdgeTable { self.table_.as_ref() } + pub fn new() -> Result { + let table_ = sys::LLEdgeTable::new_owning(0)?; + Ok(Self { table_ }) + } + /// Return the number of rows pub fn num_rows(&self) -> crate::SizeType { self.as_ref().num_rows.into() @@ -283,6 +340,13 @@ impl EdgeTable { Some(view) } + pub fn clear(&mut self) -> Result<(), TskitError> { + self.table_.clear().map_err(|e| e.into()) + } + + edge_table_add_row!(=> add_row, self, self.as_mut_ptr()); + edge_table_add_row_with_metadata!(=> add_row_with_metadata, self, self.as_mut_ptr()); + build_table_column_slice_getter!( /// Get the left column as a slice => left, left_slice, Position); @@ -309,61 +373,10 @@ impl EdgeTable { => child, child_slice_raw, ll_bindings::tsk_id_t); } -build_owned_table_type!( - /// A standalone edge table that owns its data. - /// - /// # Examples - /// - /// ``` - /// use tskit::OwningEdgeTable; - /// - /// let mut edges = OwningEdgeTable::default(); - /// let rowid = edges.add_row(1., 2., 0, 1).unwrap(); - /// assert_eq!(rowid, 0); - /// assert_eq!(edges.num_rows(), 1); - /// - /// edges.clear().unwrap(); - /// assert_eq!(edges.num_rows(), 0); - /// ``` - /// - /// An example with metadata. - /// This requires the cargo feature `"derive"` for `tskit`. - /// - /// ``` - /// # #[cfg(any(feature="doc", feature="derive"))] { - /// use tskit::OwningEdgeTable; - /// - /// #[derive(serde::Serialize, - /// serde::Deserialize, - /// tskit::metadata::EdgeMetadata)] - /// #[serializer("serde_json")] - /// struct EdgeMetadata { - /// value: i32, - /// } - /// - /// let metadata = EdgeMetadata{value: 42}; - /// - /// let mut edges = OwningEdgeTable::default(); - /// - /// let rowid = edges.add_row_with_metadata(0., 1., 5, 10, &metadata).unwrap(); - /// assert_eq!(rowid, 0); - /// - /// match edges.metadata::(rowid) { - /// // rowid is in range, decoding succeeded - /// Some(Ok(decoded)) => assert_eq!(decoded.value, 42), - /// // rowid is in range, decoding failed - /// Some(Err(e)) => panic!("error decoding metadata: {:?}", e), - /// None => panic!("row id out of range") - /// } - /// # } - /// ``` - => OwningEdgeTable, - EdgeTable, - crate::sys::LLOwningEdgeTable, - crate::bindings::tsk_edge_table_t -); - -impl OwningEdgeTable { - edge_table_add_row!(=> add_row, self, self.as_mut_ptr()); - edge_table_add_row_with_metadata!(=> add_row_with_metadata, self, self.as_mut_ptr()); +impl Default for EdgeTable { + fn default() -> Self { + Self::new().unwrap() + } } + +pub type OwningEdgeTable = EdgeTable; diff --git a/src/error.rs b/src/error.rs index 8b23a4432..fa75edebb 100644 --- a/src/error.rs +++ b/src/error.rs @@ -40,8 +40,11 @@ pub enum TskitError { impl From for TskitError { fn from(error: sys::Error) -> Self { match error { - sys::Error::Message(msg) => TskitError::LibraryError(msg), sys::Error::Code(code) => TskitError::ErrorCode { code }, + sys::Error::Message(msg) => TskitError::LibraryError(msg), + sys::Error::NullPointer => { + TskitError::LibraryError("null pointer encountered".to_owned()) + } } } } diff --git a/src/individual_table.rs b/src/individual_table.rs index 269e3d73d..08fd98797 100644 --- a/src/individual_table.rs +++ b/src/individual_table.rs @@ -95,15 +95,58 @@ impl<'a> streaming_iterator::StreamingIterator for IndividualTableRowView<'a> { } } -/// An immutable view of a individual table. +/// An individual table. /// -/// These are not created directly but are accessed -/// by types implementing [`std::ops::Deref`] to -/// [`crate::table_views::TableViews`] +/// # Examples +/// +/// ## Standalone tables +/// +/// ``` +/// use tskit::IndividualTable; +/// +/// let mut individuals = IndividualTable::default(); +/// let rowid = individuals.add_row(0, None, None).unwrap(); +/// assert_eq!(rowid, 0); +/// assert_eq!(individuals.num_rows(), 1); +/// ``` +/// +/// An example with metadata. +/// This requires the cargo feature `"derive"` for `tskit`. +/// +/// +/// ``` +/// # #[cfg(any(feature="doc", feature="derive"))] { +/// use tskit::IndividualTable; +/// +/// #[derive(serde::Serialize, +/// serde::Deserialize, +/// tskit::metadata::IndividualMetadata)] +/// #[serializer("serde_json")] +/// struct IndividualMetadata { +/// value: i32, +/// } +/// +/// let metadata = IndividualMetadata{value: 42}; +/// +/// let mut individuals = IndividualTable::default(); +/// +/// let rowid = individuals.add_row_with_metadata(0, None, None, &metadata).unwrap(); +/// assert_eq!(rowid, 0); +/// +/// match individuals.metadata::(rowid) { +/// // rowid is in range, decoding succeeded +/// Some(Ok(decoded)) => assert_eq!(decoded.value, 42), +/// // rowid is in range, decoding failed +/// Some(Err(e)) => panic!("error decoding metadata: {:?}", e), +/// None => panic!("row id out of range") +/// } +/// +/// # } +/// ``` #[derive(Debug)] #[repr(transparent)] pub struct IndividualTable { - table_: sys::LLIndividualTableRef, + table_: sys::LLIndividualTable, } fn make_individual_table_row(table: &IndividualTable, pos: tsk_id_t) -> Option { @@ -141,10 +184,23 @@ impl Iterator for IndividualTableIterator { } impl IndividualTable { + pub fn new() -> Result { + let table_ = sys::LLIndividualTable::new_owning(0)?; + Ok(Self { table_ }) + } + + pub(crate) fn as_ptr(&self) -> *const ll_bindings::tsk_individual_table_t { + self.table_.as_ptr() + } + + pub(crate) fn as_mut_ptr(&mut self) -> *mut ll_bindings::tsk_individual_table_t { + self.table_.as_mut_ptr() + } + pub(crate) fn new_from_table( individuals: *mut ll_bindings::tsk_individual_table_t, ) -> Result { - let table_ = sys::LLIndividualTableRef::new_from_table(individuals)?; + let table_ = sys::LLIndividualTable::new_non_owning(individuals)?; Ok(IndividualTable { table_ }) } @@ -431,6 +487,13 @@ match tables.individuals().metadata::(0.into()) Some(view) } + pub fn clear(&mut self) -> Result<(), TskitError> { + self.table_.clear().map_err(|e| e.into()) + } + + individual_table_add_row!(=> add_row, self, self.as_mut_ptr()); + individual_table_add_row_with_metadata!(=> add_row_with_metadata, self, self.as_mut_ptr()); + build_table_column_slice_getter!( /// Get the flags column as a slice => flags, flags_slice, IndividualFlags); @@ -439,60 +502,10 @@ match tables.individuals().metadata::(0.into()) => flags, flags_slice_raw, ll_bindings::tsk_flags_t); } -build_owned_table_type!( - /// A standalone individual table that owns its data. - /// - /// # Examples - /// - /// ``` - /// use tskit::OwningIndividualTable; - /// - /// let mut individuals = OwningIndividualTable::default(); - /// let rowid = individuals.add_row(0, None, None).unwrap(); - /// assert_eq!(rowid, 0); - /// assert_eq!(individuals.num_rows(), 1); - /// ``` - /// - /// An example with metadata. - /// This requires the cargo feature `"derive"` for `tskit`. - /// - /// - /// ``` - /// # #[cfg(any(feature="doc", feature="derive"))] { - /// use tskit::OwningIndividualTable; - /// - /// #[derive(serde::Serialize, - /// serde::Deserialize, - /// tskit::metadata::IndividualMetadata)] - /// #[serializer("serde_json")] - /// struct IndividualMetadata { - /// value: i32, - /// } - /// - /// let metadata = IndividualMetadata{value: 42}; - /// - /// let mut individuals = OwningIndividualTable::default(); - /// - /// let rowid = individuals.add_row_with_metadata(0, None, None, &metadata).unwrap(); - /// assert_eq!(rowid, 0); - /// - /// match individuals.metadata::(rowid) { - /// // rowid is in range, decoding succeeded - /// Some(Ok(decoded)) => assert_eq!(decoded.value, 42), - /// // rowid is in range, decoding failed - /// Some(Err(e)) => panic!("error decoding metadata: {:?}", e), - /// None => panic!("row id out of range") - /// } - /// - /// # } - /// ``` - => OwningIndividualTable, - IndividualTable, - crate::sys::LLOwningIndividualTable, - crate::bindings::tsk_individual_table_t -); - -impl OwningIndividualTable { - individual_table_add_row!(=> add_row, self, self.as_mut_ptr()); - individual_table_add_row_with_metadata!(=> add_row_with_metadata, self, self.as_mut_ptr()); +impl Default for IndividualTable { + fn default() -> Self { + Self::new().unwrap() + } } + +pub type OwningIndividualTable = IndividualTable; diff --git a/src/migration_table.rs b/src/migration_table.rs index 90afd3cbf..e50d146dc 100644 --- a/src/migration_table.rs +++ b/src/migration_table.rs @@ -157,22 +157,72 @@ impl<'a> streaming_iterator::StreamingIterator for MigrationTableRowView<'a> { } } -/// An immutable view of a migration table. +/// An migration table. /// -/// These are not created directly but are accessed -/// by types implementing [`std::ops::Deref`] to -/// [`crate::table_views::TableViews`] +/// # Examples +/// +/// ## Standalone table +/// +/// ``` +/// use tskit::MigrationTable; +/// +/// let mut migrations = MigrationTable::default(); +/// let rowid = migrations.add_row((0., 1.), 1, (0, 1), 10.3).unwrap(); +/// assert_eq!(rowid, 0); +/// assert_eq!(migrations.num_rows(), 1); +/// ``` +/// +/// An example with metadata. +/// This requires the cargo feature `"derive"` for `tskit`. +/// +/// ``` +/// # #[cfg(any(feature="doc", feature="derive"))] { +/// use tskit::MigrationTable; +/// +/// #[derive(serde::Serialize, +/// serde::Deserialize, +/// tskit::metadata::MigrationMetadata)] +/// #[serializer("serde_json")] +/// struct MigrationMetadata { +/// value: i32, +/// } +/// +/// let metadata = MigrationMetadata{value: 42}; +/// +/// let mut migrations = MigrationTable::default(); +/// +/// let rowid = migrations.add_row_with_metadata((0., 1.), 1, (0, 1), 10.3, &metadata).unwrap(); +/// assert_eq!(rowid, 0); +/// +/// match migrations.metadata::(rowid) { +/// // rowid is in range, decoding succeeded +/// Some(Ok(decoded)) => assert_eq!(decoded.value, 42), +/// // rowid is in range, decoding failed +/// Some(Err(e)) => panic!("error decoding metadata: {:?}", e), +/// None => panic!("row id out of range") +/// } +/// +/// # } +/// ``` #[derive(Debug)] #[repr(transparent)] pub struct MigrationTable { - table_: sys::LLMigrationTableRef, + table_: sys::LLMigrationTable, } impl MigrationTable { + pub(crate) fn as_ptr(&self) -> *const ll_bindings::tsk_migration_table_t { + self.table_.as_ptr() + } + + pub(crate) fn as_mut_ptr(&mut self) -> *mut ll_bindings::tsk_migration_table_t { + self.table_.as_mut_ptr() + } + pub(crate) fn new_from_table( migrations: *mut ll_bindings::tsk_migration_table_t, ) -> Result { - let table_ = sys::LLMigrationTableRef::new_from_table(migrations)?; + let table_ = sys::LLMigrationTable::new_non_owning(migrations)?; Ok(MigrationTable { table_ }) } @@ -180,6 +230,11 @@ impl MigrationTable { self.table_.as_ref() } + pub fn new() -> Result { + let table_ = sys::LLMigrationTable::new_owning(0)?; + Ok(Self { table_ }) + } + /// Return the number of rows pub fn num_rows(&self) -> SizeType { self.as_ref().num_rows.into() @@ -333,6 +388,13 @@ impl MigrationTable { Some(view) } + pub fn clear(&mut self) -> Result<(), TskitError> { + self.table_.clear().map_err(|e| e.into()) + } + + migration_table_add_row!(=> add_row, self, self.as_mut_ptr()); + migration_table_add_row_with_metadata!(=> add_row_with_metadata, self, self.as_mut_ptr()); + build_table_column_slice_getter!( /// Get the left column as a slice => left, left_slice, Position); @@ -371,59 +433,12 @@ impl MigrationTable { => dest, dest_slice_raw, ll_bindings::tsk_id_t); } -build_owned_table_type!( - /// A standalone migration table that owns its data. - /// - /// # Examples - /// - /// ``` - /// use tskit::OwningMigrationTable; - /// - /// let mut migrations = OwningMigrationTable::default(); - /// let rowid = migrations.add_row((0., 1.), 1, (0, 1), 10.3).unwrap(); - /// assert_eq!(rowid, 0); - /// assert_eq!(migrations.num_rows(), 1); - /// ``` - /// - /// An example with metadata. - /// This requires the cargo feature `"derive"` for `tskit`. - /// - /// ``` - /// # #[cfg(any(feature="doc", feature="derive"))] { - /// use tskit::OwningMigrationTable; - /// - /// #[derive(serde::Serialize, - /// serde::Deserialize, - /// tskit::metadata::MigrationMetadata)] - /// #[serializer("serde_json")] - /// struct MigrationMetadata { - /// value: i32, - /// } - /// - /// let metadata = MigrationMetadata{value: 42}; - /// - /// let mut migrations = OwningMigrationTable::default(); - /// - /// let rowid = migrations.add_row_with_metadata((0., 1.), 1, (0, 1), 10.3, &metadata).unwrap(); - /// assert_eq!(rowid, 0); - /// - /// match migrations.metadata::(rowid) { - /// // rowid is in range, decoding succeeded - /// Some(Ok(decoded)) => assert_eq!(decoded.value, 42), - /// // rowid is in range, decoding failed - /// Some(Err(e)) => panic!("error decoding metadata: {:?}", e), - /// None => panic!("row id out of range") - /// } - /// - /// # } - /// ``` - => OwningMigrationTable, - MigrationTable, - crate::sys::LLOwningMigrationTable, - crate::bindings::tsk_migration_table_t -); - -impl OwningMigrationTable { - migration_table_add_row!(=> add_row, self, self.as_mut_ptr()); - migration_table_add_row_with_metadata!(=> add_row_with_metadata, self, self.as_mut_ptr()); +impl Default for MigrationTable { + fn default() -> Self { + Self::new().unwrap() + } } + +pub type OwningMigrationTable = MigrationTable; + +impl OwningMigrationTable {} diff --git a/src/mutation_table.rs b/src/mutation_table.rs index 19f28da06..5aaed0c41 100644 --- a/src/mutation_table.rs +++ b/src/mutation_table.rs @@ -154,22 +154,70 @@ impl<'a> streaming_iterator::StreamingIterator for MutationTableRowView<'a> { } } -/// An immutable view of site table. +/// A mutation table /// -/// These are not created directly but are accessed -/// by types implementing [`std::ops::Deref`] to -/// [`crate::table_views::TableViews`] +/// # Examples +/// +/// # Standalone tables +/// +/// ``` +/// use tskit::MutationTable; +/// +/// let mut mutations = MutationTable::default(); +/// let rowid = mutations.add_row(1, 2, 0, 1.0, None).unwrap(); +/// assert_eq!(rowid, 0); +/// assert_eq!(mutations.num_rows(), 1); +/// ``` +/// +/// An example with metadata. +/// This requires the cargo feature `"derive"` for `tskit`. +/// +/// ``` +/// # #[cfg(any(feature="doc", feature="derive"))] { +/// use tskit::MutationTable; +/// +/// #[derive(serde::Serialize, +/// serde::Deserialize, +/// tskit::metadata::MutationMetadata)] +/// #[serializer("serde_json")] +/// struct MutationMetadata { +/// value: i32, +/// } +/// +/// let metadata = MutationMetadata{value: 42}; +/// +/// let mut mutations = MutationTable::default(); +/// +/// let rowid = mutations.add_row_with_metadata(0, 1, 5, 10.0, None, &metadata).unwrap(); +/// assert_eq!(rowid, 0); +/// +/// match mutations.metadata::(rowid) { +/// // rowid is in range, decoding succeeded +/// Some(Ok(decoded)) => assert_eq!(decoded.value, 42), +/// // rowid is in range, decoding failed +/// Some(Err(e)) => panic!("error decoding metadata: {:?}", e), +/// None => panic!("row id out of range") +/// } +/// # } #[derive(Debug)] #[repr(transparent)] pub struct MutationTable { - table_: sys::LLMutationTableRef, + table_: sys::LLMutationTable, } impl MutationTable { + pub(crate) fn as_ptr(&self) -> *const ll_bindings::tsk_mutation_table_t { + self.table_.as_ptr() + } + + pub(crate) fn as_mut_ptr(&mut self) -> *mut ll_bindings::tsk_mutation_table_t { + self.table_.as_mut_ptr() + } + pub(crate) fn new_from_table( mutations: *mut ll_bindings::tsk_mutation_table_t, ) -> Result { - let table_ = sys::LLMutationTableRef::new_from_table(mutations)?; + let table_ = sys::LLMutationTable::new_non_owning(mutations)?; Ok(MutationTable { table_ }) } @@ -177,6 +225,11 @@ impl MutationTable { self.table_.as_ref() } + pub fn new() -> Result { + let table_ = sys::LLMutationTable::new_owning(0)?; + Ok(Self { table_ }) + } + /// Return the number of rows. pub fn num_rows(&self) -> SizeType { self.as_ref().num_rows.into() @@ -321,6 +374,13 @@ impl MutationTable { Some(view) } + pub fn clear(&mut self) -> Result<(), TskitError> { + self.table_.clear().map_err(|e| e.into()) + } + + mutation_table_add_row!(=> add_row, self, self.as_mut_ptr()); + mutation_table_add_row_with_metadata!(=> add_row_with_metadata, self, self.as_mut_ptr()); + build_table_column_slice_getter!( /// Get the node column as a slice => node, node_slice, NodeId); @@ -347,58 +407,10 @@ impl MutationTable { => parent, parent_slice_raw, crate::tsk_id_t); } -build_owned_table_type!( -/// A standalone mutation table that owns its data. -/// -/// # Examples -/// -/// ``` -/// use tskit::OwningMutationTable; -/// -/// let mut mutations = OwningMutationTable::default(); -/// let rowid = mutations.add_row(1, 2, 0, 1.0, None).unwrap(); -/// assert_eq!(rowid, 0); -/// assert_eq!(mutations.num_rows(), 1); -/// ``` -/// -/// An example with metadata. -/// This requires the cargo feature `"derive"` for `tskit`. -/// -/// ``` -/// # #[cfg(any(feature="doc", feature="derive"))] { -/// use tskit::OwningMutationTable; -/// -/// #[derive(serde::Serialize, -/// serde::Deserialize, -/// tskit::metadata::MutationMetadata)] -/// #[serializer("serde_json")] -/// struct MutationMetadata { -/// value: i32, -/// } -/// -/// let metadata = MutationMetadata{value: 42}; -/// -/// let mut mutations = OwningMutationTable::default(); -/// -/// let rowid = mutations.add_row_with_metadata(0, 1, 5, 10.0, None, &metadata).unwrap(); -/// assert_eq!(rowid, 0); -/// -/// match mutations.metadata::(rowid) { -/// // rowid is in range, decoding succeeded -/// Some(Ok(decoded)) => assert_eq!(decoded.value, 42), -/// // rowid is in range, decoding failed -/// Some(Err(e)) => panic!("error decoding metadata: {:?}", e), -/// None => panic!("row id out of range") -/// } -/// # } -/// ``` - => OwningMutationTable, - MutationTable, - crate::sys::LLOwningMutationTable, - crate::bindings::tsk_mutation_table_t -); - -impl OwningMutationTable { - mutation_table_add_row!(=> add_row, self, self.as_mut_ptr()); - mutation_table_add_row_with_metadata!(=> add_row_with_metadata, self, self.as_mut_ptr()); +impl Default for MutationTable { + fn default() -> Self { + Self::new().unwrap() + } } + +pub type OwningMutationTable = MutationTable; diff --git a/src/node_table.rs b/src/node_table.rs index d460bed93..ba325180a 100644 --- a/src/node_table.rs +++ b/src/node_table.rs @@ -138,22 +138,70 @@ impl<'a> streaming_iterator::StreamingIterator for NodeTableRowView<'a> { } } -/// An immtable view of a node table. +/// A node table. /// -/// These are not created directly but are accessed -/// by types implementing [`std::ops::Deref`] to -/// [`crate::table_views::TableViews`] +/// # Examples +/// +/// ``` +/// use tskit::NodeTable; +/// +/// let mut nodes = NodeTable::default(); +/// let rowid = nodes.add_row(0, 1.1, -1, -1).unwrap(); +/// assert_eq!(rowid, 0); +/// assert_eq!(nodes.num_rows(), 1); +/// ``` +/// +/// An example with metadata. +/// This requires the cargo feature `"derive"` for `tskit`. +/// +/// ``` +/// # #[cfg(any(feature="doc", feature="derive"))] { +/// use tskit::NodeTable; +/// +/// #[derive(serde::Serialize, +/// serde::Deserialize, +/// tskit::metadata::NodeMetadata)] +/// #[serializer("serde_json")] +/// struct NodeMetadata { +/// value: i32, +/// } +/// +/// let metadata = NodeMetadata{value: 42}; +/// +/// let mut nodes = NodeTable::default(); +/// +/// let rowid = nodes.add_row_with_metadata(0, 1., -1, -1, &metadata).unwrap(); +/// assert_eq!(rowid, 0); +/// +/// match nodes.metadata::(rowid) { +/// // rowid is in range, decoding succeeded +/// Some(Ok(decoded)) => assert_eq!(decoded.value, 42), +/// // rowid is in range, decoding failed +/// Some(Err(e)) => panic!("error decoding metadata: {:?}", e), +/// None => panic!("row id out of range") +/// } +/// +/// # } +/// ``` #[derive(Debug)] #[repr(transparent)] pub struct NodeTable { - table_: sys::LLNodeTableRef, + table_: sys::LLNodeTable, } impl NodeTable { + pub(crate) fn as_ptr(&self) -> *const ll_bindings::tsk_node_table_t { + self.table_.as_ptr() + } + + pub(crate) fn as_mut_ptr(&mut self) -> *mut ll_bindings::tsk_node_table_t { + self.table_.as_mut_ptr() + } + pub(crate) fn new_from_table( nodes: *mut ll_bindings::tsk_node_table_t, ) -> Result { - let table_ = sys::LLNodeTableRef::new_from_table(nodes)?; + let table_ = sys::LLNodeTable::new_non_owning(nodes)?; Ok(NodeTable { table_ }) } @@ -161,6 +209,11 @@ impl NodeTable { self.table_.as_ref() } + pub fn new() -> Result { + let table_ = sys::LLNodeTable::new_owning(0)?; + Ok(Self { table_ }) + } + /// Return the number of rows pub fn num_rows(&self) -> SizeType { self.as_ref().num_rows.into() @@ -391,6 +444,13 @@ impl NodeTable { .collect::>() } + pub fn clear(&mut self) -> Result<(), TskitError> { + self.table_.clear().map_err(|e| e.into()) + } + + node_table_add_row!(=> add_row, self, self.as_mut_ptr()); + node_table_add_row_with_metadata!(=> add_row_with_metadata, self, self.as_mut_ptr()); + build_table_column_slice_getter!( /// Get the time column as a slice => time, time_slice, Time); @@ -535,61 +595,12 @@ impl NodeTable { => population, population_slice_raw, crate::tsk_id_t); } -build_owned_table_type!( - /// A standalone node table that owns its data. - /// - /// # Examples - /// - /// ``` - /// use tskit::OwningNodeTable; - /// - /// let mut nodes = OwningNodeTable::default(); - /// let rowid = nodes.add_row(0, 1.1, -1, -1).unwrap(); - /// assert_eq!(rowid, 0); - /// assert_eq!(nodes.num_rows(), 1); - /// ``` - /// - /// An example with metadata. - /// This requires the cargo feature `"derive"` for `tskit`. - /// - /// ``` - /// # #[cfg(any(feature="doc", feature="derive"))] { - /// use tskit::OwningNodeTable; - /// - /// #[derive(serde::Serialize, - /// serde::Deserialize, - /// tskit::metadata::NodeMetadata)] - /// #[serializer("serde_json")] - /// struct NodeMetadata { - /// value: i32, - /// } - /// - /// let metadata = NodeMetadata{value: 42}; - /// - /// let mut nodes = OwningNodeTable::default(); - /// - /// let rowid = nodes.add_row_with_metadata(0, 1., -1, -1, &metadata).unwrap(); - /// assert_eq!(rowid, 0); - /// - /// match nodes.metadata::(rowid) { - /// // rowid is in range, decoding succeeded - /// Some(Ok(decoded)) => assert_eq!(decoded.value, 42), - /// // rowid is in range, decoding failed - /// Some(Err(e)) => panic!("error decoding metadata: {:?}", e), - /// None => panic!("row id out of range") - /// } - /// - /// # } - /// ``` - => OwningNodeTable, - NodeTable, - crate::sys::LLOwningNodeTable, - crate::bindings::tsk_node_table_t -); +pub type OwningNodeTable = NodeTable; -impl OwningNodeTable { - node_table_add_row!(=> add_row, self, self.as_mut_ptr()); - node_table_add_row_with_metadata!(=> add_row_with_metadata, self, self.as_mut_ptr()); +impl Default for NodeTable { + fn default() -> Self { + Self::new().unwrap() + } } #[cfg(test)] diff --git a/src/population_table.rs b/src/population_table.rs index 154aa3c36..96fa11ead 100644 --- a/src/population_table.rs +++ b/src/population_table.rs @@ -106,22 +106,71 @@ impl<'a> streaming_iterator::StreamingIterator for PopulationTableRowView<'a> { } } -/// An immutable view of site table. +/// A population table. /// -/// These are not created directly but are accessed -/// by types implementing [`std::ops::Deref`] to -/// [`crate::table_views::TableViews`] +/// # Examples +/// +/// # Standalone tables +/// +/// ``` +/// use tskit::PopulationTable; +/// +/// let mut populations = PopulationTable::default(); +/// let rowid = populations.add_row().unwrap(); +/// assert_eq!(rowid, 0); +/// assert_eq!(populations.num_rows(), 1); +/// ``` +/// +/// An example with metadata. +/// This requires the cargo feature `"derive"` for `tskit`. +/// +/// ``` +/// # #[cfg(any(feature="doc", feature="derive"))] { +/// use tskit::PopulationTable; +/// +/// #[derive(serde::Serialize, +/// serde::Deserialize, +/// tskit::metadata::PopulationMetadata)] +/// #[serializer("serde_json")] +/// struct PopulationMetadata { +/// name: String, +/// } +/// +/// let metadata = PopulationMetadata{name: "YRB".to_string()}; +/// +/// let mut populations = PopulationTable::default(); +/// +/// let rowid = populations.add_row_with_metadata(&metadata).unwrap(); +/// assert_eq!(rowid, 0); +/// +/// match populations.metadata::(rowid) { +/// // rowid is in range, decoding succeeded +/// Some(Ok(decoded)) => assert_eq!(&decoded.name, "YRB"), +/// // rowid is in range, decoding failed +/// Some(Err(e)) => panic!("error decoding metadata: {:?}", e), +/// None => panic!("row id out of range") +/// } +/// # } +/// ``` #[repr(transparent)] #[derive(Debug)] pub struct PopulationTable { - table_: sys::LLPopulationTableRef, + table_: sys::LLPopulationTable, } impl PopulationTable { + pub(crate) fn as_ptr(&self) -> *const ll_bindings::tsk_population_table_t { + self.table_.as_ptr() + } + + pub(crate) fn as_mut_ptr(&mut self) -> *mut ll_bindings::tsk_population_table_t { + self.table_.as_mut_ptr() + } + pub(crate) fn new_from_table( populations: *mut ll_bindings::tsk_population_table_t, ) -> Result { - let table_ = sys::LLPopulationTableRef::new_from_table(populations)?; + let table_ = sys::LLPopulationTable::new_non_owning(populations)?; Ok(PopulationTable { table_ }) } @@ -129,6 +178,11 @@ impl PopulationTable { self.table_.as_ref() } + pub fn new() -> Result { + let table_ = sys::LLPopulationTable::new_owning(0)?; + Ok(Self { table_ }) + } + raw_metadata_getter_for_tables!(PopulationId); /// Return the number of rows. @@ -208,60 +262,19 @@ impl PopulationTable { _ => None, } } -} -build_owned_table_type!( -/// A standalone population table that owns its data. -/// -/// # Examples -/// -/// ``` -/// use tskit::OwningPopulationTable; -/// -/// let mut populations = OwningPopulationTable::default(); -/// let rowid = populations.add_row().unwrap(); -/// assert_eq!(rowid, 0); -/// assert_eq!(populations.num_rows(), 1); -/// ``` -/// -/// An example with metadata. -/// This requires the cargo feature `"derive"` for `tskit`. -/// -/// ``` -/// # #[cfg(any(feature="doc", feature="derive"))] { -/// use tskit::OwningPopulationTable; -/// -/// #[derive(serde::Serialize, -/// serde::Deserialize, -/// tskit::metadata::PopulationMetadata)] -/// #[serializer("serde_json")] -/// struct PopulationMetadata { -/// name: String, -/// } -/// -/// let metadata = PopulationMetadata{name: "YRB".to_string()}; -/// -/// let mut populations = OwningPopulationTable::default(); -/// -/// let rowid = populations.add_row_with_metadata(&metadata).unwrap(); -/// assert_eq!(rowid, 0); -/// -/// match populations.metadata::(rowid) { -/// // rowid is in range, decoding succeeded -/// Some(Ok(decoded)) => assert_eq!(&decoded.name, "YRB"), -/// // rowid is in range, decoding failed -/// Some(Err(e)) => panic!("error decoding metadata: {:?}", e), -/// None => panic!("row id out of range") -/// } -/// # } -/// ``` - => OwningPopulationTable, - PopulationTable, - crate::sys::LLOwningPopulationTable, - crate::bindings::tsk_population_table_t -); + pub fn clear(&mut self) -> Result<(), TskitError> { + self.table_.clear().map_err(|e| e.into()) + } -impl OwningPopulationTable { population_table_add_row!(=> add_row, self, self.as_mut_ptr()); population_table_add_row_with_metadata!(=> add_row_with_metadata, self, self.as_mut_ptr()); } + +impl Default for PopulationTable { + fn default() -> Self { + Self::new().unwrap() + } +} + +pub type OwningPopulationTable = PopulationTable; diff --git a/src/provenance.rs b/src/provenance.rs index 6dada95db..0035267db 100644 --- a/src/provenance.rs +++ b/src/provenance.rs @@ -127,26 +127,46 @@ impl<'a> streaming_iterator::StreamingIterator for ProvenanceTableRowView<'a> { } } -/// An immutable view of a provenance table. -/// -/// These are not created directly. -/// to get a reference to an existing provenance table; +/// A provenance table. /// /// # Notes /// /// * The type is enabled by the `"provenance"` feature. /// +/// # Examples +/// +/// # Standalone tables +/// +/// ```rust +/// # #[cfg(feature = "provenance")] +/// # #[cfg_attr(doc_cfg, doc(cfg(feature = "provenance")))] +/// { +/// use tskit::provenance::OwningProvenanceTable; +/// let mut provenances = OwningProvenanceTable::default(); +/// let id = provenances.add_row("message").unwrap(); +/// assert_eq!(id, 0); +/// assert_eq!(provenances.num_rows(), 1); +/// # } +/// ``` #[derive(Debug)] #[repr(transparent)] pub struct ProvenanceTable { - table_: sys::LLProvenanceTableRef, + table_: sys::LLProvenanceTable, } impl ProvenanceTable { + pub(crate) fn as_ptr(&self) -> *const ll_bindings::tsk_provenance_table_t { + self.table_.as_ptr() + } + + pub(crate) fn as_mut_ptr(&mut self) -> *mut ll_bindings::tsk_provenance_table_t { + self.table_.as_mut_ptr() + } + pub(crate) fn new_from_table( provenances: *mut ll_bindings::tsk_provenance_table_t, ) -> Result { - let table_ = sys::LLProvenanceTableRef::new_from_table(provenances)?; + let table_ = sys::LLProvenanceTable::new_non_owning(provenances)?; Ok(ProvenanceTable { table_ }) } @@ -154,6 +174,11 @@ impl ProvenanceTable { self.table_.as_ref() } + pub fn new() -> Result { + let table_ = sys::LLProvenanceTable::new_owning(0)?; + Ok(Self { table_ }) + } + /// Return the number of rows pub fn num_rows(&self) -> SizeType { self.as_ref().num_rows.into() @@ -267,34 +292,22 @@ impl ProvenanceTable { pub fn lending_iter(&self) -> ProvenanceTableRowView { ProvenanceTableRowView::new(self) } -} -build_owned_table_type!( - /// A provenance table that owns its own data. - /// - /// # Examples - /// - /// ```rust - /// # #[cfg(feature = "provenance")] - /// # #[cfg_attr(doc_cfg, doc(cfg(feature = "provenance")))] - /// { - /// use tskit::provenance::OwningProvenanceTable; - /// let mut provenances = OwningProvenanceTable::default(); - /// let id = provenances.add_row("message").unwrap(); - /// assert_eq!(id, 0); - /// assert_eq!(provenances.num_rows(), 1); - /// # } - /// ``` - => OwningProvenanceTable, - ProvenanceTable, - crate::sys::LLOwningProvenanceTable, - crate::bindings::tsk_provenance_table_t -); + pub fn clear(&mut self) -> Result<(), crate::TskitError> { + self.table_.clear().map_err(|e| e.into()) + } -impl OwningProvenanceTable { provenance_table_add_row!(=> add_row, self, self.as_mut_ptr()); } +impl Default for ProvenanceTable { + fn default() -> Self { + Self::new().unwrap() + } +} + +pub type OwningProvenanceTable = ProvenanceTable; + #[cfg(test)] mod test_provenances { use streaming_iterator::StreamingIterator; diff --git a/src/site_table.rs b/src/site_table.rs index 89130176b..548e6e1c8 100644 --- a/src/site_table.rs +++ b/src/site_table.rs @@ -124,22 +124,71 @@ impl<'a> streaming_iterator::StreamingIterator for SiteTableRowView<'a> { } } -/// An immutable view of site table. +/// A site table. /// -/// These are not created directly but are accessed -/// by types implementing [`std::ops::Deref`] to -/// [`crate::table_views::TableViews`] +/// # Examples +/// +/// # Standalone tables +/// +/// ``` +/// use tskit::SiteTable; +/// +/// let mut sites = SiteTable::default(); +/// let rowid = sites.add_row(1., None).unwrap(); +/// assert_eq!(rowid, 0); +/// assert_eq!(sites.num_rows(), 1); +/// ``` +/// +/// An example with metadata. +/// This requires the cargo feature `"derive"` for `tskit`. +/// +/// ``` +/// # #[cfg(any(feature="doc", feature="derive"))] { +/// use tskit::SiteTable; +/// +/// #[derive(serde::Serialize, +/// serde::Deserialize, +/// tskit::metadata::SiteMetadata)] +/// #[serializer("serde_json")] +/// struct SiteMetadata { +/// value: i32, +/// } +/// +/// let metadata = SiteMetadata{value: 42}; +/// +/// let mut sites = SiteTable::default(); +/// +/// let rowid = sites.add_row_with_metadata(0., None, &metadata).unwrap(); +/// assert_eq!(rowid, 0); +/// +/// match sites.metadata::(rowid) { +/// // rowid is in range, decoding succeeded +/// Some(Ok(decoded)) => assert_eq!(decoded.value, 42), +/// // rowid is in range, decoding failed +/// Some(Err(e)) => panic!("error decoding metadata: {:?}", e), +/// None => panic!("row id out of range") +/// } +/// # } +/// ``` #[derive(Debug)] #[repr(transparent)] pub struct SiteTable { - table_: sys::LLSiteTableRef, + table_: sys::LLSiteTable, } impl SiteTable { + pub(crate) fn as_ptr(&self) -> *const ll_bindings::tsk_site_table_t { + self.table_.as_ptr() + } + + pub(crate) fn as_mut_ptr(&mut self) -> *mut ll_bindings::tsk_site_table_t { + self.table_.as_mut_ptr() + } + pub(crate) fn new_from_table( sites: *mut ll_bindings::tsk_site_table_t, ) -> Result { - let table_ = sys::LLSiteTableRef::new_from_table(sites)?; + let table_ = sys::LLSiteTable::new_non_owning(sites)?; Ok(SiteTable { table_ }) } @@ -147,6 +196,11 @@ impl SiteTable { self.table_.as_ref() } + pub fn new() -> Result { + let table_ = sys::LLSiteTable::new_owning(0)?; + Ok(Self { table_ }) + } + raw_metadata_getter_for_tables!(SiteId); /// Return the number of rows @@ -254,6 +308,13 @@ impl SiteTable { Some(view) } + pub fn clear(&mut self) -> Result<(), TskitError> { + self.table_.clear().map_err(|e| e.into()) + } + + site_table_add_row!(=> add_row, self, self.as_mut_ptr()); + site_table_add_row_with_metadata!(=> add_row_with_metadata, self, self.as_mut_ptr()); + build_table_column_slice_getter!( /// Get the position column as a slice => position, position_slice, Position); @@ -262,58 +323,10 @@ impl SiteTable { => position, position_slice_raw, f64); } -build_owned_table_type!( - /// A standalone site table that owns its data. - /// - /// # Examples - /// - /// ``` - /// use tskit::OwningSiteTable; - /// - /// let mut sites = OwningSiteTable::default(); - /// let rowid = sites.add_row(1., None).unwrap(); - /// assert_eq!(rowid, 0); - /// assert_eq!(sites.num_rows(), 1); - /// ``` - /// - /// An example with metadata. - /// This requires the cargo feature `"derive"` for `tskit`. - /// - /// ``` - /// # #[cfg(any(feature="doc", feature="derive"))] { - /// use tskit::OwningSiteTable; - /// - /// #[derive(serde::Serialize, - /// serde::Deserialize, - /// tskit::metadata::SiteMetadata)] - /// #[serializer("serde_json")] - /// struct SiteMetadata { - /// value: i32, - /// } - /// - /// let metadata = SiteMetadata{value: 42}; - /// - /// let mut sites = OwningSiteTable::default(); - /// - /// let rowid = sites.add_row_with_metadata(0., None, &metadata).unwrap(); - /// assert_eq!(rowid, 0); - /// - /// match sites.metadata::(rowid) { - /// // rowid is in range, decoding succeeded - /// Some(Ok(decoded)) => assert_eq!(decoded.value, 42), - /// // rowid is in range, decoding failed - /// Some(Err(e)) => panic!("error decoding metadata: {:?}", e), - /// None => panic!("row id out of range") - /// } - /// # } - /// ``` - => OwningSiteTable, - SiteTable, - crate::sys::LLOwningSiteTable, - crate::bindings::tsk_site_table_t -); - -impl OwningSiteTable { - site_table_add_row!(=> add_row, self, self.as_mut_ptr()); - site_table_add_row_with_metadata!(=> add_row_with_metadata, self, self.as_mut_ptr()); +impl Default for SiteTable { + fn default() -> Self { + Self::new().unwrap() + } } + +pub type OwningSiteTable = SiteTable; diff --git a/src/sys.rs b/src/sys.rs index 1d75affdf..0e8c24aa7 100644 --- a/src/sys.rs +++ b/src/sys.rs @@ -1,5 +1,4 @@ use std::ffi::CString; -use std::ptr::NonNull; use mbox::MBox; use thiserror::Error; @@ -16,36 +15,6 @@ use bindings::tsk_population_table_t; use bindings::tsk_provenance_table_t; use bindings::tsk_site_table_t; -use bindings::tsk_edge_table_init; -use bindings::tsk_individual_table_init; -use bindings::tsk_migration_table_init; -use bindings::tsk_mutation_table_init; -use bindings::tsk_node_table_init; -use bindings::tsk_population_table_init; -#[cfg(feature = "provenance")] -use bindings::tsk_provenance_table_init; -use bindings::tsk_site_table_init; - -use bindings::tsk_edge_table_free; -use bindings::tsk_individual_table_free; -use bindings::tsk_migration_table_free; -use bindings::tsk_mutation_table_free; -use bindings::tsk_node_table_free; -use bindings::tsk_population_table_free; -#[cfg(feature = "provenance")] -use bindings::tsk_provenance_table_free; -use bindings::tsk_site_table_free; - -use bindings::tsk_edge_table_clear; -use bindings::tsk_individual_table_clear; -use bindings::tsk_migration_table_clear; -use bindings::tsk_mutation_table_clear; -use bindings::tsk_node_table_clear; -use bindings::tsk_population_table_clear; -#[cfg(feature = "provenance")] -use bindings::tsk_provenance_table_clear; -use bindings::tsk_site_table_clear; - #[non_exhaustive] #[derive(Error, Debug)] pub enum Error { @@ -53,155 +22,223 @@ pub enum Error { Message(String), #[error("{}", get_tskit_error_message(*.0))] Code(i32), + #[error("NULL pointer encountered")] + NullPointer, } -macro_rules! basic_lltableref_impl { - ($lltable: ident, $tsktable: ident) => { - #[repr(transparent)] - #[derive(Debug)] - pub struct $lltable(NonNull); +#[derive(Debug)] +pub struct LowLevelPointerManager { + pointer: *mut T, + owned: bool, + tskfree: Option i32>, +} - impl $lltable { - pub fn new_from_table(table: *mut $tsktable) -> Result { - let internal = NonNull::new(table).ok_or_else(|| { - let msg = format!("null pointer to {}", stringify!($tsktable)); - Error::Message(msg) - })?; - Ok(Self(internal)) - } +impl LowLevelPointerManager { + fn new_owning(init: I, free: fn(*mut T) -> i32) -> Result + where + I: Fn(*mut T) -> i32, + { + let pointer = unsafe { libc::malloc(std::mem::size_of::()) as *mut T }; + if pointer.is_null() { + Err(Error::Code(crate::bindings::TSK_ERR_NO_MEMORY)) + } else { + // The call to setup will leak memory if we don't + // explicitly match the Ok/Err pathways. + // Instead, we use RAII via MBox to free our pointer + // in the case where setup errors. + + // SAFETY: pointer not null + let mut pointer = unsafe { MBox::from_raw(pointer) }; + Self::setup(pointer.as_mut(), init)?; + Ok(Self { + pointer: MBox::into_raw(pointer), + owned: true, + tskfree: Some(free), + }) + } + } - pub fn as_ref(&self) -> &$tsktable { - // SAFETY: we cannot get this far w/o - // going through new_from_table and that - // fn protects us from null ptrs - unsafe { self.0.as_ref() } - } + fn new_non_owning(pointer: *mut T) -> Result { + if pointer.is_null() { + Err(Error::NullPointer {}) + } else { + Ok(Self { + pointer, + owned: false, + // In tskit-c, a non-owning pointer does not tear down + // its data. Doing so is the responsibility + // of the owning object. + tskfree: None, + }) } - }; -} + } -basic_lltableref_impl!(LLEdgeTableRef, tsk_edge_table_t); -basic_lltableref_impl!(LLNodeTableRef, tsk_node_table_t); -basic_lltableref_impl!(LLMutationTableRef, tsk_mutation_table_t); -basic_lltableref_impl!(LLSiteTableRef, tsk_site_table_t); -basic_lltableref_impl!(LLMigrationTableRef, tsk_migration_table_t); -basic_lltableref_impl!(LLPopulationTableRef, tsk_population_table_t); -basic_lltableref_impl!(LLIndividualTableRef, tsk_individual_table_t); + fn setup(pointer: *mut T, tskinit: I) -> Result<(), Error> + where + I: Fn(*mut T) -> i32, + { + assert!(!pointer.is_null()); + match tskinit(pointer) { + code if code < 0 => Err(Error::Code(code)), + _ => Ok(()), + } + } -#[cfg(feature = "provenance")] -basic_lltableref_impl!(LLProvenanceTableRef, tsk_provenance_table_t); + fn teardown(&mut self) -> Result<(), Error> { + assert!(!self.pointer.is_null()); + self.tskfree.map_or_else( + || Ok(()), + |function| match function(self.pointer) { + code if code < 0 => Err(Error::Code(code)), + _ => Ok(()), + }, + ) + } + + // NOTE: the stuff below is boiler-plate-y + // and we'll want to make that less painful later. + + fn as_mut_ptr(&mut self) -> *mut T { + self.pointer + } + + fn as_ptr(&self) -> *const T { + self.pointer + } + + // fn as_mut(&mut self) -> &mut T { + // assert!(self.pointer.is_null()); + // // SAFETY: pointer is not null + // unsafe { &mut *self.pointer } + // } + + fn as_ref(&self) -> &T { + assert!(!self.pointer.is_null()); + // SAFETY: pointer is not null + unsafe { &*self.pointer } + } +} + +impl Drop for LowLevelPointerManager { + fn drop(&mut self) { + // Will not + self.teardown().unwrap(); + if self.owned { + assert!(!self.pointer.is_null()); + // SAFETY: pointer is not null and we "own" it, + // meaning that we malloc'd it. + unsafe { libc::free(self.pointer.cast::()) } + } + } +} -macro_rules! basic_llowningtable_impl { - ($llowningtable: ident, $tsktable: ident, $init: ident, $free: ident, $clear: ident) => { +macro_rules! basic_lltable_impl { + ($lltable: ident, $tsktable: ident, $init: expr, $free: expr, $clear: expr) => { #[repr(transparent)] #[derive(Debug)] - pub struct $llowningtable(MBox<$tsktable>); - - impl $llowningtable { - pub fn new() -> Self { - let temp = - unsafe { libc::malloc(std::mem::size_of::<$tsktable>()) as *mut $tsktable }; - let nonnull = match std::ptr::NonNull::<$tsktable>::new(temp) { - Some(x) => x, - None => panic!("out of memory"), - }; - let mut table = unsafe { mbox::MBox::from_non_null_raw(nonnull) }; - let rv = unsafe { $init(&mut (*table), 0) }; - assert_eq!(rv, 0); - Self(table) - } + pub struct $lltable(LowLevelPointerManager<$tsktable>); - pub fn as_ptr(&self) -> *const $tsktable { - MBox::<$tsktable>::as_ptr(&self.0) + impl $lltable { + pub fn new_owning(flags: bindings::tsk_flags_t) -> Result { + let internal = LowLevelPointerManager::<$tsktable>::new_owning( + |x| { + assert!(!x.is_null()); + // SAFETY: pointer is not NULL + unsafe { $init(x, flags) } + }, + |x| { + assert!(!x.is_null()); + // SAFETY: pointer is not NULL + unsafe { $free(x) } + }, + )?; + Ok(Self(internal)) } - pub fn as_mut_ptr(&mut self) -> *mut $tsktable { - MBox::<$tsktable>::as_mut_ptr(&mut self.0) + pub fn new_non_owning(table: *mut $tsktable) -> Result { + let internal = LowLevelPointerManager::<$tsktable>::new_non_owning(table)?; + Ok(Self(internal)) } - fn free(&mut self) -> Result<(), Error> { - match unsafe { $free(self.as_mut_ptr()) } { - code if code < 0 => Err(Error::Code(code)), + pub fn clear(&mut self) -> Result<(), Error> { + assert!(!self.0.pointer.is_null()); + match unsafe { $clear(self.0.pointer) } { + x if x < 0 => Err(Error::Code(x)), _ => Ok(()), } } - pub fn clear(&mut self) -> Result { - match unsafe { $clear(self.as_mut_ptr()) } { - code if code < 0 => Err(Error::Code(code)), - code => Ok(code), - } + pub fn as_ref(&self) -> &$tsktable { + self.0.as_ref() } - } - impl Drop for $llowningtable { - fn drop(&mut self) { - match self.free() { - Ok(_) => (), - Err(e) => panic!("{}", e), - } + pub fn as_ptr(&self) -> *const $tsktable { + self.0.as_ptr() + } + + pub fn as_mut_ptr(&mut self) -> *mut $tsktable { + self.0.as_mut_ptr() } } }; } -basic_llowningtable_impl!( - LLOwningEdgeTable, +basic_lltable_impl!( + LLEdgeTable, tsk_edge_table_t, - tsk_edge_table_init, - tsk_edge_table_free, - tsk_edge_table_clear + bindings::tsk_edge_table_init, + bindings::tsk_edge_table_free, + bindings::tsk_edge_table_clear ); -basic_llowningtable_impl!( - LLOwningNodeTable, +basic_lltable_impl!( + LLNodeTable, tsk_node_table_t, - tsk_node_table_init, - tsk_node_table_free, - tsk_node_table_clear + bindings::tsk_node_table_init, + bindings::tsk_node_table_free, + bindings::tsk_node_table_clear ); -basic_llowningtable_impl!( - LLOwningSiteTable, +basic_lltable_impl!( + LLSiteTable, tsk_site_table_t, - tsk_site_table_init, - tsk_site_table_free, - tsk_site_table_clear + bindings::tsk_site_table_init, + bindings::tsk_site_table_free, + bindings::tsk_site_table_clear ); -basic_llowningtable_impl!( - LLOwningMutationTable, +basic_lltable_impl!( + LLMutationTable, tsk_mutation_table_t, - tsk_mutation_table_init, - tsk_mutation_table_free, - tsk_mutation_table_clear + bindings::tsk_mutation_table_init, + bindings::tsk_mutation_table_free, + bindings::tsk_mutation_table_clear ); -basic_llowningtable_impl!( - LLOwningIndividualTable, +basic_lltable_impl!( + LLIndividualTable, tsk_individual_table_t, - tsk_individual_table_init, - tsk_individual_table_free, - tsk_individual_table_clear + bindings::tsk_individual_table_init, + bindings::tsk_individual_table_free, + bindings::tsk_individual_table_clear ); -basic_llowningtable_impl!( - LLOwningMigrationTable, - tsk_migration_table_t, - tsk_migration_table_init, - tsk_migration_table_free, - tsk_migration_table_clear -); -basic_llowningtable_impl!( - LLOwningPopulationTable, +basic_lltable_impl!( + LLPopulationTable, tsk_population_table_t, - tsk_population_table_init, - tsk_population_table_free, - tsk_population_table_clear + bindings::tsk_population_table_init, + bindings::tsk_population_table_free, + bindings::tsk_population_table_clear +); +basic_lltable_impl!( + LLMigrationTable, + tsk_migration_table_t, + bindings::tsk_migration_table_init, + bindings::tsk_migration_table_free, + bindings::tsk_migration_table_clear ); - #[cfg(feature = "provenance")] -basic_llowningtable_impl!( - LLOwningProvenanceTable, +basic_lltable_impl!( + LLProvenanceTable, tsk_provenance_table_t, - tsk_provenance_table_init, - tsk_provenance_table_free, - tsk_provenance_table_clear + bindings::tsk_provenance_table_init, + bindings::tsk_provenance_table_free, + bindings::tsk_provenance_table_clear ); #[repr(transparent)] @@ -459,3 +496,47 @@ fn test_error_code() { _ => panic!("unexpected match"), } } + +// NOTE: these are design phase tests + +#[test] +fn test_low_level_table_collection_pointer_manager_owning() { + let flags: bindings::tsk_flags_t = 0; + let mut x = LowLevelPointerManager::::new_owning( + |x| { + assert!(!x.is_null()); + // SAFETY: pointer is not NULL + unsafe { bindings::tsk_table_collection_init(x, flags) } + }, + |x| { + assert!(!x.is_null()); + // SAFETY: pointer is not NULL + unsafe { bindings::tsk_table_collection_free(x) } + }, + ) + .unwrap(); + assert!(x.owned); + assert!(!x.as_ptr().is_null()); + assert!(!x.as_mut_ptr().is_null()); +} + +#[test] +fn test_low_level_table_collection_pointer_manager_non_owning() { + let raw = unsafe { + libc::malloc(std::mem::size_of::()) + as *mut bindings::tsk_table_collection_t + }; + let mut x = + LowLevelPointerManager::::new_non_owning(raw).unwrap(); + assert!(!x.owned); + assert!(!x.as_ptr().is_null()); + assert!(!x.as_mut_ptr().is_null()); + unsafe { libc::free(raw as *mut libc::c_void) }; +} + +// NOTE: design phase 2 tests + +#[test] +fn test_lledgetable() { + let _ = LLEdgeTable::new_owning(0).unwrap(); +}