use crate::{
BlockingEndpoint, CompletionPort, FILE_FLAG_NO_BUFFERING, PAGE_SIZE, PageBuffers,
UnassociatedEndpoint,
};
#[test]
fn blocking_write_then_read_round_trips() {
let path = std::env::temp_dir().join(format!(
"windows-overlapped-io-sys-fs-blocking-{}.tmp",
std::process::id()
));
let _ = std::fs::remove_file(&path);
std::fs::write(&path, b"").expect("create file");
let mut endpoint = BlockingEndpoint::new(
UnassociatedEndpoint::open(&path, true, true, 0).expect("open endpoint"),
)
.expect("no incompatible notification mode");
let data = b"safe file adapter round trip";
let written = endpoint.write(data, 0).expect("write");
assert_eq!(written, data.len());
let mut buffer = vec![0_u8; data.len()];
let read = endpoint.read(&mut buffer, 0).expect("read");
assert_eq!(read, data.len());
assert_eq!(buffer, data);
drop(endpoint);
let _ = std::fs::remove_file(&path);
}
#[test]
fn iocp_read_via_file_io_token() {
let content = b"iocp file adapter content";
let path = std::env::temp_dir().join(format!(
"windows-overlapped-io-sys-fs-iocp-{}.tmp",
std::process::id()
));
std::fs::write(&path, content).expect("write file");
let port = CompletionPort::new(0).expect("create port");
let endpoint = port
.associate(
UnassociatedEndpoint::open(&path, true, false, 0).expect("open endpoint"),
0,
)
.expect("associate");
let token = endpoint
.read(vec![0_u8; content.len()], 0)
.expect("submit read")
.expect_pending("this endpoint is not in skip-on-success mode");
let completion = port.get(5_000).expect("get").expect("a completion");
let (buffer, result) = match token.claim(&completion) {
Ok(pair) => pair,
Err(_) => panic!("completion did not match the token"),
};
let read = result.expect("read result");
assert_eq!(read, content.len());
assert_eq!(&buffer[..read], content);
assert_eq!(port.outstanding(), 0);
drop(endpoint);
let _ = std::fs::remove_file(&path);
}
#[test]
fn iocp_scatter_gather_via_token() {
let path = std::env::temp_dir().join(format!(
"windows-overlapped-io-sys-fs-sg-iocp-{}.tmp",
std::process::id()
));
let _ = std::fs::remove_file(&path);
std::fs::write(&path, b"").expect("create file");
let port = CompletionPort::new(0).expect("create port");
let endpoint = port
.associate(
UnassociatedEndpoint::open(&path, true, true, FILE_FLAG_NO_BUFFERING)
.expect("open endpoint"),
0,
)
.expect("associate");
let mut src = PageBuffers::new(2);
for (i, byte) in src.as_bytes_mut().iter_mut().enumerate() {
*byte = (i % 251) as u8;
}
let expected: Vec<u8> = src.as_bytes().to_vec();
let write_token = endpoint
.write_gather(src, 0)
.expect("submit write_gather")
.expect_pending("this endpoint is not in skip-on-success mode");
let completion = port.get(5_000).expect("get").expect("write completion");
let (returned, result) = match write_token.claim(&completion) {
Ok(pair) => pair,
Err(_) => panic!("write completion did not match its token"),
};
assert_eq!(result.expect("write result"), returned.len());
assert_eq!(port.outstanding(), 0);
let read_token = endpoint
.read_scatter(PageBuffers::new(2), 0)
.expect("submit read_scatter")
.expect_pending("this endpoint is not in skip-on-success mode");
let completion = port.get(5_000).expect("get").expect("read completion");
let (buffers, result) = match read_token.claim(&completion) {
Ok(pair) => pair,
Err(_) => panic!("read completion did not match its token"),
};
let read = result.expect("read result");
assert_eq!(read, buffers.len());
assert_eq!(buffers.as_bytes(), expected.as_slice());
assert_eq!(port.outstanding(), 0);
drop(endpoint);
let _ = std::fs::remove_file(&path);
}
#[test]
fn blocking_scatter_gather_round_trips() {
let path = std::env::temp_dir().join(format!(
"windows-overlapped-io-sys-fs-sg-{}.tmp",
std::process::id()
));
let _ = std::fs::remove_file(&path);
std::fs::write(&path, b"").expect("create file");
let mut endpoint = BlockingEndpoint::new(
UnassociatedEndpoint::open(&path, true, true, FILE_FLAG_NO_BUFFERING)
.expect("open endpoint"),
)
.expect("no incompatible notification mode");
let mut src = PageBuffers::new(2);
for (i, byte) in src.as_bytes_mut().iter_mut().enumerate() {
*byte = (i % 251) as u8;
}
let written = endpoint.write_gather(&src, 0).expect("write_gather");
assert_eq!(written, src.len());
let mut dst = PageBuffers::new(2);
let read = endpoint.read_scatter(&mut dst, 0).expect("read_scatter");
assert_eq!(read, src.len());
assert_eq!(dst.as_bytes(), src.as_bytes());
drop(endpoint);
let _ = std::fs::remove_file(&path);
}
#[test]
fn checked_len_rejects_lengths_beyond_u32() {
use crate::fs::checked_len;
assert_eq!(checked_len(0, "read buffer").expect("empty fits"), 0);
assert_eq!(
checked_len(u32::MAX as usize, "read buffer").expect("the largest fitting length"),
u32::MAX
);
#[cfg(target_pointer_width = "64")]
{
let too_long = u32::MAX as usize + 1;
let error = checked_len(too_long, "read buffer").expect_err("must not cap");
assert_eq!(error.kind(), std::io::ErrorKind::InvalidInput);
assert!(
error.to_string().contains("read buffer"),
"the error should name the offending buffer: {error}"
);
}
}
fn iocp_endpoint<'port>(
port: &'port CompletionPort,
content: &[u8],
tag: &str,
) -> (crate::AssociatedEndpoint<'port>, std::path::PathBuf) {
let path = std::env::temp_dir().join(format!(
"windows-overlapped-io-sys-fs-buf-{tag}-{}.tmp",
std::process::id()
));
std::fs::write(&path, content).expect("write file");
let endpoint = port
.associate(
UnassociatedEndpoint::open(&path, true, true, 0).expect("open endpoint"),
0,
)
.expect("associate");
(endpoint, path)
}
#[test]
fn a_written_buffer_comes_back_as_the_same_allocation() {
use crate::IoBuf;
let port = CompletionPort::new(0).expect("create port");
let (endpoint, path) = iocp_endpoint(&port, b"", "same-alloc");
let buffer = b"no copies anywhere in this path".to_vec();
let expected_len = buffer.len();
let expected_ptr = buffer.stable_ptr();
let token = endpoint
.write(buffer, 0)
.expect("submit write")
.expect_pending("this endpoint is not in skip-on-success mode");
let completion = port.get(5_000).expect("get").expect("a completion");
let (returned, result) = token.claim(&completion).expect("token matches");
assert_eq!(result.expect("write result"), expected_len);
assert_eq!(
returned.stable_ptr(),
expected_ptr,
"the buffer was copied somewhere along the way"
);
drop(endpoint);
drop(port);
let _ = std::fs::remove_file(&path);
}
#[test]
fn a_read_fills_the_callers_own_buffer_in_place() {
use crate::IoBuf;
let content = b"read straight into the caller's pages";
let port = CompletionPort::new(0).expect("create port");
let (endpoint, path) = iocp_endpoint(&port, content, "read-in-place");
let buffer = vec![0_u8; content.len()];
let expected_ptr = buffer.stable_ptr();
let token = endpoint
.read(buffer, 0)
.expect("submit read")
.expect_pending("this endpoint is not in skip-on-success mode");
let completion = port.get(5_000).expect("get").expect("a completion");
let (returned, result) = token.claim(&completion).expect("token matches");
let read = result.expect("read result");
assert_eq!(read, content.len());
assert_eq!(&returned[..read], content);
assert_eq!(
returned.stable_ptr(),
expected_ptr,
"the read did not land in the caller's own buffer"
);
drop(endpoint);
drop(port);
let _ = std::fs::remove_file(&path);
}
#[test]
fn a_shared_arc_buffer_can_be_written_without_copying_it() {
use crate::IoBuf;
use std::sync::Arc;
let port = CompletionPort::new(0).expect("create port");
let (endpoint, path) = iocp_endpoint(&port, b"", "arc");
let shared: Arc<[u8]> = Arc::from(b"shared bytes sent without a copy".to_vec());
let expected_ptr = shared.stable_ptr();
let expected_len = shared.len();
let observer = Arc::clone(&shared);
let token = endpoint
.write(shared, 0)
.expect("submit write")
.expect_pending("this endpoint is not in skip-on-success mode");
let completion = port.get(5_000).expect("get").expect("a completion");
let (returned, result) = token.claim(&completion).expect("token matches");
assert_eq!(result.expect("write result"), expected_len);
assert_eq!(returned.stable_ptr(), expected_ptr);
assert_eq!(
observer.stable_ptr(),
expected_ptr,
"the other owner must still name the same bytes"
);
assert_eq!(std::fs::read(&path).expect("read back"), &*observer);
drop(endpoint);
drop(port);
let _ = std::fs::remove_file(&path);
}
#[test]
fn a_boxed_slice_round_trips_through_a_write() {
use crate::IoBuf;
let port = CompletionPort::new(0).expect("create port");
let (endpoint, path) = iocp_endpoint(&port, b"", "boxed");
let buffer: Box<[u8]> = b"a boxed slice needs no conversion"
.to_vec()
.into_boxed_slice();
let expected_ptr = buffer.stable_ptr();
let expected_len = buffer.len();
let token = endpoint
.write(buffer, 0)
.expect("submit write")
.expect_pending("this endpoint is not in skip-on-success mode");
let completion = port.get(5_000).expect("get").expect("a completion");
let (returned, result) = token.claim(&completion).expect("token matches");
assert_eq!(result.expect("write result"), expected_len);
assert_eq!(returned.stable_ptr(), expected_ptr);
drop(endpoint);
drop(port);
let _ = std::fs::remove_file(&path);
}
#[test]
fn a_static_slice_can_be_written_with_no_allocation_at_all() {
use crate::IoBuf;
const PAYLOAD: &[u8] = b"a static payload owns nothing";
let port = CompletionPort::new(0).expect("create port");
let (endpoint, path) = iocp_endpoint(&port, b"", "static");
let token = endpoint
.write(PAYLOAD, 0)
.expect("submit write")
.expect_pending("this endpoint is not in skip-on-success mode");
let completion = port.get(5_000).expect("get").expect("a completion");
let (returned, result) = token.claim(&completion).expect("token matches");
assert_eq!(result.expect("write result"), PAYLOAD.len());
assert_eq!(returned.stable_ptr(), PAYLOAD.as_ptr());
drop(endpoint);
drop(port);
let _ = std::fs::remove_file(&path);
}
#[test]
fn page_buffers_can_be_read_into_directly_keeping_their_alignment() {
use crate::IoBuf;
let content = vec![0xAB_u8; PAGE_SIZE];
let port = CompletionPort::new(0).expect("create port");
let (endpoint, path) = iocp_endpoint(&port, &content, "pages");
let buffers = PageBuffers::new(1);
let expected_ptr = buffers.stable_ptr();
let token = endpoint
.read(buffers, 0)
.expect("submit read")
.expect_pending("this endpoint is not in skip-on-success mode");
let completion = port.get(5_000).expect("get").expect("a completion");
let (returned, result) = token.claim(&completion).expect("token matches");
assert_eq!(result.expect("read result"), PAGE_SIZE);
assert_eq!(returned.stable_ptr(), expected_ptr);
assert_eq!(returned.stable_ptr().addr() % PAGE_SIZE, 0);
assert_eq!(returned.as_bytes(), &content[..]);
drop(endpoint);
drop(port);
let _ = std::fs::remove_file(&path);
}