yamux/
chunks.rs

1// Copyright (c) 2019 Parity Technologies (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 or MIT license, at your option.
4//
5// A copy of the Apache License, Version 2.0 is included in the software as
6// LICENSE-APACHE and a copy of the MIT license is included in the software
7// as LICENSE-MIT. You may also obtain a copy of the Apache License, Version 2.0
8// at https://www.apache.org/licenses/LICENSE-2.0 and a copy of the MIT license
9// at https://opensource.org/licenses/MIT.
10
11use std::{collections::VecDeque, io};
12
13/// A sequence of [`Chunk`] values.
14///
15/// [`Chunks::len`] considers all [`Chunk`] elements and computes the total
16/// result, i.e. the length of all bytes, by summing up the lengths of all
17/// [`Chunk`] elements.
18#[derive(Debug)]
19pub(crate) struct Chunks {
20    seq: VecDeque<Chunk>,
21    len: usize,
22}
23
24impl Chunks {
25    /// A new empty chunk list.
26    pub(crate) fn new() -> Self {
27        Chunks {
28            seq: VecDeque::new(),
29            len: 0,
30        }
31    }
32
33    /// The total length of bytes yet-to-be-read in all `Chunk`s.
34    pub(crate) fn len(&self) -> usize {
35        self.len - self.seq.front().map(|c| c.offset()).unwrap_or(0)
36    }
37
38    /// Add another chunk of bytes to the end.
39    pub(crate) fn push(&mut self, x: Vec<u8>) {
40        self.len += x.len();
41        if !x.is_empty() {
42            self.seq.push_back(Chunk {
43                cursor: io::Cursor::new(x),
44            })
45        }
46    }
47
48    /// Remove and return the first chunk.
49    pub(crate) fn pop(&mut self) -> Option<Chunk> {
50        let chunk = self.seq.pop_front();
51        self.len -= chunk.as_ref().map(|c| c.len() + c.offset()).unwrap_or(0);
52        chunk
53    }
54
55    /// Get a mutable reference to the first chunk.
56    pub(crate) fn front_mut(&mut self) -> Option<&mut Chunk> {
57        self.seq.front_mut()
58    }
59}
60
61/// A `Chunk` wraps a `std::io::Cursor<Vec<u8>>`.
62///
63/// It provides a byte-slice view and a way to advance the cursor so the
64/// vector can be consumed in steps.
65#[derive(Debug)]
66pub(crate) struct Chunk {
67    cursor: io::Cursor<Vec<u8>>,
68}
69
70impl Chunk {
71    /// Is this chunk empty?
72    pub(crate) fn is_empty(&self) -> bool {
73        self.len() == 0
74    }
75
76    /// The remaining number of bytes in this `Chunk`.
77    pub(crate) fn len(&self) -> usize {
78        self.cursor.get_ref().len() - self.offset()
79    }
80
81    /// The sum of bytes that the cursor has been `advance`d over.
82    pub(crate) fn offset(&self) -> usize {
83        self.cursor.position() as usize
84    }
85
86    /// Move the cursor position by `amount` bytes.
87    ///
88    /// The `AsRef<[u8]>` impl of `Chunk` provides a byte-slice view
89    /// from the current position to the end.
90    pub(crate) fn advance(&mut self, amount: usize) {
91        assert!({
92            // the new position must not exceed the vector's length
93            let pos = self.offset().checked_add(amount);
94            let max = self.cursor.get_ref().len();
95            pos.is_some() && pos <= Some(max)
96        });
97
98        self.cursor
99            .set_position(self.cursor.position() + amount as u64);
100    }
101
102    /// Consume `self` and return the inner vector.
103    pub(crate) fn into_vec(self) -> Vec<u8> {
104        self.cursor.into_inner()
105    }
106}
107
108impl AsRef<[u8]> for Chunk {
109    fn as_ref(&self) -> &[u8] {
110        &self.cursor.get_ref()[self.offset()..]
111    }
112}