Skip to content

Commit 7d84c84

Browse files
authored
feat: add ProvenenceTableRowView (#401)
* feat: add ProvenenceTableRowView * derive Debug for provenance table, row, and row view. * partialeq for rows/row view comparison * Add new macro for ragged char column to &[u8]
1 parent 23b69ad commit 7d84c84

File tree

2 files changed

+128
-2
lines changed

2 files changed

+128
-2
lines changed

src/_macros.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,38 @@ macro_rules! unsafe_tsk_ragged_char_column_access {
131131
}};
132132
}
133133

134+
#[cfg(feature = "provenance")]
135+
macro_rules! unsafe_tsk_ragged_char_column_access_to_slice_u8 {
136+
($i: expr, $lo: expr, $hi: expr, $owner: expr, $array: ident, $offset_array: ident, $offset_array_len: ident) => {{
137+
let i = match $crate::SizeType::try_from($i).ok() {
138+
Some(j) => j,
139+
None => $crate::SizeType::from(u64::MAX),
140+
};
141+
if $i < $lo || i >= $hi {
142+
None
143+
} else if $owner.$offset_array_len == 0 {
144+
None
145+
} else {
146+
assert!(!$owner.$array.is_null());
147+
assert!(!$owner.$offset_array.is_null());
148+
let start = unsafe { *$owner.$offset_array.offset($i as isize) };
149+
let stop = if i < $hi {
150+
unsafe { *$owner.$offset_array.offset(($i + 1) as isize) }
151+
} else {
152+
$owner.$offset_array_len as tsk_size_t
153+
};
154+
if start == stop {
155+
None
156+
} else {
157+
let ptr = unsafe { $owner.$array.offset(start as isize) as *const u8 };
158+
let len = (stop - start) as usize;
159+
let slice = unsafe { std::slice::from_raw_parts(ptr, len) };
160+
Some(slice)
161+
}
162+
}
163+
}};
164+
}
165+
134166
macro_rules! metadata_to_vector {
135167
($outer: ident, $table: expr, $row: expr) => {
136168
$crate::metadata::char_column_to_slice(

src/provenance.rs

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::SizeType;
1717
use crate::{tsk_id_t, tsk_size_t, ProvenanceId};
1818
use ll_bindings::{tsk_provenance_table_free, tsk_provenance_table_init};
1919

20-
#[derive(Eq)]
20+
#[derive(Eq, Debug)]
2121
/// Row of a [`ProvenanceTable`].
2222
pub struct ProvenanceTableRow {
2323
/// The row id
@@ -75,6 +75,84 @@ impl Iterator for ProvenanceTableIterator {
7575
}
7676
}
7777

78+
#[derive(Debug)]
79+
pub struct ProvenanceTableRowView<'a> {
80+
table: &'a ProvenanceTable,
81+
/// The row id
82+
pub id: ProvenanceId,
83+
/// ISO-formatted time stamp
84+
pub timestamp: &'a str,
85+
/// The provenance record
86+
pub record: &'a str,
87+
}
88+
89+
impl<'a> ProvenanceTableRowView<'a> {
90+
fn new(table: &'a ProvenanceTable) -> Self {
91+
Self {
92+
table,
93+
id: ProvenanceId::NULL,
94+
timestamp: "",
95+
record: "",
96+
}
97+
}
98+
}
99+
100+
impl<'a> PartialEq for ProvenanceTableRowView<'a> {
101+
fn eq(&self, other: &Self) -> bool {
102+
self.id == other.id && self.timestamp == other.timestamp && self.record == other.record
103+
}
104+
}
105+
106+
impl Eq for ProvenanceTableRowView<'_> {}
107+
108+
impl<'a> PartialEq<ProvenanceTableRow> for ProvenanceTableRowView<'a> {
109+
fn eq(&self, other: &ProvenanceTableRow) -> bool {
110+
self.id == other.id && self.timestamp == other.timestamp && self.record == other.record
111+
}
112+
}
113+
114+
impl PartialEq<ProvenanceTableRowView<'_>> for ProvenanceTableRow {
115+
fn eq(&self, other: &ProvenanceTableRowView) -> bool {
116+
self.id == other.id && self.timestamp == other.timestamp && self.record == other.record
117+
}
118+
}
119+
120+
impl<'a> streaming_iterator::StreamingIterator for ProvenanceTableRowView<'a> {
121+
type Item = Self;
122+
123+
row_lending_iterator_get!();
124+
125+
fn advance(&mut self) {
126+
self.id = (i32::from(self.id) + 1).into();
127+
let record_slice = unsafe_tsk_ragged_char_column_access_to_slice_u8!(
128+
self.id.0,
129+
0,
130+
self.table.num_rows(),
131+
self.table.as_ref(),
132+
record,
133+
record_offset,
134+
record_length
135+
);
136+
self.record = match record_slice {
137+
Some(r) => std::str::from_utf8(r).unwrap(),
138+
None => "",
139+
};
140+
let timestamp_slice = unsafe_tsk_ragged_char_column_access_to_slice_u8!(
141+
self.id.0,
142+
0,
143+
self.table.num_rows(),
144+
self.table.as_ref(),
145+
timestamp,
146+
timestamp_offset,
147+
timestamp_length
148+
);
149+
self.timestamp = match timestamp_slice {
150+
Some(t) => std::str::from_utf8(t).unwrap(),
151+
None => "",
152+
};
153+
}
154+
}
155+
78156
/// An immutable view of a provenance table.
79157
///
80158
/// These are not created directly.
@@ -84,6 +162,7 @@ impl Iterator for ProvenanceTableIterator {
84162
///
85163
/// * The type is enabled by the `"provenance"` feature.
86164
///
165+
#[derive(Debug)]
87166
pub struct ProvenanceTable {
88167
table_: NonNull<ll_bindings::tsk_provenance_table_t>,
89168
}
@@ -188,6 +267,10 @@ impl ProvenanceTable {
188267
pub fn iter(&self) -> impl Iterator<Item = ProvenanceTableRow> + '_ {
189268
crate::table_iterator::make_table_iterator::<&ProvenanceTable>(self)
190269
}
270+
271+
pub fn lending_iter(&self) -> ProvenanceTableRowView {
272+
ProvenanceTableRowView::new(self)
273+
}
191274
}
192275

193276
build_owned_table_type!(
@@ -220,7 +303,7 @@ impl OwnedProvenanceTable {
220303

221304
#[cfg(test)]
222305
mod test_provenances {
223-
use super::*;
306+
use streaming_iterator::StreamingIterator;
224307

225308
#[test]
226309
fn test_empty_record_string() {
@@ -239,6 +322,7 @@ mod test_provenances {
239322

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

260344
assert!(tables.provenances().row(0).unwrap() == tables.provenances().row(0).unwrap());
261345
assert!(tables.provenances().row(0).unwrap() != tables.provenances().row(1).unwrap());
346+
347+
let mut lending_iter = tables.provenances().lending_iter();
348+
for i in [0, 1] {
349+
if let Some(row) = lending_iter.next() {
350+
assert_eq!(row.record, &records[i]);
351+
let owned_row = tables.provenances().row(i as i32).unwrap();
352+
assert_eq!(row, &owned_row);
353+
assert_eq!(&owned_row, row);
354+
}
355+
}
262356
}
263357
}

0 commit comments

Comments
 (0)