Skip to content

Conversation

@Berrysoft
Copy link
Member

Closes #590

Users may choose the preferred API set to use - either compio-io or futures-util. All tests and examples are fixed to use compio-io traits. Only the benchmark uses futures-util to align the comparison with quinn.

There might be an argument about removing the previous read and read_exact, because it receives BufMut which allows the users to pass uninitialized buffers. I would like to point out that poll_read_uninit serves this purpose, and users might want to implement hyper::Read or tokio::AsyncRead though it. It's the same as AsyncStream::poll_read_uninit.

@Berrysoft Berrysoft added this to the v0.18 milestone Dec 21, 2025
@Berrysoft Berrysoft self-assigned this Dec 21, 2025
@Berrysoft Berrysoft added refactor Refactoring existing code package: quic Related to compio-quic labels Dec 21, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the QUIC I/O APIs to support both compio-io and futures-util trait implementations, allowing users to choose their preferred API set. The refactoring removes methods that relied on the BufMut trait and introduces new APIs based on uninitialized buffers and owned buffer types.

  • Removed read, read_exact, write, and write_all methods that accepted BufMut arguments
  • Introduced poll_read_uninit for low-level reading into uninitialized buffers
  • Redesigned read_to_end to accept a size_limit parameter instead of a user-provided buffer
  • Updated all tests, examples, and benchmarks to use the new compio-io traits

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
compio-quic/src/recv_stream.rs Replaced poll_read and removed read/read_exact methods; introduced poll_read_uninit; redesigned read_to_end to allocate its own buffer; renamed error BufferTooShort to TooLong
compio-quic/src/send_stream.rs Removed standalone write and write_all methods; inlined write logic into AsyncWrite trait implementation
compio-quic/tests/echo.rs Updated to use new write_all from AsyncWriteExt and new read_to_end signature
compio-quic/tests/basic.rs Updated API calls to use new signatures; demonstrates using both RecvStream::read_to_end and AsyncReadExt::read_to_end for buffer reuse
compio-quic/examples/quic-server.rs Updated imports and read_to_end usage
compio-quic/examples/quic-dispatcher.rs Updated imports and API calls to use new write_all and read_to_end
compio-quic/examples/quic-client.rs Updated imports and read_to_end usage
compio-quic/benches/quic.rs Added futures_util::AsyncWriteExt import for benchmark; updated read_to_end calls
compio-quic/Cargo.toml Added io-compat feature requirement for benchmarks

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@Berrysoft
Copy link
Member Author

An overhead might be: AsyncReadExt::read_to_end, either the compio-io one or the futures-util one, doesn't optimize for unordered read, and might be slower than RecvStream::read_to_end.

@Berrysoft
Copy link
Member Author

I don't think size_limit is that useful. How about just implementing fn read_to_end<B: IoBufMut>(B) -> BufResult<usize, B>?

@George-Miao George-Miao changed the title refactor(quic): redesign IO APIs refactor(quic)!: redesign IO APIs Dec 23, 2025
@George-Miao
Copy link
Member

I don't think size_limit is that useful. How about just implementing fn read_to_end<B: IoBufMut>(B) -> BufResult<usize, B>?

But now we have reserve, how should user implement size limit? Maybe another adapter around IoBufMut to limit reserve?

@Berrysoft
Copy link
Member Author

read_to_end is implementable through other public APIs. It's just optimized for unordered chunks.

@Berrysoft
Copy link
Member Author

@George-Miao ping

@Berrysoft
Copy link
Member Author

@AsakuraMizu ping?

/// Attempts to read from the stream into the provided buffer
///
/// On success, returns `Poll::Ready(Ok(num_bytes_read))` and places data
/// into `buf`. If this returns zero bytes read (and `buf` has a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the buffer passed in has non-zero length and a 0 is returned, ...

Copy link
Collaborator

@AsakuraMizu AsakuraMizu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm generally okay with the API changes, but I have a few thoughts:

  1. Users will now be unable to even use &[u8] or &mut[u8] as read/write buffers (due to the static limitation of IoBuf and IoBufMut), unless they manually enable the io-compat feature, then add a dependency on futures-util and manually enable the io feature (which isn't even included in the default feature), and finally import futures_util::AsyncWriteExt. Goodness, that's a disaster.

  2. I prefer to use std::mem::transmute for std::slice::from_raw_parts, to be consistent with the implementation of [MaybeUninit<T>]::write_copy_of_slice, which will be stabilized in version 1.93. (That's why I just asked in the group if we have MSRV 🤣)

if needed > 0
&& let Err(e) = buf.reserve(needed)
{
return BufResult(Err(io::Error::new(io::ErrorKind::InvalidData, e)), buf);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be either OutOfMemory, Undupported or Other

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think ReserveError::NotSupported should also be OutOfMemory. Besides, why don't we implement a type conversion to std::io::Error, like std::collections::TryReserveError does?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea

@George-Miao
Copy link
Member

  1. Users will now be unable to even use &[u8] or &mut[u8] as read/write buffers (due to the static limitation of IoBuf and IoBufMut)

What about we relief the requirement on 'static, and instead require that on our other APIs'? I.e. B: IoBufMut + 'static on other place.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking change package: quic Related to compio-quic refactor Refactoring existing code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

compio-quic: IO API redesign?

3 participants