feat(nix-compat/wire/bytes): read bytes into an existing buffer
For small bytestrings (like NAR names), we can read into a preallocated fixed-size buffer, instead of allocating a Vec every time. Change-Id: Id8da9e9cea99c814361230c0ec02606b731c79a3 Reviewed-on: https://cl.tvl.fyi/c/depot/+/11606 Tested-by: BuildkiteCI Reviewed-by: flokli <flokli@flokli.de>
This commit is contained in:
		
							parent
							
								
									b68ef475ee
								
							
						
					
					
						commit
						17a7dac94f
					
				
					 1 changed files with 60 additions and 1 deletions
				
			
		| 
						 | 
					@ -1,8 +1,9 @@
 | 
				
			||||||
use std::{
 | 
					use std::{
 | 
				
			||||||
    io::{Error, ErrorKind},
 | 
					    io::{Error, ErrorKind},
 | 
				
			||||||
 | 
					    mem::MaybeUninit,
 | 
				
			||||||
    ops::RangeInclusive,
 | 
					    ops::RangeInclusive,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use tokio::io::{self, AsyncReadExt, AsyncWriteExt};
 | 
					use tokio::io::{self, AsyncReadExt, AsyncWriteExt, ReadBuf};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub(crate) mod reader;
 | 
					pub(crate) mod reader;
 | 
				
			||||||
pub use reader::BytesReader;
 | 
					pub use reader::BytesReader;
 | 
				
			||||||
| 
						 | 
					@ -81,6 +82,64 @@ where
 | 
				
			||||||
    Ok(buf)
 | 
					    Ok(buf)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[allow(dead_code)]
 | 
				
			||||||
 | 
					pub(crate) async fn read_bytes_buf<'a, const N: usize, R: ?Sized>(
 | 
				
			||||||
 | 
					    reader: &mut R,
 | 
				
			||||||
 | 
					    buf: &'a mut [MaybeUninit<u8>; N],
 | 
				
			||||||
 | 
					    allowed_size: RangeInclusive<usize>,
 | 
				
			||||||
 | 
					) -> io::Result<&'a [u8]>
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    R: AsyncReadExt + Unpin,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    assert_eq!(N % 8, 0);
 | 
				
			||||||
 | 
					    assert!(*allowed_size.end() <= N);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let len = reader.read_u64_le().await?;
 | 
				
			||||||
 | 
					    let len: usize = len
 | 
				
			||||||
 | 
					        .try_into()
 | 
				
			||||||
 | 
					        .ok()
 | 
				
			||||||
 | 
					        .filter(|len| allowed_size.contains(len))
 | 
				
			||||||
 | 
					        .ok_or_else(|| {
 | 
				
			||||||
 | 
					            io::Error::new(
 | 
				
			||||||
 | 
					                io::ErrorKind::InvalidData,
 | 
				
			||||||
 | 
					                "signalled package size not in allowed range",
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        })?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let buf_len = (len + 7) & !7;
 | 
				
			||||||
 | 
					    let buf = {
 | 
				
			||||||
 | 
					        let mut read_buf = ReadBuf::uninit(&mut buf[..buf_len]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while read_buf.filled().len() < buf_len {
 | 
				
			||||||
 | 
					            reader.read_buf(&mut read_buf).await?;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // ReadBuf::filled does not pass the underlying buffer's lifetime through,
 | 
				
			||||||
 | 
					        // so we must make a trip to hell.
 | 
				
			||||||
 | 
					        //
 | 
				
			||||||
 | 
					        // SAFETY: `read_buf` is filled up to `buf_len`, and we verify that it is
 | 
				
			||||||
 | 
					        // still pointing at the same underlying buffer.
 | 
				
			||||||
 | 
					        unsafe {
 | 
				
			||||||
 | 
					            assert_eq!(read_buf.filled().as_ptr(), buf.as_ptr() as *const u8);
 | 
				
			||||||
 | 
					            assume_init_bytes(&buf[..buf_len])
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if buf[len..buf_len].iter().any(|&b| b != 0) {
 | 
				
			||||||
 | 
					        return Err(io::Error::new(
 | 
				
			||||||
 | 
					            io::ErrorKind::InvalidData,
 | 
				
			||||||
 | 
					            "padding is not all zeroes",
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(&buf[..len])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// SAFETY: The bytes have to actually be initialized.
 | 
				
			||||||
 | 
					unsafe fn assume_init_bytes(slice: &[MaybeUninit<u8>]) -> &[u8] {
 | 
				
			||||||
 | 
					    &*(slice as *const [MaybeUninit<u8>] as *const [u8])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Read a "bytes wire packet" of from the AsyncRead and tries to parse as string.
 | 
					/// Read a "bytes wire packet" of from the AsyncRead and tries to parse as string.
 | 
				
			||||||
/// Internally uses [read_bytes].
 | 
					/// Internally uses [read_bytes].
 | 
				
			||||||
/// Rejects reading more than `allowed_size` bytes of payload.
 | 
					/// Rejects reading more than `allowed_size` bytes of payload.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue