willow_data_model/path/
builder.rs

1use core::cmp::min;
2use core::mem::size_of;
3
4use bytes::{BufMut, BytesMut};
5use either::Either::*;
6use ufotofu::BulkProducer;
7use ufotofu_codec::{Blame, DecodeError};
8
9use super::{Component, InvalidPathError, Path, Representation};
10
11/// A helper struct for creating a [`Path`] with exactly one memory allocation. Requires total length and component count to be known in advance.
12///
13/// Enforces that each component has a length of at most `MCL` ([**m**ax\_**c**omponent\_**l**ength](https://willowprotocol.org/specs/data-model/index.html#max_component_length)), that each path has at most `MCC` ([**m**ax\_**c**omponent\_**c**count](https://willowprotocol.org/specs/data-model/index.html#max_component_count)) components, and that the total size in bytes of all components is at most `MPL` ([**m**ax\_**p**ath\_**l**ength](https://willowprotocol.org/specs/data-model/index.html#max_path_length)).
14pub struct PathBuilder<const MCL: usize, const MCC: usize, const MPL: usize> {
15    bytes: BytesMut,               // Turns into the [`HeapEncoding`] when building.
16    initialised_components: usize, // How many of the components have been appended to the `bytes` already?
17    target_length: usize,          // The total length that the path should have when building.
18}
19
20impl<const MCL: usize, const MCC: usize, const MPL: usize> PathBuilder<MCL, MCC, MPL> {
21    /// Creates a builder for a path of known total length and component count.
22    /// The component data must be filled in before building.
23    pub fn new(total_length: usize, component_count: usize) -> Result<Self, InvalidPathError> {
24        if total_length > MPL {
25            return Err(InvalidPathError::PathTooLong);
26        }
27
28        if component_count > MCC {
29            return Err(InvalidPathError::TooManyComponents);
30        }
31
32        // Allocate all storage in a single go.
33        let mut buf = BytesMut::with_capacity(Representation::allocation_size(
34            total_length,
35            component_count,
36        ));
37
38        // Place the number of components at the start of the buffer.
39        buf.extend_from_slice(&(component_count.to_ne_bytes())[..]);
40
41        // Fill up the accumulated component lengths with dummy zeroes, so we can use buf.extend_from_slice to append the actual component data in `append_component`. We overwrite the zeroes with actual values in `append_component`.
42        buf.put_bytes(0, component_count * size_of::<usize>());
43
44        Ok(Self {
45            bytes: buf,
46            initialised_components: 0,
47            target_length: total_length,
48        })
49    }
50
51    /// Creates a builder for a path of known total length and component count, efficiently prefilled with the first `prefix_component_count` components of a given `reference` path. Panics if there are not enough components in the `reference`.
52    /// The missing component data must be filled in before building.
53    pub fn new_from_prefix(
54        target_total_length: usize,
55        target_component_count: usize,
56        reference: &Path<MCL, MCC, MPL>,
57        prefix_component_count: usize,
58    ) -> Result<Self, InvalidPathError> {
59        let mut builder = Self::new(target_total_length, target_component_count)?;
60
61        assert!(reference.component_count() >= prefix_component_count);
62
63        // The following is logically equivalent to successively appending the first `prefix_component_count` components of `reference` to the builder, but more efficient in terms of `memcpy` calls.
64        match prefix_component_count.checked_sub(1) {
65            None => return Ok(builder),
66            Some(index_of_final_component) => {
67                // Overwrite the dummy accumulated component lengths for the first `prefix_component_count` components with the actual values.
68                let accumulated_component_lengths_start =
69                    Representation::start_offset_of_sum_of_lengths_for_component(0);
70                let accumulated_component_lengths_end =
71                    Representation::start_offset_of_sum_of_lengths_for_component(
72                        index_of_final_component + 1,
73                    );
74                builder.bytes.as_mut()
75                    [accumulated_component_lengths_start..accumulated_component_lengths_end]
76                    .copy_from_slice(
77                        &reference.data[accumulated_component_lengths_start
78                            ..accumulated_component_lengths_end],
79                    );
80
81                let components_start_in_prefix =
82                    Representation::start_offset_of_component(&reference.data, 0);
83                let components_end_in_prefix = Representation::end_offset_of_component(
84                    &reference.data,
85                    index_of_final_component,
86                );
87                builder.bytes.extend_from_slice(
88                    &reference.data[components_start_in_prefix..components_end_in_prefix],
89                );
90
91                // Record that we added the components.
92                builder.initialised_components = prefix_component_count;
93            }
94        }
95
96        // For reference, here is a naive implementation of the same functionality:
97
98        // for comp in reference
99        //     .create_prefix(prefix_component_count)
100        //     .expect("`prefix_component_count` must be less than the number of components in `reference`")
101        //     .components()
102        // {
103        //     builder.append_component(comp);
104        // }
105
106        Ok(builder)
107    }
108
109    /// Appends data for a component of known length by reading data from a [`BulkProducer`] of bytes. Panics if `component_length > MCL`.
110    pub async fn append_component_from_bulk_producer<P>(
111        &mut self,
112        component_length: usize,
113        p: &mut P,
114    ) -> Result<(), DecodeError<P::Final, P::Error, Blame>>
115    where
116        P: BulkProducer<Item = u8>,
117    {
118        assert!(component_length <= MCL);
119
120        // Compute the accumulated length for the new component.
121        let total_length_so_far = match self.initialised_components.checked_sub(1) {
122            Some(i) => Representation::sum_of_lengths_for_component(&self.bytes, i),
123            None => 0,
124        };
125        let acc_length = component_length + total_length_so_far;
126
127        // Overwrite the dummy accumulated component length for this component with the actual value.
128        let start = Representation::start_offset_of_sum_of_lengths_for_component(
129            self.initialised_components,
130        );
131        let end = start + size_of::<usize>();
132        self.bytes.as_mut()[start..end].copy_from_slice(&acc_length.to_ne_bytes()[..]);
133
134        // Ufotofu prohibits empty slices, so it is easier to handle the case that would require them explicitly.
135        if component_length == 0 {
136            // Record that we added a component.
137            self.initialised_components += 1;
138            return Ok(());
139        }
140
141        // Now, read bytes until we have the full component.
142        let mut produced_so_far = 0;
143        while produced_so_far < component_length {
144            // Get as many bytes from the producer as efficiently possible.
145            match p.expose_items().await? {
146                Right(fin) => return Err(DecodeError::UnexpectedEndOfInput(fin)),
147                Left(data) => {
148                    let remaining_len = min(data.len(), component_length - produced_so_far);
149                    self.bytes.extend_from_slice(&data[..remaining_len]);
150                    p.consider_produced(remaining_len).await?;
151                    produced_so_far += remaining_len;
152                }
153            }
154        }
155
156        // Record that we added a component.
157        self.initialised_components += 1;
158
159        Ok(())
160    }
161
162    /// Appends the data for the next component.
163    pub fn append_component(&mut self, component: Component<MCL>) {
164        // Compute the accumulated length for the new component.
165        let total_length_so_far = match self.initialised_components.checked_sub(1) {
166            Some(i) => Representation::sum_of_lengths_for_component(&self.bytes, i),
167            None => 0,
168        };
169        let acc_length = component.len() + total_length_so_far;
170
171        // Overwrite the dummy accumulated component length for this component with the actual value.
172        let start = Representation::start_offset_of_sum_of_lengths_for_component(
173            self.initialised_components,
174        );
175        let end = start + size_of::<usize>();
176        self.bytes.as_mut()[start..end].copy_from_slice(&acc_length.to_ne_bytes()[..]);
177
178        // Append the component to the path.
179        self.bytes.extend_from_slice(component.as_ref());
180
181        // Record that we added a component.
182        self.initialised_components += 1;
183    }
184
185    /// Turn this builder into an immutable [`Path`].
186    ///
187    /// Panics if the number of components or the total length does not match what was claimed in [`PathBuilder::new`].
188    pub fn build(self) -> Path<MCL, MCC, MPL> {
189        // Check whether we appended the correct number of components.
190        assert_eq!(
191            self.initialised_components,
192            Representation::component_count(self.bytes.as_ref())
193        );
194
195        assert_eq!(
196            self.target_length,
197            Representation::total_length(&self.bytes, self.initialised_components),
198            "Expected a target length of {}, but got an actual total_length of {}\nRaw representation:{:?}",
199            self.target_length,
200            Representation::total_length(&self.bytes, self.initialised_components),
201            self.bytes
202        );
203
204        Path {
205            data: self.bytes.freeze(),
206            component_count: self.initialised_components,
207        }
208    }
209}