blob: e88a74ea2767af48c4de9e7ce240f5715c0491fa [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use zerocopy::{AsBytes, FromBytes};
use crate::types::*;
/// Matches iovec_t.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, AsBytes, FromBytes)]
#[repr(C)]
pub struct UserBuffer {
pub address: UserAddress,
pub length: usize,
}
impl UserBuffer {
pub fn get_total_length(buffers: &[UserBuffer]) -> Result<usize, Errno> {
let mut total: usize = 0;
for buffer in buffers {
total = total.checked_add(buffer.length).ok_or_else(|| errno!(EINVAL))?;
}
// The docs say we should return EINVAL if "the sum of the iov_len
// values overflows an ssize_t value."
if total > isize::MAX as usize {
return error!(EINVAL);
}
Ok(total)
}
}
pub struct UserBufferIterator<'a> {
buffers: &'a [UserBuffer],
index: usize,
offset: usize,
}
impl<'a> UserBufferIterator<'a> {
pub fn new(buffers: &'a [UserBuffer]) -> UserBufferIterator<'a> {
UserBufferIterator { buffers, index: 0, offset: 0 }
}
pub fn remaining(&self) -> usize {
let remaining =
self.buffers[self.index..].iter().fold(0, |sum, buffer| sum + buffer.length);
remaining - self.offset
}
pub fn next(&mut self, limit: usize) -> Option<UserBuffer> {
if self.index >= self.buffers.len() || limit == 0 {
return None;
}
let buffer = &self.buffers[self.index];
let chunk_size = std::cmp::min(limit, buffer.length - self.offset);
let result = UserBuffer { address: buffer.address + self.offset, length: chunk_size };
self.offset += chunk_size;
while self.index < self.buffers.len() && self.offset == self.buffers[self.index].length {
self.index += 1;
self.offset = 0;
}
Some(result)
}
pub fn drain_to_vec(&mut self) -> Vec<UserBuffer> {
let mut buffers = Vec::<UserBuffer>::new();
while let Some(buffer) = self.next(usize::MAX) {
buffers.push(buffer);
}
buffers
}
}
#[cfg(test)]
mod tests {
use super::*;
#[::fuchsia::test]
fn test_get_total_length_overflow() {
let buffers = vec![
UserBuffer { address: UserAddress::from_ptr(0x20), length: usize::MAX - 10 },
UserBuffer { address: UserAddress::from_ptr(0x820), length: usize::MAX - 10 },
UserBuffer { address: UserAddress::from_ptr(0x3820), length: usize::MAX - 10 },
];
assert!(UserBuffer::get_total_length(&buffers).is_err());
let buffers = vec![
UserBuffer { address: UserAddress::from_ptr(0x20), length: (isize::MAX - 10) as usize },
UserBuffer {
address: UserAddress::from_ptr(0x820),
length: (isize::MAX - 10) as usize,
},
UserBuffer {
address: UserAddress::from_ptr(0x3820),
length: (isize::MAX - 10) as usize,
},
];
assert!(UserBuffer::get_total_length(&buffers).is_err());
}
#[::fuchsia::test]
fn test_user_buffer_iterator() {
let buffers = vec![
UserBuffer { address: UserAddress::from_ptr(0x20), length: 7 },
UserBuffer { address: UserAddress::from_ptr(0x820), length: 0 },
UserBuffer { address: UserAddress::from_ptr(0x3820), length: 42 },
];
let data = &buffers[..];
let mut user_buffers = UserBufferIterator::new(data);
assert_eq!(49, user_buffers.remaining());
assert_eq!(None, user_buffers.next(0));
assert_eq!(
Some(UserBuffer { address: UserAddress::from_ptr(0x20), length: 1 }),
user_buffers.next(1)
);
assert_eq!(48, user_buffers.remaining());
assert_eq!(
Some(UserBuffer { address: UserAddress::from_ptr(0x21), length: 2 }),
user_buffers.next(2)
);
assert_eq!(46, user_buffers.remaining());
assert_eq!(
Some(UserBuffer { address: UserAddress::from_ptr(0x23), length: 4 }),
user_buffers.next(31)
);
assert_eq!(42, user_buffers.remaining());
assert_eq!(
Some(UserBuffer { address: UserAddress::from_ptr(0x3820), length: 9 }),
user_buffers.next(9)
);
assert_eq!(33, user_buffers.remaining());
assert_eq!(
Some(UserBuffer { address: UserAddress::from_ptr(0x3829), length: 33 }),
user_buffers.next(40)
);
assert_eq!(0, user_buffers.remaining());
assert_eq!(None, user_buffers.next(40));
assert_eq!(0, user_buffers.remaining());
}
}