treams_core/numerics/
memory.rs

1//! Allocations that return [`Error::OutOfMemory`] when the system refuses them, for
2//! outputs and workspaces that grow with the problem. treams-rs extension.
3//!
4//! `vec!`, `Vec::with_capacity` and `DMatrix::zeros` call `handle_alloc_error` on a
5//! refused request, which aborts the process, and panic when the byte count overflows.
6//! These helpers reserve with `Vec::try_reserve_exact` instead and report the request
7//! in bytes. On Linux with memory overcommit a reservation larger than the free memory
8//! can still succeed, and the kernel may kill the process later when the pages are
9//! written; no allocation API reports that.
10
11use nalgebra::DMatrix;
12
13use crate::{Complex, Error, Result};
14
15/// A zero `rows × cols` matrix.
16pub(crate) fn zeros(rows: usize, cols: usize) -> Result<DMatrix<Complex>> {
17    let len = rows
18        .checked_mul(cols)
19        .ok_or_else(|| Error::out_of_memory(usize::MAX, size_of::<Complex>()))?;
20    Ok(DMatrix::from_vec(
21        rows,
22        cols,
23        filled(len, Complex::default())?,
24    ))
25}
26
27/// `len` copies of `value`.
28pub(crate) fn filled<T: Clone>(len: usize, value: T) -> Result<Vec<T>> {
29    let mut values = Vec::new();
30    reserve(&mut values, len)?;
31    values.resize(len, value);
32    Ok(values)
33}
34
35/// Reserve room for exactly `additional` more elements in `values`.
36pub(crate) fn reserve<T>(values: &mut Vec<T>, additional: usize) -> Result<()> {
37    values
38        .try_reserve_exact(additional)
39        .map_err(|_| Error::out_of_memory(values.len().saturating_add(additional), size_of::<T>()))
40}
41
42/// Refused, overflowing and ordinary requests.
43#[cfg(test)]
44mod tests {
45    use super::{filled, reserve, zeros};
46    use crate::Error;
47
48    #[test]
49    fn requests_beyond_the_address_space_return_out_of_memory() {
50        // 2^48 elements of 16 bytes: 2^52 bytes, beyond the user address space of
51        // every supported operating system.
52        let error = zeros(1 << 24, 1 << 24).unwrap_err();
53        assert!(
54            matches!(&error, Error::OutOfMemory(message) if message == "cannot allocate 4503599627370496 bytes"),
55            "{error:?}"
56        );
57        assert!(!error.is_numerical());
58        let mut values = vec![0_u8; 3];
59        assert!(matches!(
60            reserve(&mut values, 1 << 52),
61            Err(Error::OutOfMemory(_))
62        ));
63        assert_eq!(values, [0, 0, 0]);
64    }
65
66    #[test]
67    fn overflowing_requests_return_out_of_memory_without_panicking() {
68        let more = format!("cannot allocate more than {} bytes", usize::MAX);
69        for (rows, cols) in [(usize::MAX, 1), (usize::MAX, 2), (1 << 33, 1 << 33)] {
70            let error = zeros(rows, cols).unwrap_err();
71            assert!(
72                matches!(&error, Error::OutOfMemory(message) if *message == more),
73                "{rows} x {cols}: {error:?}"
74            );
75        }
76        assert!(matches!(
77            filled(usize::MAX, 0.0_f64),
78            Err(Error::OutOfMemory(message)) if message == more
79        ));
80        let mut values = vec![0_u64; 2];
81        assert!(matches!(
82            reserve(&mut values, usize::MAX),
83            Err(Error::OutOfMemory(message)) if message == more
84        ));
85    }
86
87    #[test]
88    fn granted_requests_hold_the_requested_values() {
89        let matrix = zeros(3, 2).unwrap();
90        assert_eq!(matrix.shape(), (3, 2));
91        assert!(matrix.iter().all(|&z| z == crate::Complex::default()));
92        assert_eq!(zeros(0, 7).unwrap().shape(), (0, 7));
93        assert_eq!(filled(4, 1.5).unwrap(), [1.5; 4]);
94        let mut values = vec![1];
95        reserve(&mut values, 5).unwrap();
96        assert!(values.capacity() >= 6);
97    }
98}