use super::Reassembler;
use crate::{
buffer::{
reader::{
storage::{Chunk, Infallible},
Reader, Storage,
},
writer,
},
varint::VarInt,
};
use bytes::BytesMut;
impl Storage for Reassembler {
type Error = core::convert::Infallible;
#[inline]
fn buffered_len(&self) -> usize {
self.len()
}
#[inline]
fn buffer_is_empty(&self) -> bool {
self.is_empty()
}
#[inline]
fn read_chunk(&mut self, watermark: usize) -> Result<Chunk<'_>, Self::Error> {
let Some(slot) = self.slots.front_mut() else {
return Ok(BytesMut::new().into());
};
ensure!(
slot.is_occupied(self.cursors.start_offset),
Ok(BytesMut::new().into())
);
let chunk = if self.cursors.final_size().is_some_and(|final_size| {
final_size <= slot.end_allocated() && watermark >= slot.buffered_len()
}) {
slot.consume()
} else {
let Chunk::BytesMut(chunk) = slot.read_chunk(watermark)? else {
unsafe { assume!(false) }
};
chunk
};
if slot.should_drop() {
self.slots.pop_front();
}
super::probe::pop(self.cursors.start_offset, chunk.len());
self.cursors.start_offset += chunk.len() as u64;
self.invariants();
Ok(chunk.into())
}
#[inline]
fn partial_copy_into<Dest>(&mut self, dest: &mut Dest) -> Result<Chunk<'_>, Self::Error>
where
Dest: writer::Storage + ?Sized,
{
ensure!(dest.has_remaining_capacity(), Ok(Default::default()));
let mut prev = BytesMut::new();
loop {
let remaining = dest.remaining_capacity();
unsafe {
assume!(prev.len() <= remaining);
}
let watermark = remaining - prev.len();
debug_assert!(remaining > 0);
let Chunk::BytesMut(chunk) = self.infallible_read_chunk(watermark) else {
unsafe { assume!(false) }
};
ensure!(!chunk.is_empty(), Ok(prev.into()));
if !prev.is_empty() {
dest.put_bytes_mut(prev);
}
if chunk.len() == watermark {
return Ok(chunk.into());
}
prev = chunk;
}
}
#[inline]
fn copy_into<Dest>(&mut self, dest: &mut Dest) -> Result<(), Self::Error>
where
Dest: writer::Storage + ?Sized,
{
if Dest::SPECIALIZES_BYTES || Dest::SPECIALIZES_BYTES_MUT {
let mut chunk = self.infallible_partial_copy_into(dest);
chunk.infallible_copy_into(dest);
return Ok(());
}
loop {
ensure!(dest.has_remaining_capacity(), Ok(()));
let Some(slot) = self.slots.front_mut() else {
return Ok(());
};
ensure!(slot.is_occupied(self.cursors.start_offset), Ok(()));
let mut dest = dest.track_write();
slot.infallible_copy_into(&mut dest);
if slot.should_drop() {
self.slots.pop_front();
}
super::probe::pop(self.cursors.start_offset, dest.written_len());
self.cursors.start_offset += dest.written_len() as u64;
self.invariants();
}
}
}
impl Reader for Reassembler {
#[inline]
fn current_offset(&self) -> VarInt {
unsafe {
VarInt::new_unchecked(self.cursors.start_offset)
}
}
#[inline]
fn final_offset(&self) -> Option<VarInt> {
self.final_size().map(|v| unsafe {
VarInt::new_unchecked(v)
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn undersized_dest_partial_copy_into_test() {
let mut reassembler = Reassembler::default();
reassembler.write_at(VarInt::ZERO, b"hello").unwrap();
let mut dest = &mut [0u8; 1][..];
let chunk = reassembler.infallible_partial_copy_into(&mut dest);
assert_eq!(dest.len(), 1, "the destination should not be written into");
assert_eq!(&chunk[..], b"h");
assert_eq!(reassembler.current_offset().as_u64(), 1);
}
#[test]
fn oversized_dest_partial_copy_into_test() {
let mut reassembler = Reassembler::default();
reassembler.write_at(VarInt::ZERO, b"hello").unwrap();
let mut reader = reassembler.with_checks();
let mut dest = &mut [0u8; 10][..];
let chunk = reader.infallible_partial_copy_into(&mut dest);
assert_eq!(dest.len(), 10, "the destination should not be written into");
assert_eq!(&chunk[..], b"hello");
assert_eq!(reader.current_offset().as_u64(), 5);
}
#[test]
fn multiple_chunk_dest_partial_copy_into_test() {
let mut reassembler = Reassembler::default();
let offset: VarInt = (super::super::MIN_BUFFER_ALLOCATION_SIZE - 1)
.try_into()
.unwrap();
reassembler.skip(offset).unwrap();
reassembler.write_at(offset, b"hello").unwrap();
let mut reader = reassembler.with_checks();
let mut dest = [0u8; 10];
let chunk = {
let mut dest = &mut dest[..];
let chunk = reader.infallible_partial_copy_into(&mut dest);
assert_eq!(
dest.len(),
9,
"the destination should have a single byte written to it"
);
chunk
};
assert_eq!(&dest[..1], b"h");
assert_eq!(&chunk[..], b"ello");
}
}