Repository: tokio-rs/tokio
File: tokio-util/src/io/sync_bridge.rs (Rust)

Code before:
/// ```rust
/// let mut data = Vec::new();
/// reader.read_to_end(&mut data).await?;
/// let value: MyStruct = serde_json::from_slice(&data)?;
/// ```
///
/// ## Correct Usage of `SyncIoBridge` inside `spawn_blocking`
///
/// SyncIoBridge is mainly useful when you need to interface with synchronous libraries from an asynchronous context.
/// Here’s how you can do it correctly:
///
/// ```rust
/// use tokio::task::spawn_blocking;
/// use tokio::io::AsyncReadExt;
/// use tokio_util::io::SyncIoBridge;
/// use std::fs::File;
/// use std::io::Read;
///
/// let reader = tokio::fs::File::open("data.txt").await?;
/// let sync_reader = SyncIoBridge::new(reader);
///
/// let result = spawn_blocking(move || {
///    let mut file = File::open("output.txt")?;
///    std::io::copy(&mut sync_reader, &mut file)?;
///    Ok::<_, std::io::Error>(())
/// }).await??;
/// ```
/// 
#[derive(Debug)]
pub struct SyncIoBridge<T> {
    src: T,
    rt: tokio::runtime::Handle,
}

impl<T: AsyncBufRead + Unpin> BufRead for SyncIoBridge<T> {
    fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
        let src = &mut self.src;
        self.rt.block_on(AsyncBufReadExt::fill_buf(src))
    }

    fn consume(&mut self, amt: usize) {
        let src = &mut self.src;
        AsyncBufReadExt::consume(src, amt)
    }

    fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> std::io::Result<usize> {
        let src = &mut self.src;
        self.rt
            .block_on(AsyncBufReadExt::read_until(src, byte, buf))
    }
    fn read_line(&mut self, buf: &mut String) -> std::io::Result<usize> {

Review comment (suggestion):
To please `rustfmt` in the CI I think you need to move the `.await??;` into its own line.
```suggestion
/// })
/// .await??;
```

Code after:
///
///    // Hash the data using the blake3 hashing function.
///    let hash = blake3::hash(&data);
///
///    Ok(hash)
///}
///
/// #[tokio::main]
/// async fn main() -> Result<(), std::io::Error> {
///     // Example: In-memory data.
///     let data = b"Hello, world!"; // A byte slice.
///     let reader = Cursor::new(data); // Create an in-memory AsyncRead.
///     hash_contents(reader).await
/// }
/// ```
///
/// When the data doesn't fit into memory, the hashing library will usually
/// provide a `hasher` that you can repeatedly call `update` on to hash the data
/// one chunk at the time.
///
/// Explanation: This example demonstrates how to asynchronously stream data in
/// chunks for hashing. Each chunk is read asynchronously, and the hash is updated
/// incrementally. This avoids blocking and improves performance over using
/// `SyncIoBridge`.
///
/// ```rust
/// use tokio::io::AsyncReadExt;
/// use tokio::io::AsyncRead;
/// use std::io::Cursor;
/// # struct Hasher;
/// # impl Hasher { pub fn update(&mut self, _: &[u8]) {} pub fn finalize(&self) {} }
///
/// /// Asynchronously streams data from an async reader, processes it in chunks,
/// /// and hashes the data incrementally.
/// async fn hash_stream(mut reader: impl AsyncRead + Unpin, mut hasher: Hasher) -> Result<(), std::io::Error> {
///    // Create a buffer to read data into, sized for performance.
///    let mut data = vec![0; 64 * 1024];
///    loop {
///        // Read data from the reader into the buffer.
///        let len = reader.read(&mut data).await?;
///        if len == 0 { break; } // Exit loop if no more data.
///
///        // Update the hash with the data read.
///        hasher.update(&data[..len]);
///    }
///
///    // Finalize the hash after all data has been processed.
///    let hash = hasher.finalize();
///
///    Ok(hash)
///}