Skip to content

Generic fn for ragged column access #423

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 4, 2022
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
127 changes: 4 additions & 123 deletions src/_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,130 +27,12 @@ macro_rules! panic_on_tskit_error {
};
}

macro_rules! unsafe_tsk_ragged_column_access {
($i: expr, $lo: expr, $hi: expr, $owner: expr, $array: ident, $offset_array: ident, $offset_array_len: ident, $output_id_type: ty) => {{
let i = $crate::SizeType::try_from($i).ok()?;
if $i < $lo || i >= $hi {
None
} else if $owner.$offset_array_len == 0 {
None
} else {
debug_assert!(!$owner.$array.is_null());
if $owner.$array.is_null() {
return None;
}
let offset = i.as_usize() as isize;
// SAFETY: we have checked bounds and ensured not null
let start = unsafe { *$owner.$offset_array.offset(offset) };
let stop = if i < $hi {
unsafe { *$owner.$offset_array.offset((offset + 1) as isize) }
} else {
$owner.$offset_array_len as tsk_size_t
};
if start == stop {
None
} else {
Some(unsafe {
std::slice::from_raw_parts(
$owner.$array.offset(start as isize) as *const $output_id_type,
stop as usize - start as usize,
)
})
}
}
}};
}

// Allow this to be unused for default features
// to pass clippy checks
#[allow(unused_macros)]
macro_rules! unsafe_tsk_ragged_char_column_access {
($i: expr, $lo: expr, $hi: expr, $owner: expr, $array: ident, $offset_array: ident, $offset_array_len: ident) => {{
let i = $crate::SizeType::try_from($i).ok()?;
if $i < $lo || i >= $hi {
None
} else if $owner.$offset_array_len == 0 {
None
} else {
assert!(!$owner.$array.is_null());
assert!(!$owner.$offset_array.is_null());
let start = unsafe { *$owner.$offset_array.offset($i as isize) };
let stop = if i < $hi {
unsafe { *$owner.$offset_array.offset(($i + 1) as isize) }
} else {
$owner.$offset_array_len as tsk_size_t
};
if start == stop {
None
} else {
let mut buffer = String::new();
for i in start..stop {
buffer.push(unsafe { *$owner.$array.offset(i as isize) as u8 as char });
}
Some(buffer)
}
}
}};
}

#[cfg(feature = "provenance")]
macro_rules! unsafe_tsk_ragged_char_column_access_to_slice_u8 {
($i: expr, $lo: expr, $hi: expr, $owner: expr, $array: ident, $offset_array: ident, $offset_array_len: ident) => {{
let i = match $crate::SizeType::try_from($i).ok() {
Some(j) => j,
None => $crate::SizeType::from(u64::MAX),
};
if $i < $lo || i >= $hi {
None
} else if $owner.$offset_array_len == 0 {
None
} else {
assert!(!$owner.$array.is_null());
assert!(!$owner.$offset_array.is_null());
let offset = i.as_usize() as isize;
let start = unsafe { *$owner.$offset_array.offset(offset) };
let stop = if i < $hi {
unsafe { *$owner.$offset_array.offset((offset + 1) as isize) }
} else {
$owner.$offset_array_len as tsk_size_t
};
if start == stop {
None
} else {
let ptr = unsafe { $owner.$array.offset(start as isize) as *const u8 };
let len = (stop - start) as usize;
let slice = unsafe { std::slice::from_raw_parts(ptr, len) };
Some(slice)
}
}
}};
}

macro_rules! metadata_to_vector {
($outer: ident, $table: expr, $row: expr) => {
$crate::metadata::char_column_to_slice(
$outer,
$table.metadata,
$table.metadata_offset,
$row,
$table.num_rows,
$table.metadata_length,
)
};
}

macro_rules! decode_metadata_row {
($T: ty, $buffer: expr) => {
<$T as $crate::metadata::MetadataRoundtrip>::decode($buffer)
};
}

macro_rules! table_row_decode_metadata {
($owner: ident, $table: ident, $pos: ident) => {
metadata_to_vector!($owner, $table, $pos)
};
}

macro_rules! process_state_input {
($state: expr) => {
match $state {
Expand Down Expand Up @@ -1093,14 +975,13 @@ macro_rules! build_owned_table_type {
}

macro_rules! raw_metadata_getter_for_tables {
($idtype: ident) => {
($idtype: ty) => {
fn raw_metadata(&self, row: $idtype) -> Option<&[u8]> {
$crate::metadata::char_column_to_slice(
self,
$crate::sys::tsk_ragged_column_access::<'_, u8, $idtype, _, _>(
row.into(),
self.as_ref().metadata,
self.num_rows(),
self.as_ref().metadata_offset,
row.into(),
self.num_rows().into(),
self.as_ref().metadata_length,
)
}
Expand Down
6 changes: 2 additions & 4 deletions src/edge_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,13 @@ impl PartialEq for EdgeTableRow {
}

fn make_edge_table_row(table: &EdgeTable, pos: tsk_id_t) -> Option<EdgeTableRow> {
let table_ref = table.as_ref();
Some(EdgeTableRow {
id: pos.into(),
left: table.left(pos)?,
right: table.right(pos)?,
parent: table.parent(pos)?,
child: table.child(pos)?,
metadata: table_row_decode_metadata!(table, table_ref, pos).map(|m| m.to_vec()),
metadata: table.raw_metadata(pos.into()).map(|m| m.to_vec()),
})
}

Expand Down Expand Up @@ -238,8 +237,7 @@ impl EdgeTable {
&self,
row: EdgeId,
) -> Option<Result<T, TskitError>> {
let table_ref = self.as_ref();
let buffer = metadata_to_vector!(self, table_ref, row.into())?;
let buffer = self.raw_metadata(row)?;
Some(decode_metadata_row!(T, buffer).map_err(|e| e.into()))
}

Expand Down
30 changes: 11 additions & 19 deletions src/individual_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::sys;
use crate::IndividualFlags;
use crate::IndividualId;
use crate::Location;
use crate::{tsk_id_t, tsk_size_t, TskitError};
use crate::{tsk_id_t, TskitError};
use ll_bindings::{tsk_individual_table_free, tsk_individual_table_init};

/// Row of a [`IndividualTable`]
Expand Down Expand Up @@ -109,13 +109,12 @@ pub struct IndividualTable {
}

fn make_individual_table_row(table: &IndividualTable, pos: tsk_id_t) -> Option<IndividualTableRow> {
let table_ref = table.as_ref();
Some(IndividualTableRow {
id: pos.into(),
flags: table.flags(pos)?,
location: table.location(pos).map(|s| s.to_vec()),
parents: table.parents(pos).map(|s| s.to_vec()),
metadata: table_row_decode_metadata!(table, table_ref, pos).map(|m| m.to_vec()),
metadata: table.raw_metadata(pos.into()).map(|m| m.to_vec()),
})
}

Expand Down Expand Up @@ -186,15 +185,12 @@ impl IndividualTable {
/// * `Some(location)` if `row` is valid.
/// * `None` otherwise.
pub fn location<I: Into<IndividualId> + Copy>(&self, row: I) -> Option<&[Location]> {
unsafe_tsk_ragged_column_access!(
sys::tsk_ragged_column_access(
row.into(),
0,
self.as_ref().location,
self.num_rows(),
self.as_ref(),
location,
location_offset,
location_length,
Location
self.as_ref().location_offset,
self.as_ref().location_length,
)
}

Expand All @@ -205,15 +201,12 @@ impl IndividualTable {
/// * `Some(parents)` if `row` is valid.
/// * `None` otherwise.
pub fn parents<I: Into<IndividualId> + Copy>(&self, row: I) -> Option<&[IndividualId]> {
unsafe_tsk_ragged_column_access!(
sys::tsk_ragged_column_access(
row.into(),
0,
self.as_ref().parents,
self.num_rows(),
self.as_ref(),
parents,
parents_offset,
parents_length,
IndividualId
self.as_ref().parents_offset,
self.as_ref().parents_length,
)
}

Expand Down Expand Up @@ -391,8 +384,7 @@ match tables.individuals().metadata::<MutationMetadata>(0.into())
&self,
row: IndividualId,
) -> Option<Result<T, TskitError>> {
let table_ref = self.as_ref();
let buffer = metadata_to_vector!(self, table_ref, row.into())?;
let buffer = self.raw_metadata(row)?;
Some(decode_metadata_row!(T, buffer).map_err(|e| e.into()))
}

Expand Down
54 changes: 0 additions & 54 deletions src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,6 @@
//! * We have not yet tested importing metadata encoded using `rust`
//! into `Python` via the `tskit` `Python API`.

use crate::bindings::{tsk_id_t, tsk_size_t};
use crate::SizeType;
use thiserror::Error;

Expand Down Expand Up @@ -250,59 +249,6 @@ pub enum MetadataError {
},
}

pub(crate) fn char_column_to_slice<T: Sized>(
_lifetime: &T,
column: *const libc::c_char,
column_offset: *const tsk_size_t,
row: tsk_id_t,
num_rows: tsk_size_t,
column_length: tsk_size_t,
) -> Option<&[u8]> {
let row = match tsk_size_t::try_from(row).ok() {
Some(r) if r < num_rows => r,
_ => return None,
};
if column_length == 0 {
return None;
}
let row_isize = match isize::try_from(row).ok() {
Some(x) => x,
None => return None,
};
debug_assert!(!column.is_null());
debug_assert!(!column_offset.is_null());
if column.is_null() {
return None;
}
if column_offset.is_null() {
return None;
}
// SAFETY: not null and best effort bounds check
let start = unsafe { *column_offset.offset(row_isize) };
let stop = if (row as tsk_size_t) < num_rows {
unsafe { *column_offset.offset(row_isize + 1) }
} else {
column_length
};
if start >= stop {
return None;
}
if column_length == 0 {
return None;
}
let istart = match isize::try_from(start).ok() {
Some(v) => v,
None => return None,
};
let ustop = match usize::try_from(stop).ok() {
Some(v) => v,
None => return None,
};
Some(unsafe {
std::slice::from_raw_parts(column.offset(istart) as *const u8, ustop - istart as usize)
})
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
6 changes: 2 additions & 4 deletions src/migration_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ impl PartialEq for MigrationTableRow {
}

fn make_migration_table_row(table: &MigrationTable, pos: tsk_id_t) -> Option<MigrationTableRow> {
let table_ref = table.as_ref();
Some(MigrationTableRow {
id: pos.into(),
left: table.left(pos)?,
Expand All @@ -46,7 +45,7 @@ fn make_migration_table_row(table: &MigrationTable, pos: tsk_id_t) -> Option<Mig
source: table.source(pos)?,
dest: table.dest(pos)?,
time: table.time(pos)?,
metadata: table_row_decode_metadata!(table, table_ref, pos).map(|m| m.to_vec()),
metadata: table.raw_metadata(pos.into()).map(|m| m.to_vec()),
})
}

Expand Down Expand Up @@ -285,8 +284,7 @@ impl MigrationTable {
&self,
row: MigrationId,
) -> Option<Result<T, TskitError>> {
let table_ref = self.as_ref();
let buffer = metadata_to_vector!(self, table_ref, row.into())?;
let buffer = self.raw_metadata(row)?;
Some(decode_metadata_row!(T, buffer).map_err(|e| e.into()))
}

Expand Down
13 changes: 5 additions & 8 deletions src/mutation_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ fn make_mutation_table_row(table: &MutationTable, pos: tsk_id_t) -> Option<Mutat
let index = ll_bindings::tsk_size_t::try_from(pos).ok()?;
match index {
i if i < table.num_rows() => {
let table_ref = table.as_ref();
let derived_state = table.derived_state(pos).map(|s| s.to_vec());
Some(MutationTableRow {
id: pos.into(),
Expand All @@ -46,7 +45,7 @@ fn make_mutation_table_row(table: &MutationTable, pos: tsk_id_t) -> Option<Mutat
parent: table.parent(pos)?,
time: table.time(pos)?,
derived_state,
metadata: table_row_decode_metadata!(table, table_ref, pos).map(|m| m.to_vec()),
metadata: table.raw_metadata(pos.into()).map(|m| m.to_vec()),
})
}
_ => None,
Expand Down Expand Up @@ -245,12 +244,11 @@ impl MutationTable {
/// Will return [``IndexError``](crate::TskitError::IndexError)
/// if ``row`` is out of range.
pub fn derived_state<M: Into<MutationId>>(&self, row: M) -> Option<&[u8]> {
metadata::char_column_to_slice(
self,
sys::tsk_ragged_column_access(
row.into(),
self.as_ref().derived_state,
self.num_rows(),
self.as_ref().derived_state_offset,
row.into().into(),
self.as_ref().num_rows,
self.as_ref().derived_state_length,
)
}
Expand All @@ -275,8 +273,7 @@ impl MutationTable {
&self,
row: MutationId,
) -> Option<Result<T, TskitError>> {
let table_ref = self.as_ref();
let buffer = metadata_to_vector!(self, table_ref, row.into())?;
let buffer = self.raw_metadata(row)?;
Some(decode_metadata_row!(T, buffer).map_err(|e| e.into()))
}

Expand Down
Loading