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}