Skip to content

Commit c4cc6e8

Browse files
authored
Add tskit::SizeType (#192)
* tsk_id_t and tsk_size_t removed from prelude. * tsk_id_t and tsk_size_t no longer publicly exported from lib.rs * Public API now returns SizeType instead of tsk_size_t Closes #181
1 parent 8429cc3 commit c4cc6e8

16 files changed

+316
-80
lines changed

src/_macros.rs

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,15 @@ macro_rules! unsafe_tsk_column_access {
4646

4747
macro_rules! unsafe_tsk_ragged_column_access {
4848
($i: expr, $lo: expr, $hi: expr, $array: expr, $offset_array: expr, $offset_array_len: expr) => {{
49-
if $i < $lo || $i >= ($hi as tsk_id_t) {
49+
use std::convert::TryFrom;
50+
let i = crate::SizeType::try_from($i)?;
51+
if $i < $lo || i >= $hi {
5052
Err(TskitError::IndexError {})
5153
} else if $offset_array_len == 0 {
5254
Ok(None)
5355
} else {
5456
let start = unsafe { *$offset_array.offset($i as isize) };
55-
let stop = if $i < ($hi as tsk_id_t) {
57+
let stop = if i < $hi {
5658
unsafe { *$offset_array.offset(($i + 1) as isize) }
5759
} else {
5860
$offset_array_len as tsk_size_t
@@ -70,13 +72,15 @@ macro_rules! unsafe_tsk_ragged_column_access {
7072
}};
7173

7274
($i: expr, $lo: expr, $hi: expr, $array: expr, $offset_array: expr, $offset_array_len: expr, $output_id_type: expr) => {{
73-
if $i < $lo || $i >= ($hi as tsk_id_t) {
75+
use std::convert::TryFrom;
76+
let i = crate::SizeType::try_from($i)?;
77+
if $i < $lo || i >= $hi {
7478
Err(TskitError::IndexError {})
7579
} else if $offset_array_len == 0 {
7680
Ok(None)
7781
} else {
7882
let start = unsafe { *$offset_array.offset($i as isize) };
79-
let stop = if $i < ($hi as tsk_id_t) {
83+
let stop = if i < $hi {
8084
unsafe { *$offset_array.offset(($i + 1) as isize) }
8185
} else {
8286
$offset_array_len as tsk_size_t
@@ -99,13 +103,15 @@ macro_rules! unsafe_tsk_ragged_column_access {
99103
#[allow(unused_macros)]
100104
macro_rules! unsafe_tsk_ragged_char_column_access {
101105
($i: expr, $lo: expr, $hi: expr, $array: expr, $offset_array: expr, $offset_array_len: expr) => {{
102-
if $i < $lo || $i >= ($hi as tsk_id_t) {
106+
use std::convert::TryFrom;
107+
let i = crate::SizeType::try_from($i)?;
108+
if $i < $lo || i >= $hi {
103109
Err(TskitError::IndexError {})
104110
} else if $offset_array_len == 0 {
105111
Ok(None)
106112
} else {
107113
let start = unsafe { *$offset_array.offset($i as isize) };
108-
let stop = if $i < ($hi as tsk_id_t) {
114+
let stop = if i < $hi {
109115
unsafe { *$offset_array.offset(($i + 1) as isize) }
110116
} else {
111117
$offset_array_len as tsk_size_t
@@ -299,6 +305,14 @@ macro_rules! impl_id_traits {
299305
}
300306
}
301307

308+
impl std::convert::TryFrom<$idtype> for crate::SizeType {
309+
type Error = crate::TskitError;
310+
311+
fn try_from(value: $idtype) -> Result<Self, Self::Error> {
312+
crate::SizeType::try_from(value.0)
313+
}
314+
}
315+
302316
impl PartialEq<$crate::tsk_id_t> for $idtype {
303317
fn eq(&self, other: &$crate::tsk_id_t) -> bool {
304318
self.0 == *other
@@ -325,6 +339,35 @@ macro_rules! impl_id_traits {
325339
};
326340
}
327341

342+
macro_rules! impl_size_type_comparisons_for_row_ids {
343+
($idtype: ty) => {
344+
impl PartialEq<$idtype> for crate::SizeType {
345+
fn eq(&self, other: &$idtype) -> bool {
346+
self.0 == other.0 as crate::bindings::tsk_size_t
347+
}
348+
}
349+
350+
impl PartialEq<crate::SizeType> for $idtype {
351+
fn eq(&self, other: &crate::SizeType) -> bool {
352+
(self.0 as crate::bindings::tsk_size_t) == other.0
353+
}
354+
}
355+
356+
impl PartialOrd<$idtype> for crate::SizeType {
357+
fn partial_cmp(&self, other: &$idtype) -> Option<std::cmp::Ordering> {
358+
self.0
359+
.partial_cmp(&(other.0 as crate::bindings::tsk_size_t))
360+
}
361+
}
362+
363+
impl PartialOrd<crate::SizeType> for $idtype {
364+
fn partial_cmp(&self, other: &crate::SizeType) -> Option<std::cmp::Ordering> {
365+
(self.0 as crate::bindings::tsk_size_t).partial_cmp(&other.0)
366+
}
367+
}
368+
};
369+
}
370+
328371
/// Convenience macro to handle implementing
329372
/// [`crate::metadata::MetadataRoundtrip`]
330373
#[macro_export]

src/edge_table.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::bindings as ll_bindings;
22
use crate::metadata;
3-
use crate::{tsk_id_t, tsk_size_t, TskitError};
3+
use crate::{tsk_id_t, TskitError};
44
use crate::{EdgeId, NodeId};
55

66
/// Row of an [`EdgeTable`]
@@ -25,7 +25,12 @@ impl PartialEq for EdgeTableRow {
2525
}
2626

2727
fn make_edge_table_row(table: &EdgeTable, pos: tsk_id_t) -> Option<EdgeTableRow> {
28-
if pos < table.num_rows() as tsk_id_t {
28+
use std::convert::TryFrom;
29+
// panic is okay here, as we are handling a bad
30+
// input value before we first call this to
31+
// set up the iterator
32+
let p = crate::SizeType::try_from(pos).unwrap();
33+
if p < table.num_rows() {
2934
let rv = EdgeTableRow {
3035
id: pos.into(),
3136
left: table.left(pos).unwrap(),
@@ -78,8 +83,8 @@ impl<'a> EdgeTable<'a> {
7883
}
7984

8085
/// Return the number of rows
81-
pub fn num_rows(&'a self) -> tsk_size_t {
82-
self.table_.num_rows
86+
pub fn num_rows(&'a self) -> crate::SizeType {
87+
self.table_.num_rows.into()
8388
}
8489

8590
/// Return the ``parent`` value from row ``row`` of the table.
@@ -147,6 +152,10 @@ impl<'a> EdgeTable<'a> {
147152
///
148153
/// [`TskitError::IndexError`] if `r` is out of range.
149154
pub fn row<E: Into<EdgeId> + Copy>(&self, r: E) -> Result<EdgeTableRow, TskitError> {
150-
table_row_access!(r.into().0, self, make_edge_table_row)
155+
let ri = r.into();
156+
if ri < 0 {
157+
return Err(crate::TskitError::IndexError);
158+
}
159+
table_row_access!(ri.0, self, make_edge_table_row)
151160
}
152161
}

src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ use thiserror::Error;
55

66
#[derive(Error, Debug)]
77
pub enum TskitError {
8+
/// Returned when conversion attempts fail
9+
#[error("range error: {}", *.0)]
10+
RangeError(&'static str),
811
/// Used when bad input is encountered.
912
#[error("we received {} but expected {}",*got, *expected)]
1013
ValueError { got: String, expected: String },

src/individual_table.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,12 @@ pub struct IndividualTable<'a> {
4949
}
5050

5151
fn make_individual_table_row(table: &IndividualTable, pos: tsk_id_t) -> Option<IndividualTableRow> {
52-
if pos < table.num_rows() as tsk_id_t {
52+
use std::convert::TryFrom;
53+
// panic is okay here, as we are handling a bad
54+
// input value before we first call this to
55+
// set up the iterator
56+
let p = crate::SizeType::try_from(pos).unwrap();
57+
if p < table.num_rows() {
5358
let rv = IndividualTableRow {
5459
id: pos.into(),
5560
flags: table.flags(pos).unwrap(),
@@ -96,8 +101,8 @@ impl<'a> IndividualTable<'a> {
96101
}
97102

98103
/// Return the number of rows
99-
pub fn num_rows(&'a self) -> ll_bindings::tsk_size_t {
100-
self.table_.num_rows
104+
pub fn num_rows(&'a self) -> crate::SizeType {
105+
self.table_.num_rows.into()
101106
}
102107

103108
/// Return the flags for a given row.
@@ -181,6 +186,10 @@ impl<'a> IndividualTable<'a> {
181186
&self,
182187
r: I,
183188
) -> Result<IndividualTableRow, TskitError> {
184-
table_row_access!(r.into().0, self, make_individual_table_row)
189+
let ri = r.into();
190+
if ri < 0 {
191+
return Err(crate::TskitError::IndexError);
192+
}
193+
table_row_access!(ri.0, self, make_individual_table_row)
185194
}
186195
}

src/lib.rs

Lines changed: 111 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,9 @@ pub use bindings::TSK_NODE_IS_SAMPLE;
102102

103103
// re-export types, too
104104
pub use bindings::tsk_flags_t;
105-
pub use bindings::tsk_id_t;
106-
pub use bindings::tsk_size_t;
105+
106+
use bindings::tsk_id_t;
107+
use bindings::tsk_size_t;
107108

108109
/// A node ID
109110
///
@@ -116,7 +117,7 @@ pub use bindings::tsk_size_t;
116117
///
117118
/// ```
118119
/// use tskit::NodeId;
119-
/// use tskit::tsk_id_t;
120+
/// use tskit::bindings::tsk_id_t;
120121
///
121122
/// let x: tsk_id_t = 1;
122123
/// let y: NodeId = NodeId::from(x);
@@ -137,7 +138,7 @@ pub use bindings::tsk_size_t;
137138
///
138139
/// ```
139140
/// use tskit::NodeId;
140-
/// use tskit::tsk_id_t;
141+
/// use tskit::bindings::tsk_id_t;
141142
///
142143
/// fn interesting<N: Into<NodeId>>(x: N) -> NodeId {
143144
/// x.into()
@@ -226,6 +227,112 @@ impl_id_traits!(MutationId);
226227
impl_id_traits!(MigrationId);
227228
impl_id_traits!(EdgeId);
228229

230+
impl_size_type_comparisons_for_row_ids!(NodeId);
231+
impl_size_type_comparisons_for_row_ids!(EdgeId);
232+
impl_size_type_comparisons_for_row_ids!(SiteId);
233+
impl_size_type_comparisons_for_row_ids!(MutationId);
234+
impl_size_type_comparisons_for_row_ids!(PopulationId);
235+
impl_size_type_comparisons_for_row_ids!(MigrationId);
236+
237+
/// Wraps `tsk_size_t`
238+
///
239+
/// This type plays the role of C's `size_t` in the `tskit` C library.
240+
///
241+
/// # Examples
242+
///
243+
/// ```
244+
/// let s = tskit::SizeType::from(1 as tskit::bindings::tsk_size_t);
245+
/// let mut t: tskit::bindings::tsk_size_t = s.into();
246+
/// assert!(t == s);
247+
/// assert!(t == 1);
248+
/// let u = tskit::SizeType::from(s);
249+
/// assert!(u == s);
250+
/// t += 1;
251+
/// assert!(t > s);
252+
/// assert!(s < t);
253+
/// ```
254+
///
255+
/// #[repr(transparent)]
256+
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, std::hash::Hash)]
257+
pub struct SizeType(tsk_size_t);
258+
259+
impl std::fmt::Display for SizeType {
260+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
261+
write!(f, "SizeType({})", self.0)
262+
}
263+
}
264+
265+
impl From<tsk_size_t> for SizeType {
266+
fn from(value: tsk_size_t) -> Self {
267+
Self(value)
268+
}
269+
}
270+
271+
impl From<SizeType> for tsk_size_t {
272+
fn from(value: SizeType) -> Self {
273+
value.0
274+
}
275+
}
276+
277+
impl From<SizeType> for usize {
278+
fn from(value: SizeType) -> Self {
279+
value.0 as usize
280+
}
281+
}
282+
283+
impl From<usize> for SizeType {
284+
fn from(value: usize) -> Self {
285+
Self(value as tsk_size_t)
286+
}
287+
}
288+
289+
impl std::convert::TryFrom<tsk_id_t> for SizeType {
290+
type Error = crate::TskitError;
291+
292+
fn try_from(value: tsk_id_t) -> Result<Self, Self::Error> {
293+
match value >= 0 {
294+
true => Ok(Self(value as crate::bindings::tsk_size_t)),
295+
false => Err(crate::TskitError::RangeError(stringify!(value.0))),
296+
}
297+
}
298+
}
299+
300+
impl std::convert::TryFrom<SizeType> for tsk_id_t {
301+
type Error = crate::TskitError;
302+
303+
fn try_from(value: SizeType) -> Result<Self, Self::Error> {
304+
if value.0 > tsk_id_t::MAX as tsk_size_t {
305+
Err(TskitError::RangeError(stringify!(value.0)))
306+
} else {
307+
Ok(value.0 as tsk_id_t)
308+
}
309+
}
310+
}
311+
312+
impl PartialEq<SizeType> for tsk_size_t {
313+
fn eq(&self, other: &SizeType) -> bool {
314+
*self == other.0
315+
}
316+
}
317+
318+
impl PartialEq<tsk_size_t> for SizeType {
319+
fn eq(&self, other: &tsk_size_t) -> bool {
320+
self.0 == *other
321+
}
322+
}
323+
324+
impl PartialOrd<tsk_size_t> for SizeType {
325+
fn partial_cmp(&self, other: &tsk_size_t) -> Option<std::cmp::Ordering> {
326+
self.0.partial_cmp(other)
327+
}
328+
}
329+
330+
impl PartialOrd<SizeType> for tsk_size_t {
331+
fn partial_cmp(&self, other: &SizeType) -> Option<std::cmp::Ordering> {
332+
self.partial_cmp(&other.0)
333+
}
334+
}
335+
229336
// tskit defines this via a type cast
230337
// in a macro. bindgen thus misses it.
231338
// See bindgen issue 316.

src/metadata.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@
164164
//! into `Python` via the `tskit` `Python API`.
165165
166166
use crate::bindings::{tsk_id_t, tsk_size_t};
167+
use crate::SizeType;
167168
use thiserror::Error;
168169

169170
#[cfg(feature = "derive")]
@@ -233,8 +234,8 @@ impl EncodedMetadata {
233234
}
234235
}
235236

236-
pub(crate) fn len(&self) -> tsk_size_t {
237-
self.encoded.len() as tsk_size_t
237+
pub(crate) fn len(&self) -> SizeType {
238+
self.encoded.len().into()
238239
}
239240
}
240241

@@ -344,8 +345,8 @@ mod tests {
344345
let enc = EncodedMetadata::new(&f).unwrap();
345346
let p = enc.as_ptr();
346347
let mut d = vec![];
347-
for i in 0..enc.len() {
348-
d.push(unsafe { *p.add(i as usize) as u8 });
348+
for i in 0..usize::from(enc.len()) {
349+
d.push(unsafe { *p.add(i) as u8 });
349350
}
350351
let df = F::decode(&d).unwrap();
351352
assert_eq!(f.x, df.x);
@@ -378,8 +379,8 @@ mod test_serde {
378379
let enc = EncodedMetadata::new(&f).unwrap();
379380
let p = enc.as_ptr();
380381
let mut d = vec![];
381-
for i in 0..enc.len() {
382-
d.push(unsafe { *p.add(i as usize) as u8 });
382+
for i in 0..usize::from(enc.len()) {
383+
d.push(unsafe { *p.add(i) as u8 });
383384
}
384385
let df = F::decode(&d).unwrap();
385386
assert_eq!(f.x, df.x);

0 commit comments

Comments
 (0)