#![doc = include_str!("../README.md")]
#![cfg_attr(not(any(feature = "std", test)), no_std)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, allow(unused_attributes))]
#![deny(missing_docs)]
#![forbid(unsafe_code)]
#[cfg(all(not(feature = "std"), feature = "alloc"))]
extern crate alloc as std;
#[cfg(feature = "std")]
extern crate std;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Buffer<B> {
data: B,
write_cursor: usize,
written: usize,
}
impl<B> From<B> for Buffer<B> {
fn from(data: B) -> Self {
Self {
data,
write_cursor: 0,
written: 0,
}
}
}
impl<B> Buffer<B> {
#[inline]
pub const fn new(data: B) -> Self {
Self {
data,
write_cursor: 0,
written: 0,
}
}
pub fn write(&mut self, mut buf: &[u8]) -> usize
where
B: AsMut<[u8]>,
{
let n = buf.len();
let data = self.data.as_mut();
let size = data.len();
self.written += n;
if n > size {
buf = &buf[n - size..];
}
let remain = size - self.write_cursor;
let data = self.data.as_mut();
copy(&mut data[self.write_cursor..], buf);
if n > remain {
copy(data, &buf[remain..]);
}
self.write_cursor = (self.write_cursor + buf.len()) % size;
n
}
#[inline]
pub fn read_hint(&self) -> usize
where
B: AsRef<[u8]>,
{
let data = self.data.as_ref();
let size = data.len();
match () {
() if self.written >= size && self.write_cursor == 0 => self.data.as_ref().len(),
() if self.written > size => size,
_ => self.data.as_ref()[..self.write_cursor].len(),
}
}
pub fn read_into(&self, dst: &mut [u8]) -> usize
where
B: AsRef<[u8]>,
{
let data = self.data.as_ref();
let size = data.len();
match () {
() if self.written >= size && self.write_cursor == 0 => {
dst[..size].copy_from_slice(data);
size
}
() if self.written > size => {
copy(dst, &data[self.write_cursor..]);
copy(
&mut dst[size - self.write_cursor..],
&data[..self.write_cursor],
);
size
}
_ => {
dst[..self.write_cursor].copy_from_slice(&data[..self.write_cursor]);
self.write_cursor
}
}
}
#[cfg(any(feature = "std", feature = "alloc"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))]
#[inline]
pub fn read_to_bytes(&self) -> std::borrow::Cow<'_, [u8]>
where
B: AsRef<[u8]>,
{
let data = self.data.as_ref();
let size = data.len();
std::borrow::Cow::Borrowed(match () {
() if self.written >= size && self.write_cursor == 0 => data,
() if self.written > size => {
let mut out = std::vec![0; size];
copy(&mut out, &data[self.write_cursor..]);
copy(
&mut out[size - self.write_cursor..],
&data[..self.write_cursor],
);
return out.into();
}
_ => &data[..self.write_cursor],
})
}
#[inline]
pub fn size(&self) -> usize
where
B: AsRef<[u8]>,
{
self.data.as_ref().len()
}
#[inline]
pub const fn written(&self) -> usize {
self.written
}
#[inline]
pub const fn reset(&mut self) {
self.write_cursor = 0;
self.written = 0;
}
#[inline]
pub fn into_inner(self) -> B {
self.data
}
}
#[cfg(feature = "std")]
const _: () = {
use std::io::Write;
impl<B> Write for Buffer<B>
where
B: AsMut<[u8]>,
{
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
Ok(self.write(buf))
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
};
#[cfg(all(feature = "tokio", feature = "std"))]
const _: () = {
use core::{
pin::Pin,
task::{Context, Poll},
};
use tokio::io::AsyncWrite;
impl<B> AsyncWrite for Buffer<B>
where
B: AsMut<[u8]> + Unpin,
{
fn poll_write(
self: Pin<&mut Self>,
_: &mut Context<'_>,
buf: &[u8],
) -> Poll<Result<usize, std::io::Error>> {
Poll::Ready(Ok(self.get_mut().write(buf)))
}
fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Result<(), std::io::Error>> {
Poll::Ready(Ok(()))
}
fn poll_shutdown(
self: Pin<&mut Self>,
_: &mut Context<'_>,
) -> Poll<Result<(), std::io::Error>> {
Poll::Ready(Ok(()))
}
}
};
#[cfg(all(feature = "std", feature = "futures-io"))]
const _: () = {
use core::{
pin::Pin,
task::{Context, Poll},
};
use futures_io::AsyncWrite;
impl<B> AsyncWrite for Buffer<B>
where
B: AsMut<[u8]> + Unpin,
{
fn poll_write(
self: Pin<&mut Self>,
_: &mut Context<'_>,
buf: &[u8],
) -> Poll<Result<usize, std::io::Error>> {
Poll::Ready(Ok(self.get_mut().write(buf)))
}
fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Result<(), std::io::Error>> {
Poll::Ready(Ok(()))
}
fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Result<(), std::io::Error>> {
Poll::Ready(Ok(()))
}
}
};
#[inline]
fn copy(dst: &mut [u8], src: &[u8]) -> usize {
let min_len = core::cmp::min(src.len(), dst.len());
dst[..min_len].copy_from_slice(&src[..min_len]);
min_len
}