Skip to content

Commit 793949a

Browse files
committed
Implement a dedicated reader for vectored buffers
Signed-off-by: Marvin Gudel <[email protected]>
1 parent d1650f9 commit 793949a

File tree

2 files changed

+122
-85
lines changed

2 files changed

+122
-85
lines changed

mctp-estack/src/fragment.rs

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::fmt::{debug, error, info, trace, warn};
1010

1111
use mctp::{Eid, Error, MsgIC, MsgType, Result, Tag};
1212

13-
use crate::{AppCookie, MctpHeader};
13+
use crate::{util::VectorReader, AppCookie, MctpHeader};
1414

1515
/// Fragments a MCTP message.
1616
///
@@ -24,8 +24,8 @@ pub struct Fragmenter {
2424

2525
cookie: Option<AppCookie>,
2626

27-
// A count of how many bytes have already been sent.
28-
payload_used: usize,
27+
// A reader to read from the payload vector
28+
reader: VectorReader,
2929
}
3030

3131
impl Fragmenter {
@@ -59,7 +59,7 @@ impl Fragmenter {
5959
};
6060

6161
Ok(Self {
62-
payload_used: 0,
62+
reader: VectorReader::new(),
6363
header,
6464
typ,
6565
mtu,
@@ -123,9 +123,7 @@ impl Fragmenter {
123123
return SendOutput::success(self);
124124
}
125125

126-
let total_payload_len =
127-
payload.iter().fold(0, |acc, part| acc + part.len());
128-
if total_payload_len < self.payload_used {
126+
if self.reader.is_exhausted(payload).is_err() {
129127
// Caller is passing varying payload buffers
130128
debug!("varying payload");
131129
return SendOutput::failure(Error::BadArgument, self);
@@ -149,14 +147,13 @@ impl Fragmenter {
149147
rest = &mut rest[1..];
150148
}
151149

152-
let remaining_payload_len = total_payload_len - self.payload_used;
153-
let l = remaining_payload_len.min(rest.len());
154-
let (d, rest) = rest.split_at_mut(l);
155-
crate::util::copy_vectored(payload, self.payload_used, d);
156-
self.payload_used += l;
150+
let Ok(n) = self.reader.read(payload, &mut rest) else {
151+
return SendOutput::failure(Error::BadArgument, self);
152+
};
153+
let rest = &rest[n..];
157154

158155
// Add the header
159-
if self.payload_used == total_payload_len {
156+
if self.reader.is_exhausted(payload).unwrap() {
160157
self.header.eom = true;
161158
}
162159
// OK unwrap: seq and tag are valid.

mctp-estack/src/util.rs

Lines changed: 112 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -19,96 +19,136 @@ macro_rules! get_build_var {
1919
}};
2020
}
2121

22-
/// Copy data from a vectored src to dest
22+
/// A reader to read a vector of byte slices
2323
///
24-
/// Copies `dest.len()` bytes from payload to dest,
25-
/// starting after `offset` bytes.
26-
///
27-
/// ## Panics
28-
///
29-
/// This function will panic when not enough bytes are available to fill dest.
30-
/// Total size of `payload` has to be `atleast dest.len()` + `offset`.
31-
pub fn copy_vectored(src: &[&[u8]], offset: usize, dest: &mut [u8]) {
32-
let mut i = 0;
24+
#[derive(Debug)]
25+
pub struct VectorReader {
26+
/// The index of the current slice
27+
///
28+
/// Set to `vector.len()` when exhausted.
29+
slice_index: usize,
30+
/// The index in the current slice
31+
///
32+
/// E.g. the element to be read next.
33+
current_slice_offset: usize,
34+
}
3335

34-
while i < dest.len() {
35-
let payload_index = i + offset;
36-
let next = get_sub_slice(src, payload_index);
37-
let remaining = dest.len() - i;
38-
if remaining > next.len() {
39-
dest[i..(i + next.len())].copy_from_slice(next);
40-
i += next.len();
41-
} else {
42-
dest[i..].copy_from_slice(&next[..remaining]);
43-
return;
36+
impl VectorReader {
37+
/// Create a new reader
38+
pub fn new() -> Self {
39+
VectorReader {
40+
slice_index: 0,
41+
current_slice_offset: 0,
4442
}
4543
}
46-
}
44+
/// Read `dest.len()` bytes from `src` into `dest`, returning how many bytes were read
45+
///
46+
/// Returns a [VectorReaderError] when the current position is out of range for `src`.
47+
///
48+
/// The same `src` buffer has to be passed to subsequent calls to `read()`.
49+
/// Changing the vector is undefined behaviour.
50+
pub fn read(
51+
&mut self,
52+
src: &[&[u8]],
53+
dest: &mut [u8],
54+
) -> Result<usize, VectorReaderError> {
55+
let mut i = 0;
56+
while i < dest.len() {
57+
if self.is_exhausted(src)? {
58+
return Ok(i);
59+
}
4760

48-
/// Get a slice of `vector` indexed by `offset`
49-
///
50-
/// The `offset` is the absolute byte index.
51-
/// The returned slice is the remaining sub slice starting at `offset`.
52-
///
53-
/// ## Panics
54-
///
55-
/// Will panic when offset is larger than the size of `vector`.
56-
///
57-
/// ## Example
58-
/// ```ignore
59-
/// # use mctp_estack::fragment::get_slice;
60-
/// let vector: &[&[u8]] = &[&[1, 2, 3], &[4, 5, 6]];
61-
///
62-
/// let slice = get_slice(vector, 4);
63-
///
64-
/// assert_eq!(slice, &[5, 6]);
65-
/// ```
66-
pub fn get_sub_slice<'a>(vector: &'a [&[u8]], offset: usize) -> &'a [u8] {
67-
let mut i = offset;
68-
for slice in vector {
69-
if i >= slice.len() {
70-
i -= slice.len();
71-
} else {
72-
return &slice[i..];
61+
let slice = &src[self.slice_index][self.current_slice_offset..];
62+
let n = slice.len().min(dest[i..].len());
63+
dest[i..i + n].copy_from_slice(&slice[..n]);
64+
i += n;
65+
self.increment_index(src, n);
66+
}
67+
Ok(i)
68+
}
69+
/// Checks if `src` has been read to the end
70+
///
71+
/// Returns a [VectorReaderError] when the current position is out of range for `src`.
72+
///
73+
/// _Note:_ Might return a `Ok` even if the `src` vector changed between calls.
74+
pub fn is_exhausted(
75+
&self,
76+
src: &[&[u8]],
77+
) -> Result<bool, VectorReaderError> {
78+
if src.len() == self.slice_index {
79+
return Ok(true);
80+
}
81+
// This shlould only occur if the caller passed varying vectors
82+
src.get(self.slice_index).ok_or(VectorReaderError)?;
83+
Ok(false)
84+
}
85+
/// Increment the index by `n`, panic if out ouf bounds
86+
///
87+
/// If this exhausts the vector exactly, the index is incremented to `vector[vector.len()][0]`
88+
fn increment_index(&mut self, vector: &[&[u8]], n: usize) {
89+
let mut n = n;
90+
loop {
91+
if vector[self.slice_index]
92+
.get(self.current_slice_offset + n)
93+
.is_some()
94+
{
95+
// If we can index the current slice at offset + n just increment offset and return
96+
self.current_slice_offset += n;
97+
return;
98+
} else {
99+
// Substract what has been read from the current slice, then increment to next slice
100+
n -=
101+
vector[self.slice_index][self.current_slice_offset..].len();
102+
self.slice_index += 1;
103+
self.current_slice_offset = 0;
104+
if self.slice_index == vector.len() {
105+
// return when the end of the vector is reached
106+
debug_assert_eq!(n, 0);
107+
return;
108+
}
109+
}
73110
}
74111
}
75-
panic!("offset for vector out of bounds");
76112
}
77113

114+
#[derive(Debug)]
115+
pub struct VectorReaderError;
116+
78117
#[cfg(test)]
79118
mod tests {
80119
#[test]
81-
fn test_get_slice() {
82-
use super::get_sub_slice;
83-
let vector: &[&[u8]] = &[&[1, 2, 3], &[4, 5, 6], &[7, 8, 9]];
84-
let slice = get_sub_slice(vector, 4);
85-
assert_eq!(slice, &[5, 6]);
86-
let slice = get_sub_slice(vector, 0);
87-
assert_eq!(slice, &[1, 2, 3]);
88-
let slice = get_sub_slice(vector, 3);
89-
assert_eq!(slice, &[4, 5, 6]);
90-
}
91-
#[test]
92-
fn test_copy_vectored() {
93-
use super::copy_vectored;
120+
fn test_vector_reader() {
121+
use super::VectorReader;
122+
let mut reader = VectorReader::new();
94123
let vector: &[&[u8]] = &[&[1, 2, 3], &[4, 5], &[6, 7, 8, 9]];
95124

125+
// Test reading a vector partially
126+
let mut dest = [0; 4];
127+
let n = reader.read(vector, &mut dest).unwrap();
128+
assert_eq!(n, 4);
129+
assert_eq!(&dest, &[1, 2, 3, 4]);
130+
131+
// Test reading all remaining elements into a larger than necessary destination
96132
let mut dest = [0; 6];
97-
copy_vectored(vector, 1, &mut dest);
98-
assert_eq!(&dest, &[2, 3, 4, 5, 6, 7]);
133+
let n = reader.read(vector, &mut dest).unwrap();
134+
assert_eq!(n, 5);
135+
assert_eq!(&dest[..5], &[5, 6, 7, 8, 9]);
99136

100-
let mut dest = [0; 5];
101-
copy_vectored(vector, 4, &mut dest);
102-
assert_eq!(&dest, &[5, 6, 7, 8, 9]);
137+
assert!(reader
138+
.is_exhausted(vector)
139+
.expect("Vector should be exhausted"));
103140

104-
let mut dest = [0; 9];
105-
copy_vectored(vector, 0, &mut dest);
106-
assert_eq!(&dest, &[1, 2, 3, 4, 5, 6, 7, 8, 9]);
141+
// Test reading to end in one pass
142+
let mut reader = VectorReader::new();
143+
let vector: &[&[u8]] = &[&[1, 2, 3], &[4]];
107144

108-
let vector: &[&[u8]] = &[&[1, 2, 3]];
145+
let mut dest = [0; 4];
146+
let n = reader.read(vector, &mut dest).unwrap();
147+
assert_eq!(n, 4);
148+
assert_eq!(&dest, &[1, 2, 3, 4]);
109149

110-
let mut dest = [0; 1];
111-
copy_vectored(vector, 2, &mut dest);
112-
assert_eq!(&dest, &[3]);
150+
assert!(reader
151+
.is_exhausted(vector)
152+
.expect("Vector should be exhausted"));
113153
}
114154
}

0 commit comments

Comments
 (0)