| // 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()); |
| } |
| } |