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}; use tokio::io::{AsyncReadExt, AsyncWriteExt};
mod reader; pub(crate) mod reader;
pub use reader::BytesReader; pub use reader::BytesReader;
mod writer; mod writer;
pub use writer::BytesWriter; pub use writer::BytesWriter;

View file

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

View file

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