use std::{io, ops::Range};
use crate::buf::{IoBuf, IoBufMut, IoBufMutExt, Slice};
const DEFAULT_RESERVE: usize = 16;
pub(super) fn append<B: IoBufMut>(buffer: &mut B, bytes: &[u8]) -> io::Result<()> {
let initialized = IoBuf::as_init(buffer).len();
let end = initialized
.checked_add(bytes.len())
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "framed buffer length overflow"))?;
buffer.reserve(bytes.len())?;
if end > IoBufMut::as_uninit(buffer).len() {
return Err(io::Error::other(
"buffer reserve did not provide the requested capacity",
));
}
unsafe {
std::ptr::copy_nonoverlapping(
bytes.as_ptr(),
IoBufMut::as_uninit(buffer).as_mut_ptr().cast::<u8>().add(initialized),
bytes.len(),
);
buffer.set_len(end);
}
Ok(())
}
pub(super) struct ReadBuffer<B = Vec<u8>>(Option<Slice<B>>);
impl ReadBuffer<Vec<u8>> {
pub(super) fn new() -> Self {
Self::new_with(Vec::new())
}
pub(super) fn with_capacity(cap: usize) -> Self {
Self::new_with(Vec::with_capacity(cap))
}
}
impl<B: IoBufMut + IoBufMutExt> ReadBuffer<B> {
pub(super) fn new_with(mut buf: B) -> Self {
let end = buf.as_uninit().len().max(buf.as_init().len());
Self(Some(Slice::new(buf, 0, end)))
}
#[inline]
pub(super) fn pending(&self) -> &Slice<B> {
self.0.as_ref().expect("ReadBuffer in inconsistent state")
}
#[inline]
pub(super) fn pending_mut(&mut self) -> &mut [u8] {
self.0.as_mut().expect("ReadBuffer in inconsistent state")
}
#[inline]
pub(super) fn is_empty(&self) -> bool {
self.pending().is_empty()
}
#[inline]
pub(super) fn take_inner(&mut self) -> Slice<B> {
self.0.take().expect("ReadBuffer in inconsistent state")
}
#[inline]
pub(super) fn restore_inner(&mut self, buf: Slice<B>) {
debug_assert!(self.0.is_none());
self.0 = Some(buf);
}
pub(super) fn restore_from_parts(&mut self, mut buf: B, start: usize) {
let init = buf.as_init().len();
let end = buf.as_uninit().len().max(init);
let start = start.min(init);
self.restore_inner(Slice::new(buf, start, end));
}
pub(super) fn reserve(&mut self, additional: usize) -> io::Result<()> {
let slice = self.take_inner();
let start = slice.start();
let mut buf = slice.into_inner();
let init = buf.as_init().len();
let spare = buf.as_uninit().len().saturating_sub(init);
if spare < additional
&& let Err(error) = buf.reserve(additional)
{
self.restore_from_parts(buf, start);
return Err(error);
}
let init = buf.as_init().len();
let end = buf.as_uninit().len().max(init);
let start = start.min(init);
self.restore_inner(Slice::new(buf, start, end));
Ok(())
}
pub(super) fn prepare_fill(&mut self) -> io::Result<(usize, Slice<B>)> {
let slice = self.take_inner();
let mut pending_start = slice.start();
let mut buf = slice.into_inner();
let init = buf.as_init().len();
if pending_start > 0 && pending_start >= init / 2 {
if pending_start < init {
let pending_len = init - pending_start;
buf.copy_within(pending_start..init, 0);
buf.truncate(pending_len);
} else {
buf.clear();
}
pending_start = 0;
}
self.restore_from_parts(buf, pending_start);
self.reserve(DEFAULT_RESERVE)?;
let slice = self.take_inner();
let pending_start = slice.start();
let mut buf = slice.into_inner();
let init = buf.as_init().len();
let end = buf.as_uninit().len().max(init);
let fill = Slice::new(buf, init, end);
Ok((pending_start, fill))
}
pub(super) fn finish_fill(&mut self, fill: Slice<B>, pending_start: usize) {
let mut buf = fill.into_inner();
let init = buf.as_init().len();
let end = buf.as_uninit().len().max(init);
let start = pending_start.min(init);
self.restore_inner(Slice::new(buf, start, end));
}
#[cfg(test)]
pub(super) fn get_ref(&self) -> &B {
self.pending().get_ref()
}
pub(super) fn into_parts(mut self) -> (B, Range<usize>) {
let slice = self.take_inner();
let start = slice.start();
let buf = slice.into_inner();
let end = buf.as_init().len();
(buf, start..end)
}
pub(super) fn from_parts(mut buf: B, unread: Range<usize>) -> io::Result<Self> {
let initialized = buf.as_init().len();
if unread.start > unread.end || unread.end != initialized {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"unread range must end at the initialized buffer length",
));
}
let end = buf.as_uninit().len().max(initialized);
Ok(Self(Some(Slice::new(buf, unread.start, end))))
}
}
#[cfg(test)]
mod tests {
use std::mem::MaybeUninit;
use super::*;
use crate::buf::{IoBuf, SetLen};
struct FixedBuffer {
bytes: Box<[MaybeUninit<u8>]>,
initialized: usize,
}
impl FixedBuffer {
fn with_capacity(capacity: usize) -> Self {
Self {
bytes: Box::new_uninit_slice(capacity),
initialized: 0,
}
}
fn initialized(capacity: usize) -> Self {
let mut buffer = Self::with_capacity(capacity);
buffer.bytes.fill(MaybeUninit::new(0));
buffer.initialized = capacity;
buffer
}
}
impl IoBuf for FixedBuffer {
fn as_init(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.bytes.as_ptr().cast(), self.initialized) }
}
}
impl SetLen for FixedBuffer {
unsafe fn set_len(&mut self, len: usize) {
assert!(len <= self.bytes.len());
self.initialized = len;
}
}
impl IoBufMut for FixedBuffer {
fn as_uninit(&mut self) -> &mut [MaybeUninit<u8>] {
&mut self.bytes
}
}
#[test]
fn failed_reserve_preserves_the_owned_buffer() {
let mut buffer = ReadBuffer::new_with(FixedBuffer::with_capacity(4));
let error = buffer.reserve(8).unwrap_err();
assert_eq!(error.kind(), io::ErrorKind::Unsupported);
assert_eq!(buffer.get_ref().bytes.len(), 4);
assert!(buffer.is_empty());
}
#[test]
fn prepare_fill_compacts_before_requesting_growth() {
let mut buffer = ReadBuffer::new_with(FixedBuffer::initialized(DEFAULT_RESERVE));
let slice = buffer.take_inner();
let buf = slice.into_inner();
buffer.restore_from_parts(buf, DEFAULT_RESERVE);
let (pending_start, mut fill) = buffer.prepare_fill().unwrap();
assert_eq!(pending_start, 0);
assert_eq!(fill.start(), 0);
assert!(fill.as_init().is_empty());
assert_eq!(fill.buf_capacity(), DEFAULT_RESERVE);
}
}