Skip to main content

smoltcp/storage/
assembler.rs

1use core::fmt;
2
3use crate::config::ASSEMBLER_MAX_SEGMENT_COUNT;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub struct TooManyHolesError;
7
8impl fmt::Display for TooManyHolesError {
9    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
10        write!(f, "too many holes")
11    }
12}
13
14impl core::error::Error for TooManyHolesError {}
15
16/// A contiguous chunk of absent data, followed by a contiguous chunk of present data.
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18struct Contig {
19    hole_size: usize,
20    data_size: usize,
21}
22
23impl fmt::Display for Contig {
24    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
25        if self.has_hole() {
26            write!(f, "({})", self.hole_size)?;
27        }
28        if self.has_hole() && self.has_data() {
29            write!(f, " ")?;
30        }
31        if self.has_data() {
32            write!(f, "{}", self.data_size)?;
33        }
34        Ok(())
35    }
36}
37
38#[cfg(feature = "defmt")]
39impl defmt::Format for Contig {
40    fn format(&self, fmt: defmt::Formatter) {
41        if self.has_hole() {
42            defmt::write!(fmt, "({})", self.hole_size);
43        }
44        if self.has_hole() && self.has_data() {
45            defmt::write!(fmt, " ");
46        }
47        if self.has_data() {
48            defmt::write!(fmt, "{}", self.data_size);
49        }
50    }
51}
52
53impl Contig {
54    const fn empty() -> Contig {
55        Contig {
56            hole_size: 0,
57            data_size: 0,
58        }
59    }
60
61    fn hole_and_data(hole_size: usize, data_size: usize) -> Contig {
62        Contig {
63            hole_size,
64            data_size,
65        }
66    }
67
68    fn has_hole(&self) -> bool {
69        self.hole_size != 0
70    }
71
72    fn has_data(&self) -> bool {
73        self.data_size != 0
74    }
75
76    fn total_size(&self) -> usize {
77        self.hole_size + self.data_size
78    }
79
80    fn shrink_hole_by(&mut self, size: usize) {
81        self.hole_size -= size;
82    }
83
84    fn shrink_hole_to(&mut self, size: usize) {
85        debug_assert!(self.hole_size >= size);
86
87        let total_size = self.total_size();
88        self.hole_size = size;
89        self.data_size = total_size - size;
90    }
91}
92
93/// A buffer (re)assembler.
94///
95/// Currently, up to a hardcoded limit of 4 or 32 holes can be tracked in the buffer.
96#[derive(Debug, PartialEq, Eq, Clone)]
97pub struct Assembler {
98    contigs: [Contig; ASSEMBLER_MAX_SEGMENT_COUNT],
99}
100
101impl fmt::Display for Assembler {
102    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
103        write!(f, "[ ")?;
104        for contig in self.contigs.iter() {
105            if !contig.has_data() {
106                break;
107            }
108            write!(f, "{contig} ")?;
109        }
110        write!(f, "]")?;
111        Ok(())
112    }
113}
114
115#[cfg(feature = "defmt")]
116impl defmt::Format for Assembler {
117    fn format(&self, fmt: defmt::Formatter) {
118        defmt::write!(fmt, "[ ");
119        for contig in self.contigs.iter() {
120            if !contig.has_data() {
121                break;
122            }
123            defmt::write!(fmt, "{} ", contig);
124        }
125        defmt::write!(fmt, "]");
126    }
127}
128
129// Invariant on Assembler::contigs:
130// - There's an index `i` where all contigs before have data, and all contigs after don't (are unused).
131// - All contigs with data must have hole_size != 0, except the first.
132
133impl Assembler {
134    /// Create a new buffer assembler.
135    pub const fn new() -> Assembler {
136        const EMPTY: Contig = Contig::empty();
137        Assembler {
138            contigs: [EMPTY; ASSEMBLER_MAX_SEGMENT_COUNT],
139        }
140    }
141
142    pub fn clear(&mut self) {
143        self.contigs.fill(Contig::empty());
144    }
145
146    fn front(&self) -> Contig {
147        self.contigs[0]
148    }
149
150    /// Return length of the front contiguous range without removing it from the assembler
151    pub fn peek_front(&self) -> usize {
152        let front = self.front();
153        if front.has_hole() { 0 } else { front.data_size }
154    }
155
156    fn back(&self) -> Contig {
157        self.contigs[self.contigs.len() - 1]
158    }
159
160    /// Return whether the assembler contains no data.
161    pub fn is_empty(&self) -> bool {
162        !self.front().has_data()
163    }
164
165    /// Remove a contig at the given index.
166    fn remove_contig_at(&mut self, at: usize) {
167        debug_assert!(self.contigs[at].has_data());
168
169        for i in at..self.contigs.len() - 1 {
170            if !self.contigs[i].has_data() {
171                return;
172            }
173            self.contigs[i] = self.contigs[i + 1];
174        }
175
176        // Removing the last one.
177        self.contigs[self.contigs.len() - 1] = Contig::empty();
178    }
179
180    /// Add a contig at the given index, and return a pointer to it.
181    fn add_contig_at(&mut self, at: usize) -> Result<&mut Contig, TooManyHolesError> {
182        if self.back().has_data() {
183            return Err(TooManyHolesError);
184        }
185
186        for i in (at + 1..self.contigs.len()).rev() {
187            self.contigs[i] = self.contigs[i - 1];
188        }
189
190        self.contigs[at] = Contig::empty();
191        Ok(&mut self.contigs[at])
192    }
193
194    /// Add a new contiguous range to the assembler,
195    /// or return `Err(TooManyHolesError)` if too many discontinuities are already recorded.
196    pub fn add(&mut self, mut offset: usize, size: usize) -> Result<(), TooManyHolesError> {
197        if size == 0 {
198            return Ok(());
199        }
200
201        let mut i = 0;
202
203        // Find index of the contig containing the start of the range.
204        loop {
205            if i == self.contigs.len() {
206                // The new range is after all the previous ranges, but there/s no space to add it.
207                return Err(TooManyHolesError);
208            }
209            let contig = &mut self.contigs[i];
210            if !contig.has_data() {
211                // The new range is after all the previous ranges. Add it.
212                *contig = Contig::hole_and_data(offset, size);
213                return Ok(());
214            }
215            if offset <= contig.total_size() {
216                break;
217            }
218            offset -= contig.total_size();
219            i += 1;
220        }
221
222        let contig = &mut self.contigs[i];
223        if offset < contig.hole_size {
224            // Range starts within the hole.
225
226            if offset + size < contig.hole_size {
227                // Range also ends within the hole.
228                let new_contig = self.add_contig_at(i)?;
229                new_contig.hole_size = offset;
230                new_contig.data_size = size;
231
232                // Previous contigs[index] got moved to contigs[index+1]
233                self.contigs[i + 1].shrink_hole_by(offset + size);
234                return Ok(());
235            }
236
237            // The range being added covers both a part of the hole and a part of the data
238            // in this contig, shrink the hole in this contig.
239            contig.shrink_hole_to(offset);
240        }
241
242        // coalesce contigs to the right.
243        let mut j = i + 1;
244        while j < self.contigs.len()
245            && self.contigs[j].has_data()
246            && offset + size >= self.contigs[i].total_size() + self.contigs[j].hole_size
247        {
248            self.contigs[i].data_size += self.contigs[j].total_size();
249            j += 1;
250        }
251        let shift = j - i - 1;
252        if shift != 0 {
253            for x in i + 1..self.contigs.len() {
254                if !self.contigs[x].has_data() {
255                    break;
256                }
257
258                self.contigs[x] = self
259                    .contigs
260                    .get(x + shift)
261                    .copied()
262                    .unwrap_or_else(Contig::empty);
263            }
264        }
265
266        if offset + size > self.contigs[i].total_size() {
267            // The added range still extends beyond the current contig. Increase data size.
268            let left = offset + size - self.contigs[i].total_size();
269            self.contigs[i].data_size += left;
270
271            // Decrease hole size of the next, if any.
272            if i + 1 < self.contigs.len() && self.contigs[i + 1].has_data() {
273                self.contigs[i + 1].hole_size -= left;
274            }
275        }
276
277        Ok(())
278    }
279
280    /// Remove a contiguous range from the front of the assembler.
281    /// If no such range, return 0.
282    pub fn remove_front(&mut self) -> usize {
283        let front = self.front();
284        if front.has_hole() || !front.has_data() {
285            0
286        } else {
287            self.remove_contig_at(0);
288            debug_assert!(front.data_size > 0);
289            front.data_size
290        }
291    }
292
293    /// Add a segment, then remove_front.
294    ///
295    /// This is equivalent to calling `add` then `remove_front` individually,
296    /// except it's guaranteed to not fail when offset = 0.
297    /// This is required for TCP: we must never drop the next expected segment, or
298    /// the protocol might get stuck.
299    pub fn add_then_remove_front(
300        &mut self,
301        offset: usize,
302        size: usize,
303    ) -> Result<usize, TooManyHolesError> {
304        // This is the only case where a segment at offset=0 would cause the
305        // total amount of contigs to rise (and therefore can potentially cause
306        // a TooManyHolesError). Handle it in a way that is guaranteed to succeed.
307        if offset == 0 && size < self.contigs[0].hole_size {
308            self.contigs[0].hole_size -= size;
309            return Ok(size);
310        }
311
312        self.add(offset, size)?;
313        Ok(self.remove_front())
314    }
315
316    /// Iterate over all of the contiguous data ranges.
317    ///
318    /// Returns `(offset, size)` tuples for each contiguous data range, where
319    /// offset is relative to the start of the assembler.
320    ///
321    ///    Data        Hole        Data
322    /// |--- 100 ---|--- 200 ---|--- 100 ---|
323    ///
324    /// Would return the ranges: ``(0, 100), (300, 400)``
325    pub fn iter_data(&self) -> impl Iterator<Item = (usize, usize)> + '_ {
326        let mut offset = 0;
327        self.contigs.iter().filter_map(move |contig| {
328            offset += contig.hole_size;
329            let left = offset;
330            offset += contig.data_size;
331            let right = offset;
332            if left < right {
333                Some((left, right))
334            } else {
335                None
336            }
337        })
338    }
339}
340
341#[cfg(test)]
342mod test {
343    use super::*;
344    use std::vec::Vec;
345
346    impl From<Vec<(usize, usize)>> for Assembler {
347        fn from(vec: Vec<(usize, usize)>) -> Assembler {
348            const EMPTY: Contig = Contig::empty();
349
350            let mut contigs = [EMPTY; ASSEMBLER_MAX_SEGMENT_COUNT];
351            for (i, &(hole_size, data_size)) in vec.iter().enumerate() {
352                contigs[i] = Contig {
353                    hole_size,
354                    data_size,
355                };
356            }
357            Assembler { contigs }
358        }
359    }
360
361    macro_rules! contigs {
362        [$( $x:expr ),*] => ({
363            Assembler::from(vec![$( $x ),*])
364        })
365    }
366
367    #[test]
368    fn test_new() {
369        let assr = Assembler::new();
370        assert_eq!(assr, contigs![]);
371    }
372
373    #[test]
374    fn test_empty_add_full() {
375        let mut assr = Assembler::new();
376        assert_eq!(assr.add(0, 16), Ok(()));
377        assert_eq!(assr, contigs![(0, 16)]);
378    }
379
380    #[test]
381    fn test_empty_add_front() {
382        let mut assr = Assembler::new();
383        assert_eq!(assr.add(0, 4), Ok(()));
384        assert_eq!(assr, contigs![(0, 4)]);
385    }
386
387    #[test]
388    fn test_empty_add_back() {
389        let mut assr = Assembler::new();
390        assert_eq!(assr.add(12, 4), Ok(()));
391        assert_eq!(assr, contigs![(12, 4)]);
392    }
393
394    #[test]
395    fn test_empty_add_mid() {
396        let mut assr = Assembler::new();
397        assert_eq!(assr.add(4, 8), Ok(()));
398        assert_eq!(assr, contigs![(4, 8)]);
399    }
400
401    #[test]
402    fn test_partial_add_front() {
403        let mut assr = contigs![(4, 8)];
404        assert_eq!(assr.add(0, 4), Ok(()));
405        assert_eq!(assr, contigs![(0, 12)]);
406    }
407
408    #[test]
409    fn test_partial_add_back() {
410        let mut assr = contigs![(4, 8)];
411        assert_eq!(assr.add(12, 4), Ok(()));
412        assert_eq!(assr, contigs![(4, 12)]);
413    }
414
415    #[test]
416    fn test_partial_add_front_overlap() {
417        let mut assr = contigs![(4, 8)];
418        assert_eq!(assr.add(0, 8), Ok(()));
419        assert_eq!(assr, contigs![(0, 12)]);
420    }
421
422    #[test]
423    fn test_partial_add_front_overlap_split() {
424        let mut assr = contigs![(4, 8)];
425        assert_eq!(assr.add(2, 6), Ok(()));
426        assert_eq!(assr, contigs![(2, 10)]);
427    }
428
429    #[test]
430    fn test_partial_add_back_overlap() {
431        let mut assr = contigs![(4, 8)];
432        assert_eq!(assr.add(8, 8), Ok(()));
433        assert_eq!(assr, contigs![(4, 12)]);
434    }
435
436    #[test]
437    fn test_partial_add_back_overlap_split() {
438        let mut assr = contigs![(4, 8)];
439        assert_eq!(assr.add(10, 4), Ok(()));
440        assert_eq!(assr, contigs![(4, 10)]);
441    }
442
443    #[test]
444    fn test_partial_add_both_overlap() {
445        let mut assr = contigs![(4, 8)];
446        assert_eq!(assr.add(0, 16), Ok(()));
447        assert_eq!(assr, contigs![(0, 16)]);
448    }
449
450    #[test]
451    fn test_partial_add_both_overlap_split() {
452        let mut assr = contigs![(4, 8)];
453        assert_eq!(assr.add(2, 12), Ok(()));
454        assert_eq!(assr, contigs![(2, 12)]);
455    }
456
457    #[test]
458    fn test_rejected_add_keeps_state() {
459        let mut assr = Assembler::new();
460        for c in 1..=ASSEMBLER_MAX_SEGMENT_COUNT {
461            assert_eq!(assr.add(c * 10, 3), Ok(()));
462        }
463        // Maximum of allowed holes is reached
464        let assr_before = assr.clone();
465        assert_eq!(assr.add(1, 3), Err(TooManyHolesError));
466        assert_eq!(assr_before, assr);
467    }
468
469    #[test]
470    fn test_empty_remove_front() {
471        let mut assr = contigs![];
472        assert_eq!(assr.remove_front(), 0);
473    }
474
475    #[test]
476    fn test_trailing_hole_remove_front() {
477        let mut assr = contigs![(0, 4)];
478        assert_eq!(assr.remove_front(), 4);
479        assert_eq!(assr, contigs![]);
480    }
481
482    #[test]
483    fn test_trailing_data_remove_front() {
484        let mut assr = contigs![(0, 4), (4, 4)];
485        assert_eq!(assr.remove_front(), 4);
486        assert_eq!(assr, contigs![(4, 4)]);
487    }
488
489    #[test]
490    fn test_boundary_case_remove_front() {
491        let mut vec = vec![(1, 1); ASSEMBLER_MAX_SEGMENT_COUNT];
492        vec[0] = (0, 2);
493        let mut assr = Assembler::from(vec);
494        assert_eq!(assr.remove_front(), 2);
495        let mut vec = vec![(1, 1); ASSEMBLER_MAX_SEGMENT_COUNT];
496        vec[ASSEMBLER_MAX_SEGMENT_COUNT - 1] = (0, 0);
497        let exp_assr = Assembler::from(vec);
498        assert_eq!(assr, exp_assr);
499    }
500
501    #[test]
502    fn test_shrink_next_hole() {
503        let mut assr = Assembler::new();
504        assert_eq!(assr.add(100, 10), Ok(()));
505        assert_eq!(assr.add(50, 10), Ok(()));
506        assert_eq!(assr.add(40, 30), Ok(()));
507        assert_eq!(assr, contigs![(40, 30), (30, 10)]);
508    }
509
510    #[test]
511    fn test_join_two() {
512        let mut assr = Assembler::new();
513        assert_eq!(assr.add(10, 10), Ok(()));
514        assert_eq!(assr.add(50, 10), Ok(()));
515        assert_eq!(assr.add(15, 40), Ok(()));
516        assert_eq!(assr, contigs![(10, 50)]);
517    }
518
519    #[test]
520    fn test_join_two_reversed() {
521        let mut assr = Assembler::new();
522        assert_eq!(assr.add(50, 10), Ok(()));
523        assert_eq!(assr.add(10, 10), Ok(()));
524        assert_eq!(assr.add(15, 40), Ok(()));
525        assert_eq!(assr, contigs![(10, 50)]);
526    }
527
528    #[test]
529    fn test_join_two_overlong() {
530        let mut assr = Assembler::new();
531        assert_eq!(assr.add(50, 10), Ok(()));
532        assert_eq!(assr.add(10, 10), Ok(()));
533        assert_eq!(assr.add(15, 60), Ok(()));
534        assert_eq!(assr, contigs![(10, 65)]);
535    }
536
537    #[test]
538    fn test_iter_empty() {
539        let assr = Assembler::new();
540        let segments: Vec<_> = assr.iter_data().collect();
541        assert_eq!(segments, vec![]);
542    }
543
544    #[test]
545    fn test_iter_full() {
546        let mut assr = Assembler::new();
547        assert_eq!(assr.add(0, 16), Ok(()));
548        let segments: Vec<_> = assr.iter_data().collect();
549        assert_eq!(segments, vec![(0, 16)]);
550    }
551
552    #[test]
553    fn test_iter_one_front() {
554        let mut assr = Assembler::new();
555        assert_eq!(assr.add(0, 4), Ok(()));
556        let segments: Vec<_> = assr.iter_data().collect();
557        assert_eq!(segments, vec![(0, 4)]);
558    }
559
560    #[test]
561    fn test_iter_one_back() {
562        let mut assr = Assembler::new();
563        assert_eq!(assr.add(12, 4), Ok(()));
564        let segments: Vec<_> = assr.iter_data().collect();
565        assert_eq!(segments, vec![(12, 16)]);
566    }
567
568    #[test]
569    fn test_iter_one_mid() {
570        let mut assr = Assembler::new();
571        assert_eq!(assr.add(4, 8), Ok(()));
572        let segments: Vec<_> = assr.iter_data().collect();
573        assert_eq!(segments, vec![(4, 12)]);
574    }
575
576    #[test]
577    fn test_iter_one_trailing_gap() {
578        let assr = contigs![(4, 8)];
579        let segments: Vec<_> = assr.iter_data().collect();
580        assert_eq!(segments, vec![(4, 12)]);
581    }
582
583    #[test]
584    fn test_iter_two_split() {
585        let assr = contigs![(2, 6), (4, 1)];
586        let segments: Vec<_> = assr.iter_data().collect();
587        assert_eq!(segments, vec![(2, 8), (12, 13)]);
588    }
589
590    #[test]
591    fn test_iter_three_split() {
592        let assr = contigs![(2, 6), (2, 1), (2, 2)];
593        let segments: Vec<_> = assr.iter_data().collect();
594        assert_eq!(segments, vec![(2, 8), (10, 11), (13, 15)]);
595    }
596
597    #[test]
598    fn test_issue_694() {
599        let mut assr = Assembler::new();
600        assert_eq!(assr.add(0, 1), Ok(()));
601        assert_eq!(assr.add(2, 1), Ok(()));
602        assert_eq!(assr.add(1, 1), Ok(()));
603    }
604
605    #[test]
606    fn test_add_then_remove_front() {
607        let mut assr = Assembler::new();
608        assert_eq!(assr.add(50, 10), Ok(()));
609        assert_eq!(assr.add_then_remove_front(10, 10), Ok(0));
610        assert_eq!(assr, contigs![(10, 10), (30, 10)]);
611    }
612
613    #[test]
614    fn test_add_then_remove_front_at_front() {
615        let mut assr = Assembler::new();
616        assert_eq!(assr.add(50, 10), Ok(()));
617        assert_eq!(assr.add_then_remove_front(0, 10), Ok(10));
618        assert_eq!(assr, contigs![(40, 10)]);
619    }
620
621    #[test]
622    fn test_add_then_remove_front_at_front_touch() {
623        let mut assr = Assembler::new();
624        assert_eq!(assr.add(50, 10), Ok(()));
625        assert_eq!(assr.add_then_remove_front(0, 50), Ok(60));
626        assert_eq!(assr, contigs![]);
627    }
628
629    #[test]
630    fn test_add_then_remove_front_at_front_full() {
631        let mut assr = Assembler::new();
632        for c in 1..=ASSEMBLER_MAX_SEGMENT_COUNT {
633            assert_eq!(assr.add(c * 10, 3), Ok(()));
634        }
635        // Maximum of allowed holes is reached
636        let assr_before = assr.clone();
637        assert_eq!(assr.add_then_remove_front(1, 3), Err(TooManyHolesError));
638        assert_eq!(assr_before, assr);
639    }
640
641    #[test]
642    fn test_add_then_remove_front_at_front_full_offset_0() {
643        let mut assr = Assembler::new();
644        for c in 1..=ASSEMBLER_MAX_SEGMENT_COUNT {
645            assert_eq!(assr.add(c * 10, 3), Ok(()));
646        }
647        assert_eq!(assr.add_then_remove_front(0, 3), Ok(3));
648    }
649
650    // Test against an obviously-correct but inefficient bitmap impl.
651    #[test]
652    fn test_random() {
653        use rand::Rng;
654
655        const MAX_INDEX: usize = 256;
656
657        for max_size in [2, 5, 10, 100] {
658            for _ in 0..300 {
659                //println!("===");
660                let mut assr = Assembler::new();
661                let mut map = [false; MAX_INDEX];
662
663                for _ in 0..60 {
664                    let offset = rand::thread_rng().gen_range(0..MAX_INDEX - max_size - 1);
665                    let size = rand::thread_rng().gen_range(1..=max_size);
666
667                    //println!("add {}..{} {}", offset, offset + size, size);
668                    // Real impl
669                    let res = assr.add(offset, size);
670
671                    // Bitmap impl
672                    let mut map2 = map;
673                    map2[offset..][..size].fill(true);
674
675                    let mut contigs = vec![];
676                    let mut hole: usize = 0;
677                    let mut data: usize = 0;
678                    for b in map2 {
679                        if b {
680                            data += 1;
681                        } else {
682                            if data != 0 {
683                                contigs.push((hole, data));
684                                hole = 0;
685                                data = 0;
686                            }
687                            hole += 1;
688                        }
689                    }
690
691                    // Compare.
692                    let wanted_res = if contigs.len() > ASSEMBLER_MAX_SEGMENT_COUNT {
693                        Err(TooManyHolesError)
694                    } else {
695                        Ok(())
696                    };
697                    assert_eq!(res, wanted_res);
698                    if res.is_ok() {
699                        map = map2;
700                        assert_eq!(assr, Assembler::from(contigs));
701                    }
702                }
703            }
704        }
705    }
706}