use std::io::Cursor;
use std::io::Error;
use std::io::ErrorKind;
use std::io::IoSliceMut;
use std::io::Read;
#[cfg(debug_assertions)]
use std::panic::AssertUnwindSafe;
#[cfg(debug_assertions)]
use std::panic::catch_unwind;
use qubit_io::Input;
struct VectorStdReader;
impl Read for VectorStdReader {
fn read(&mut self, _: &mut [u8]) -> std::io::Result<usize> {
panic!("scalar read called")
}
fn read_vectored(&mut self, buffers: &mut [IoSliceMut<'_>]) -> std::io::Result<usize> {
let mut bytes = b"abcd".iter();
let mut total = 0;
for buffer in buffers {
for slot in buffer.iter_mut() {
let Some(byte) = bytes.next() else {
return Ok(total);
};
*slot = *byte;
total += 1;
}
}
Ok(total)
}
}
#[test]
fn test_vectored_std_reader_keeps_native_override() {
let mut reader = VectorStdReader;
let mut first = [0; 2];
let mut second = [0; 2];
assert_eq!(
Input::read_vectored(&mut reader, &mut [&mut first[..], &mut second[..]]).unwrap(),
4
);
assert_eq!((&first, &second), (b"ab", b"cd"));
}
struct EdgeStdReader {
reply: Result<usize, ErrorKind>,
calls: usize,
}
impl Read for EdgeStdReader {
fn read(&mut self, _: &mut [u8]) -> std::io::Result<usize> {
panic!("scalar read called")
}
fn read_vectored(&mut self, buffers: &mut [IoSliceMut<'_>]) -> std::io::Result<usize> {
self.calls += 1;
let count = self.reply.map_err(Error::from)?;
let mut remaining = count;
for buffer in buffers {
for slot in buffer.iter_mut() {
if remaining == 0 {
return Ok(count);
}
*slot = b'x';
remaining -= 1;
}
}
Ok(count)
}
}
#[test]
fn test_vectored_std_reader_preserves_empty_short_error_and_count_contracts() {
let mut reader = EdgeStdReader { reply: Ok(0), calls: 0 };
assert_eq!(Input::read_vectored(&mut reader, &mut []).unwrap(), 0);
assert_eq!(Input::read_vectored(&mut reader, &mut [&mut [], &mut []]).unwrap(), 0);
assert_eq!(reader.calls, 0);
let mut reader = EdgeStdReader { reply: Ok(1), calls: 0 };
let mut first = [b'!'; 2];
let mut second = [b'?'; 2];
assert_eq!(
Input::read_vectored(&mut reader, &mut [&mut first, &mut second]).unwrap(),
1
);
assert_eq!(first, [b'x', b'!']);
assert_eq!(second, [b'?', b'?']);
assert_eq!(reader.calls, 1);
for kind in [ErrorKind::Interrupted, ErrorKind::PermissionDenied] {
let mut reader = EdgeStdReader {
reply: Err(kind),
calls: 0,
};
assert_eq!(
Input::read_vectored(&mut reader, &mut [&mut [0; 1]])
.unwrap_err()
.kind(),
kind
);
assert_eq!(reader.calls, 1);
}
let mut reader = EdgeStdReader { reply: Ok(5), calls: 0 };
assert_eq!(
Input::read_vectored(&mut reader, &mut [&mut [0; 2], &mut [0; 2]])
.unwrap_err()
.kind(),
ErrorKind::InvalidData
);
assert_eq!(reader.calls, 1);
}
struct OverreportingStdReader;
struct FailingStdReader;
impl Read for FailingStdReader {
fn read(&mut self, _output: &mut [u8]) -> std::io::Result<usize> {
Err(Error::new(ErrorKind::PermissionDenied, "read failed"))
}
}
impl Read for OverreportingStdReader {
fn read(&mut self, output: &mut [u8]) -> std::io::Result<usize> {
Ok(output.len() + 1)
}
}
#[test]
fn test_read_blanket_impl_exposes_input_read_and_read_unchecked() {
let mut cursor = Cursor::new(b"ab".to_vec());
let mut output = [0_u8; 4];
let read = Input::read(&mut cursor, &mut output).expect("read should succeed");
assert_eq!(2, read);
assert_eq!(b"ab\x00\x00", &output);
let mut cursor = Cursor::new(b"cd".to_vec());
let mut output = [b'.'; 4];
let read = unsafe { Input::read_unchecked(&mut cursor, &mut output, 1, 2).expect("read_unchecked should succeed") };
assert_eq!(2, read);
assert_eq!(b".cd.", &output);
}
#[test]
fn test_read_blanket_impl_rejects_overreported_count() {
let mut reader = OverreportingStdReader;
let mut output = [0_u8; 3];
let error = Input::read(&mut reader, &mut output).expect_err("blanket input should validate the Read count");
assert_eq!(ErrorKind::InvalidData, error.kind());
}
#[test]
fn test_read_blanket_impl_propagates_std_read_error() {
let mut reader = FailingStdReader;
let mut output = [0_u8; 3];
let error = Input::read(&mut reader, &mut output).expect_err("blanket input should propagate Read errors");
assert_eq!(ErrorKind::PermissionDenied, error.kind());
}
#[cfg(debug_assertions)]
#[test]
fn test_read_blanket_impl_read_unchecked_panics_on_invalid_range() {
let mut reader = Cursor::new(b"ab".to_vec());
let mut output = [0_u8; 1];
let result = catch_unwind(AssertUnwindSafe(|| {
let _ = unsafe { Input::read_unchecked(&mut reader, &mut output, 1, 2).expect("should panic") };
}));
assert!(result.is_err(), "read_unchecked should panic on invalid range");
}
#[cfg(debug_assertions)]
#[test]
fn test_read_blanket_impl_read_unchecked_panics_on_index_plus_count_overflow() {
let mut reader = Cursor::new(b"ab".to_vec());
let mut output = [0_u8; 1];
let result = catch_unwind(AssertUnwindSafe(|| {
let _ = unsafe { Input::read_unchecked(&mut reader, &mut output, usize::MAX, 2).expect("should panic") };
}));
assert!(
result.is_err(),
"read_unchecked should panic when index + count overflows"
);
}
#[test]
fn test_read_blanket_impl_read_unchecked_propagates_std_read_error() {
let mut reader = FailingStdReader;
let mut output = [0_u8; 1];
let result = unsafe { Input::read_unchecked(&mut reader, &mut output, 0, 1) }
.expect_err("read_unchecked should propagate std read errors");
assert_eq!(ErrorKind::PermissionDenied, result.kind());
}