Skip to content
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

Minimal solution for infinite loop #1395

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
78 changes: 74 additions & 4 deletions src/base/default_allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,13 @@ impl<T: Scalar, C: Dim> Allocator<T, Dyn, C> for DefaultAllocator {
ncols: C,
iter: I,
) -> Self::Buffer {
let it = iter.into_iter();
let size = nrows.value() * ncols.value();
// This solution is not too elegant and meant to prevent an infinite
// loop for infinite iterators and trigger the assertion instead. See
// the tests below for more details.
let it = iter.into_iter().take(size + 1);
let res: Vec<T> = it.collect();
assert!(res.len() == nrows.value() * ncols.value(),
assert!(res.len() == size,
"Allocation from iterator error: the iterator did not yield the correct number of elements.");

VecStorage::new(nrows, ncols, res)
Expand Down Expand Up @@ -167,9 +171,13 @@ impl<T: Scalar, R: DimName> Allocator<T, R, Dyn> for DefaultAllocator {
ncols: Dyn,
iter: I,
) -> Self::Buffer {
let it = iter.into_iter();
let size = nrows.value() * ncols.value();
// This solution is not too elegant and meant to prevent an infinite
// loop for infinite iterators and trigger the assertion instead. See
// the tests below for more details.
let it = iter.into_iter().take(size + 1);
let res: Vec<T> = it.collect();
assert!(res.len() == nrows.value() * ncols.value(),
assert!(res.len() == size,
"Allocation from iterator error: the iterator did not yield the correct number of elements.");

VecStorage::new(nrows, ncols, res)
Expand Down Expand Up @@ -333,3 +341,65 @@ impl<T: Scalar, RFrom: DimName, RTo: DimName> Reallocator<T, RFrom, Dyn, RTo, Dy
VecStorage::new(rto, cto, new_buf)
}
}

#[cfg(test)]
mod tests {

/// There was a bug where infinite iterators would be collected and
/// therefore cause infinite loops for dynamic storage. This tests that
/// this issue is solved and that this will panic instead as for the finite
/// case. This is kind of the minimal solution, which doesn't break
/// backwards compatibility.
///
/// The current situation is:
///
/// - Vec, Iterator or slice to Const storage:
/// - If too small, panic
/// - If too large, ignore remaining entries
/// - Vec, Iterator or slice to Dyn storage:
/// - If too small, panic
/// - If too large, panic
///
// TODO: Decide on consistent and predictable behaviour for these cases.
mod panic_cases {
use core::iter;

use crate::{allocator::Allocator, Const, DefaultAllocator, Dyn};

#[test]
#[should_panic(
message = "Allocation from iterator error: the iterator did not yield the correct number of elements."
)]
fn test_allocation_const_dyn() {
let _ = DefaultAllocator::allocate_from_iterator(
Const::<100>,
Dyn(100),
iter::from_fn(|| Some(0)),
);
}

#[test]
#[should_panic(
message = "Allocation from iterator error: the iterator did not yield the correct number of elements."
)]
fn test_allocation_dyn_const() {
let _ = DefaultAllocator::allocate_from_iterator(
Dyn(50),
Const::<50>,
iter::from_fn(|| Some(0)),
);
}

#[test]
#[should_panic(
message = "Allocation from iterator error: the iterator did not yield the correct number of elements."
)]
fn test_allocation_dyn_dyn() {
let _ = DefaultAllocator::allocate_from_iterator(
Dyn(10),
Dyn(10),
iter::from_fn(|| Some(0)),
);
}
}
}