litep2p/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.set_position(self.cursor.position() + amount as u64);
99 }
100
101 /// Consume `self` and return the inner vector.
102 pub(crate) fn into_vec(self) -> Vec<u8> {
103 self.cursor.into_inner()
104 }
105}
106
107impl AsRef<[u8]> for Chunk {
108 fn as_ref(&self) -> &[u8] {
109 &self.cursor.get_ref()[self.offset()..]
110 }
111}