Skip to main content

taffy/util/
front_back_vec_builder.rs

1//! A fixed-capacity `Vec` builder that can be filled from both ends
2use crate::util::sys::Vec;
3
4/// Builds a `Vec` of a fixed length that is known up front by pushing items to either its front or its back.
5///
6/// Items pushed to the front end up at the start of the `Vec` in the order that they were pushed. Items pushed to the
7/// back end up at the end of the `Vec` in the reverse of the order that they were pushed. This allows a list of items
8/// to be partitioned into two groups in a single pass and with a single allocation.
9///
10/// Exactly `capacity` items must be pushed before calling [`into_vec`](Self::into_vec), which panics otherwise.
11/// If the `FrontBackVecBuilder` is dropped before then (or if too many items are pushed), then items that were
12/// pushed to it are leaked rather than dropped.
13pub(crate) struct FrontBackVecBuilder<T> {
14    /// The underlying storage. Its length is kept at zero until `into_vec` is called, and its allocated capacity
15    /// is at least `capacity`.
16    vec: Vec<T>,
17    /// The total number of items that the `Vec` will contain
18    capacity: usize,
19    /// The number of items pushed to the front. The slots `0..front_len` are initialized.
20    front_len: usize,
21    /// The number of items pushed to the back. The slots `(capacity - back_len)..capacity` are initialized.
22    back_len: usize,
23}
24
25impl<T> FrontBackVecBuilder<T> {
26    /// Create a new `FrontBackVecBuilder` that will contain exactly `capacity` items
27    #[inline(always)]
28    pub(crate) fn with_capacity(capacity: usize) -> Self {
29        Self { vec: Vec::with_capacity(capacity), capacity, front_len: 0, back_len: 0 }
30    }
31
32    /// Add an item after the items that have already been pushed to the front.
33    ///
34    /// Pushing more than `capacity` items in total is not checked here: it either panics or overwrites (and leaks)
35    /// a previously pushed item, and then causes [`into_vec`](Self::into_vec) to panic.
36    #[inline(always)]
37    pub(crate) fn push_front(&mut self, value: T) {
38        self.vec.spare_capacity_mut()[self.front_len].write(value);
39        self.front_len += 1;
40    }
41
42    /// Add an item before the items that have already been pushed to the back.
43    ///
44    /// Pushing more than `capacity` items in total is not checked here: it either panics or overwrites (and leaks)
45    /// a previously pushed item, and then causes [`into_vec`](Self::into_vec) to panic.
46    #[inline(always)]
47    pub(crate) fn push_back(&mut self, value: T) {
48        self.vec.spare_capacity_mut()[self.capacity - self.back_len - 1].write(value);
49        self.back_len += 1;
50    }
51
52    /// Returns the completed `Vec`, along with the number of items that were pushed to the front (which is also
53    /// the index of the first item that was pushed to the back).
54    ///
55    /// Panics if the total number of items that have been pushed is not exactly `capacity`.
56    #[inline(always)]
57    pub(crate) fn into_vec(self) -> (Vec<T>, usize) {
58        let Self { mut vec, capacity, front_len, back_len } = self;
59        assert_eq!(
60            front_len + back_len,
61            capacity,
62            "the number of items pushed to a FrontBackVecBuilder must equal its capacity"
63        );
64        // SAFETY:
65        //   - `vec` was allocated with a capacity of at least `capacity` and has not been reallocated since.
66        //   - Each `push_front` initializes the slot at index `front_len` and then increments `front_len`, so the
67        //     slots `0..front_len` are initialized. Likewise each `push_back` initializes the slot just before the
68        //     ones it has already initialized, so the slots `(capacity - back_len)..capacity` are initialized.
69        //     (These writes are bounds checked, and writing to a slot that is already initialized is safe).
70        //   - `front_len + back_len == capacity` (asserted above), so `capacity - back_len == front_len` and those
71        //     two ranges cover all of `0..capacity`.
72        #[allow(unsafe_code)]
73        unsafe {
74            vec.set_len(capacity);
75        }
76        (vec, front_len)
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::FrontBackVecBuilder;
83    use crate::util::sys::Vec;
84
85    #[test]
86    fn front_items_keep_their_order_and_back_items_are_reversed() {
87        let mut builder = FrontBackVecBuilder::with_capacity(6);
88        for value in 0..6 {
89            if value % 2 == 0 {
90                builder.push_front(value);
91            } else {
92                builder.push_back(value);
93            }
94        }
95        let (vec, front_len) = builder.into_vec();
96        assert_eq!(vec, [0, 2, 4, 5, 3, 1]);
97        assert_eq!(front_len, 3);
98    }
99
100    #[test]
101    fn all_items_pushed_to_one_end() {
102        let mut builder = FrontBackVecBuilder::with_capacity(3);
103        (0..3).for_each(|value| builder.push_front(value));
104        assert_eq!(builder.into_vec(), (Vec::from([0, 1, 2]), 3));
105
106        let mut builder = FrontBackVecBuilder::with_capacity(3);
107        (0..3).for_each(|value| builder.push_back(value));
108        assert_eq!(builder.into_vec(), (Vec::from([2, 1, 0]), 0));
109    }
110
111    #[test]
112    fn zero_capacity() {
113        let (vec, front_len) = FrontBackVecBuilder::<u8>::with_capacity(0).into_vec();
114        assert!(vec.is_empty());
115        assert_eq!(front_len, 0);
116    }
117
118    #[test]
119    fn owned_items_are_moved_into_the_vec() {
120        let mut builder = FrontBackVecBuilder::with_capacity(2);
121        builder.push_back(Vec::from([1, 2]));
122        builder.push_front(Vec::from([3]));
123        let (vec, front_len) = builder.into_vec();
124        assert_eq!(vec, [Vec::from([3]), Vec::from([1, 2])]);
125        assert_eq!(front_len, 1);
126    }
127
128    #[test]
129    #[should_panic(expected = "must equal its capacity")]
130    fn pushing_too_many_items_to_the_front_panics_in_into_vec() {
131        let mut builder = FrontBackVecBuilder::with_capacity(1);
132        builder.push_back(1);
133        builder.push_front(2);
134        let _ = builder.into_vec();
135    }
136
137    #[test]
138    #[should_panic(expected = "must equal its capacity")]
139    fn pushing_too_many_items_to_the_back_panics_in_into_vec() {
140        let mut builder = FrontBackVecBuilder::with_capacity(1);
141        builder.push_front(1);
142        builder.push_back(2);
143        let _ = builder.into_vec();
144    }
145
146    #[test]
147    #[should_panic]
148    fn push_front_out_of_bounds_panics() {
149        let mut builder = FrontBackVecBuilder::with_capacity(0);
150        builder.push_front(1);
151    }
152
153    #[test]
154    #[should_panic]
155    fn push_back_out_of_bounds_panics() {
156        let mut builder = FrontBackVecBuilder::with_capacity(1);
157        builder.push_back(1);
158        builder.push_back(2);
159    }
160
161    #[test]
162    #[should_panic(expected = "must equal its capacity")]
163    fn into_vec_before_full_panics() {
164        let mut builder = FrontBackVecBuilder::with_capacity(2);
165        builder.push_front(1);
166        let _ = builder.into_vec();
167    }
168}