Skip to content

Commit

Permalink
Add fallible Indices constructor
Browse files Browse the repository at this point in the history
Checks against incorrect initialisation of the initial Indices. Incorrect initialisation could result in out of bounds behaviour when running the bisector in release mode. In debug mode, Indices::from_bisector will panic instead.
  • Loading branch information
foresterre committed May 25, 2022
1 parent 0c92de0 commit b77e615
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 0 deletions.
14 changes: 14 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use std::fmt::{Debug, Display, Formatter};

#[derive(Debug, Eq, PartialEq)]
pub struct EmptySliceError;

impl Display for EmptySliceError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"Expected a non-empty slice, but the given slice was empty (len = 0)"
))
}
}

impl std::error::Error for EmptySliceError {}
30 changes: 30 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,17 @@
#[cfg(test)]
mod tests;

pub(crate) mod error;

use std::fmt::Debug;

/// Error returned by [`Indices::try_from_bisector`], when the slice given to [`Bisector::new`]
/// is empty.
///
/// [`Indices::try_from_bisector`]: crate::Indices::try_from_bisector
/// [`Bisector::new`]: crate::Bisector::new
pub use error::EmptySliceError;

/// Stateless implementation of the bisection method.
#[derive(Debug)]
pub struct Bisector<'v, T> {
Expand Down Expand Up @@ -214,6 +223,27 @@ impl Indices {
}
}

/// Re-use the slice of the [`Bisector`] to determine the starting indices.
///
/// The returned indices will be the complete range of the slice, i.e. from index `0` to
/// index `|slice| - 1` (length of slice minus 1, i.e. the last index of the slice).
///
/// The slice given to [`Bisector`] must not be empty. If it is, an [`EmptySliceError`]
/// `Err` result will be returned..
///
/// [`Bisector`]: crate::Bisector
/// [`EmptySliceError`]: crate::EmptySliceError
pub fn try_from_bisector<T>(bisector: &Bisector<T>) -> Result<Self, EmptySliceError> {
if !bisector.view.is_empty() {
Ok(Self {
left: 0,
right: bisector.view.len() - 1,
})
} else {
Err(EmptySliceError)
}
}

/// Computes the mid-point between the left and right indices.
/// Uses integer division, so use with care.
#[inline]
Expand Down
29 changes: 29 additions & 0 deletions src/tests/indices.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,35 @@ fn create_indices_with_new() {
assert_eq!(indices.right, 1);
}

#[yare::parameterized(
one_to_ten = { input_1_to_10, (0, 9) },
one = { input_1, (0, 0) },
)]
fn create_starting_indices_try_from_bisector(
input: fn() -> Vec<u32>,
indices_expected: (usize, usize),
) {
let values = input();
let bisector = Bisector::new(&values);

let indices = Indices::try_from_bisector(&bisector).unwrap();

let (left_expected, right_expected) = indices_expected;

assert_eq!(indices.left, left_expected);
assert_eq!(indices.right, right_expected);
}

#[test]
fn creating_starting_indices_try_from_bisector_with_empty_slice_should_error() {
let values = input_empty();
let bisector = Bisector::new(&values);

let result = Indices::try_from_bisector(&bisector);

assert_eq!(result.unwrap_err(), EmptySliceError);
}

#[yare::parameterized(
zeros = { 0, 0, 0 },
zero_one = { 0, 1, 0 },
Expand Down

0 comments on commit b77e615

Please sign in to comment.