Skip to main content

implicit_predicates_core/scratch/
write.rs

1//! Writing an expansion into an arena through a closure.
2
3use super::{Scratch, ScratchExhausted, Span};
4use crate::expansion;
5
6impl Scratch<'_> {
7    /// Lends `capacity` free components to `f`, which writes an expansion
8    /// and returns its length; the top is left right after that length.
9    ///
10    /// This is how code outside the arena writes a result of its own, such
11    /// as the exact coordinates of a point, without building it in a
12    /// separate buffer first. `f` receives the `capacity` components just
13    /// above the top, whose contents are unspecified, writes the expansion
14    /// at their start and may use the rest as working space. The returned
15    /// span holds the first components `f` reports; `f` must not report
16    /// more than `capacity`.
17    ///
18    /// Requirement: what `f` writes is a valid expansion (see
19    /// [`expansion`]); debug builds panic if it is not. Needs `capacity`
20    /// free components, which [`high_water`](Self::high_water) counts even
21    /// when the expansion turns out shorter.
22    ///
23    /// # Errors
24    ///
25    /// [`ScratchExhausted`] if `capacity` components are not free; `f` is
26    /// not called and the arena is unchanged.
27    ///
28    /// # Panics
29    ///
30    /// If `f` returns a length greater than `capacity`. The top of the arena
31    /// and the spans below it are then as they were before the call, but
32    /// [`high_water`](Self::high_water) already counts the `capacity`
33    /// components reserved, and a growable arena may already have grown.
34    ///
35    /// # Examples
36    ///
37    /// ```
38    /// use implicit_predicates_core::{Scratch, eft};
39    ///
40    /// let mut buf = [0.0; 4];
41    /// let mut scratch = Scratch::borrowed(&mut buf);
42    /// // The exact sum of 1 and 2^-60, as an expansion of two components.
43    /// let s = scratch.write(2, |out| {
44    ///     let (high, low) = eft::two_sum(1.0, 2f64.powi(-60));
45    ///     out[0] = low;
46    ///     out[1] = high;
47    ///     2
48    /// })?;
49    /// assert_eq!(scratch.get(s), &[2f64.powi(-60), 1.0]);
50    /// # Ok::<(), implicit_predicates_core::ScratchExhausted>(())
51    /// ```
52    pub fn write(
53        &mut self,
54        capacity: usize,
55        f: impl FnOnce(&mut [f64]) -> usize,
56    ) -> Result<Span, ScratchExhausted> {
57        let needed = u64::try_from(capacity).unwrap_or(u64::MAX);
58        self.write_result([], needed, |[], out| {
59            let len = f(out);
60            assert!(
61                len <= capacity,
62                "the closure given to Scratch::write returned a length of {len}, \
63                 more than the capacity of {capacity}"
64            );
65            debug_assert!(
66                expansion::is_valid(&out[..len]),
67                "Scratch::write of {:?}, which is not a valid expansion",
68                &out[..len]
69            );
70            len
71        })
72    }
73}