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
32 changes: 32 additions & 0 deletions src/_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,38 @@ macro_rules! unsafe_tsk_ragged_char_column_access {
}};
}

#[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 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 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(
Expand Down
98 changes: 96 additions & 2 deletions src/provenance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::SizeType;
use crate::{tsk_id_t, tsk_size_t, ProvenanceId};
use ll_bindings::{tsk_provenance_table_free, tsk_provenance_table_init};

#[derive(Eq)]
#[derive(Eq, Debug)]
/// Row of a [`ProvenanceTable`].
pub struct ProvenanceTableRow {
/// The row id
Expand Down Expand Up @@ -75,6 +75,84 @@ impl Iterator for ProvenanceTableIterator {
}
}

#[derive(Debug)]
pub struct ProvenanceTableRowView<'a> {
table: &'a ProvenanceTable,
/// The row id
pub id: ProvenanceId,
/// ISO-formatted time stamp
pub timestamp: &'a str,
/// The provenance record
pub record: &'a str,
}

impl<'a> ProvenanceTableRowView<'a> {
fn new(table: &'a ProvenanceTable) -> Self {
Self {
table,
id: ProvenanceId::NULL,
timestamp: "",
record: "",
}
}
}

impl<'a> PartialEq for ProvenanceTableRowView<'a> {
fn eq(&self, other: &Self) -> bool {
self.id == other.id && self.timestamp == other.timestamp && self.record == other.record
}
}

impl Eq for ProvenanceTableRowView<'_> {}

impl<'a> PartialEq<ProvenanceTableRow> for ProvenanceTableRowView<'a> {
fn eq(&self, other: &ProvenanceTableRow) -> bool {
self.id == other.id && self.timestamp == other.timestamp && self.record == other.record
}
}

impl PartialEq<ProvenanceTableRowView<'_>> for ProvenanceTableRow {
fn eq(&self, other: &ProvenanceTableRowView) -> bool {
self.id == other.id && self.timestamp == other.timestamp && self.record == other.record
}
}

impl<'a> streaming_iterator::StreamingIterator for ProvenanceTableRowView<'a> {
type Item = Self;

row_lending_iterator_get!();

fn advance(&mut self) {
self.id = (i32::from(self.id) + 1).into();
let record_slice = unsafe_tsk_ragged_char_column_access_to_slice_u8!(
self.id.0,
0,
self.table.num_rows(),
self.table.as_ref(),
record,
record_offset,
record_length
);
self.record = match record_slice {
Some(r) => std::str::from_utf8(r).unwrap(),
None => "",
};
let timestamp_slice = unsafe_tsk_ragged_char_column_access_to_slice_u8!(
self.id.0,
0,
self.table.num_rows(),
self.table.as_ref(),
timestamp,
timestamp_offset,
timestamp_length
);
self.timestamp = match timestamp_slice {
Some(t) => std::str::from_utf8(t).unwrap(),
None => "",
};
}
}

/// An immutable view of a provenance table.
///
/// These are not created directly.
Expand All @@ -84,6 +162,7 @@ impl Iterator for ProvenanceTableIterator {
///
/// * The type is enabled by the `"provenance"` feature.
///
#[derive(Debug)]
pub struct ProvenanceTable {
table_: NonNull<ll_bindings::tsk_provenance_table_t>,
}
Expand Down Expand Up @@ -188,6 +267,10 @@ impl ProvenanceTable {
pub fn iter(&self) -> impl Iterator<Item = ProvenanceTableRow> + '_ {
crate::table_iterator::make_table_iterator::<&ProvenanceTable>(self)
}

pub fn lending_iter(&self) -> ProvenanceTableRowView {
ProvenanceTableRowView::new(self)
}
}

build_owned_table_type!(
Expand Down Expand Up @@ -220,7 +303,7 @@ impl OwnedProvenanceTable {

#[cfg(test)]
mod test_provenances {
use super::*;
use streaming_iterator::StreamingIterator;

#[test]
fn test_empty_record_string() {
Expand All @@ -239,6 +322,7 @@ mod test_provenances {

#[test]
fn test_add_rows() {
use crate::provenance::*;
let records = vec!["banana".to_string(), "split".to_string()];
let mut tables = crate::TableCollection::new(10.).unwrap();
for (i, r) in records.iter().enumerate() {
Expand All @@ -259,5 +343,15 @@ mod test_provenances {

assert!(tables.provenances().row(0).unwrap() == tables.provenances().row(0).unwrap());
assert!(tables.provenances().row(0).unwrap() != tables.provenances().row(1).unwrap());

let mut lending_iter = tables.provenances().lending_iter();
for i in [0, 1] {
if let Some(row) = lending_iter.next() {
assert_eq!(row.record, &records[i]);
let owned_row = tables.provenances().row(i as i32).unwrap();
assert_eq!(row, &owned_row);
assert_eq!(&owned_row, row);
}
}
}
}