feat(nix-compat/wire/bytes/reader): parametrise on trailer tag

This allows using BytesReader with a custom tag, eg the closing parens
for the NAR reader.

No public constructor is provided for custom-tagged readers, since this
feature isn't public API.

Change-Id: I82e73d064edc4b6783ead1d6fe46a5b35f45c844
Reviewed-on: https://cl.tvl.fyi/c/depot/+/11543
Reviewed-by: Brian Olsen <me@griff.name>
Reviewed-by: flokli <flokli@flokli.de>
Tested-by: BuildkiteCI
This commit is contained in:
edef 2024-04-30 08:52:14 +00:00
parent ba00f0c695
commit 343e176bec
3 changed files with 31 additions and 10 deletions

View file

@ -4,7 +4,7 @@ use std::{
};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
mod reader;
pub(crate) mod reader;
pub use reader::BytesReader;
mod writer;
pub use writer::BytesWriter;

View file

@ -8,6 +8,10 @@ use std::{
use tokio::io::{AsyncRead, AsyncReadExt, ReadBuf};
use trailer::{read_trailer, ReadTrailer, Trailer};
#[doc(hidden)]
pub use self::trailer::Pad;
pub(crate) use self::trailer::Tag;
mod trailer;
/// Reads a "bytes wire packet" from the underlying reader.
@ -24,12 +28,13 @@ mod trailer;
/// If the data is not read all the way to the end, or an error is encountered,
/// the underlying reader is no longer usable and might return garbage.
#[derive(Debug)]
pub struct BytesReader<R> {
state: State<R>,
#[allow(private_bounds)]
pub struct BytesReader<R, T: Tag = Pad> {
state: State<R, T>,
}
#[derive(Debug)]
enum State<R> {
enum State<R, T: Tag> {
/// Full 8-byte blocks are being read and released to the caller.
Body {
reader: Option<R>,
@ -38,7 +43,7 @@ enum State<R> {
user_len: u64,
},
/// The trailer is in the process of being read.
ReadTrailer(ReadTrailer<R>),
ReadTrailer(ReadTrailer<R, T>),
/// The trailer has been fully read and validated,
/// and data can now be released to the caller.
ReleaseTrailer { consumed: u8, data: Trailer },
@ -49,7 +54,21 @@ where
R: AsyncRead + Unpin,
{
/// Constructs a new BytesReader, using the underlying passed reader.
pub async fn new<S: RangeBounds<u64>>(mut reader: R, allowed_size: S) -> io::Result<Self> {
pub async fn new<S: RangeBounds<u64>>(reader: R, allowed_size: S) -> io::Result<Self> {
BytesReader::new_internal(reader, allowed_size).await
}
}
#[allow(private_bounds)]
impl<R, T: Tag> BytesReader<R, T>
where
R: AsyncRead + Unpin,
{
/// Constructs a new BytesReader, using the underlying passed reader.
pub(crate) async fn new_internal<S: RangeBounds<u64>>(
mut reader: R,
allowed_size: S,
) -> io::Result<Self> {
let size = reader.read_u64_le().await?;
if !allowed_size.contains(&size) {
@ -84,7 +103,8 @@ where
}
}
impl<R: AsyncRead + Unpin> AsyncRead for BytesReader<R> {
#[allow(private_bounds)]
impl<R: AsyncRead + Unpin, T: Tag> AsyncRead for BytesReader<R, T> {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut task::Context,

View file

@ -1,4 +1,5 @@
use std::{
fmt::Debug,
future::Future,
marker::PhantomData,
ops::Deref,
@ -33,14 +34,14 @@ pub(crate) trait Tag {
/// Suitably sized buffer for reading [Self::PATTERN]
///
/// HACK: This is a workaround for const generics limitations.
type Buf: AsRef<[u8]> + AsMut<[u8]> + Unpin;
type Buf: AsRef<[u8]> + AsMut<[u8]> + Debug + Unpin;
/// Make an instance of [Self::Buf]
fn make_buf() -> Self::Buf;
}
#[derive(Debug)]
pub(crate) enum Pad {}
pub enum Pad {}
impl Tag for Pad {
const PATTERN: &'static [u8] = &[0; 8];
@ -53,7 +54,7 @@ impl Tag for Pad {
}
#[derive(Debug)]
pub(crate) struct ReadTrailer<R, T: Tag = Pad> {
pub(crate) struct ReadTrailer<R, T: Tag> {
reader: R,
data_len: u8,
filled: u8,