use std::io::{self, IoSlice, Write};
use std::ops::{Deref, Range, RangeBounds};
use bytes::{Buf, Bytes, BytesMut};
#[derive(Debug, Clone, Default)]
pub struct BytesBuffer {
inner: BytesMut,
}
impl BytesBuffer {
#[must_use]
pub fn new() -> Self {
Self {
inner: BytesMut::new(),
}
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
inner: BytesMut::with_capacity(capacity),
}
}
#[must_use]
pub fn from_bytes(data: impl Into<Bytes>) -> Self {
let bytes: Bytes = data.into();
let mut inner = BytesMut::with_capacity(bytes.len());
inner.extend_from_slice(&bytes);
Self { inner }
}
#[must_use]
pub fn len(&self) -> usize {
self.inner.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
#[must_use]
pub fn capacity(&self) -> usize {
self.inner.capacity()
}
pub fn extend(&mut self, data: &[u8]) {
self.inner.extend_from_slice(data);
}
pub fn reserve(&mut self, additional: usize) {
self.inner.reserve(additional);
}
pub fn clear(&mut self) {
self.inner.clear();
}
#[must_use]
pub fn slice(&self, range: Range<usize>) -> Bytes {
self.inner.clone().freeze().slice(range)
}
#[must_use]
pub fn slice_ref<R: RangeBounds<usize>>(&self, range: R) -> Bytes {
use std::ops::Bound;
let start = match range.start_bound() {
Bound::Included(&n) => n,
Bound::Excluded(&n) => n + 1,
Bound::Unbounded => 0,
};
let end = match range.end_bound() {
Bound::Included(&n) => n + 1,
Bound::Excluded(&n) => n,
Bound::Unbounded => self.len(),
};
self.slice(start..end)
}
#[must_use]
pub fn freeze(self) -> Bytes {
self.inner.freeze()
}
pub fn split_to(&mut self, at: usize) -> BytesMut {
self.inner.split_to(at)
}
pub fn split_off(&mut self, at: usize) -> BytesMut {
self.inner.split_off(at)
}
pub fn advance(&mut self, n: usize) {
self.inner.advance(n);
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
&self.inner
}
#[must_use]
pub fn as_str_lossy(&self) -> std::borrow::Cow<'_, str> {
String::from_utf8_lossy(&self.inner)
}
#[must_use]
pub fn find(&self, needle: &[u8]) -> Option<usize> {
self.inner
.windows(needle.len())
.position(|window| window == needle)
}
#[must_use]
pub fn find_str(&self, needle: &str) -> Option<usize> {
self.find(needle.as_bytes())
}
#[must_use]
pub fn tail(&self, n: usize) -> &[u8] {
let start = self.len().saturating_sub(n);
&self.inner[start..]
}
#[must_use]
pub fn head(&self, n: usize) -> &[u8] {
let end = n.min(self.len());
&self.inner[..end]
}
}
impl Deref for BytesBuffer {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl AsRef<[u8]> for BytesBuffer {
fn as_ref(&self) -> &[u8] {
&self.inner
}
}
impl From<Vec<u8>> for BytesBuffer {
fn from(vec: Vec<u8>) -> Self {
Self {
inner: BytesMut::from(&vec[..]),
}
}
}
impl From<&[u8]> for BytesBuffer {
fn from(slice: &[u8]) -> Self {
Self {
inner: BytesMut::from(slice),
}
}
}
impl From<&str> for BytesBuffer {
fn from(s: &str) -> Self {
Self::from(s.as_bytes())
}
}
impl Write for BytesBuffer {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.extend(buf);
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
#[derive(Debug, Default)]
pub struct VecWriter {
chunks: Vec<Bytes>,
total_len: usize,
}
impl VecWriter {
#[must_use]
pub const fn new() -> Self {
Self {
chunks: Vec::new(),
total_len: 0,
}
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
chunks: Vec::with_capacity(capacity),
total_len: 0,
}
}
pub fn push(&mut self, data: impl Into<Bytes>) {
let bytes: Bytes = data.into();
self.total_len += bytes.len();
self.chunks.push(bytes);
}
pub fn push_slice(&mut self, data: &[u8]) {
self.push(Bytes::copy_from_slice(data));
}
#[must_use]
pub const fn chunk_count(&self) -> usize {
self.chunks.len()
}
#[must_use]
pub const fn len(&self) -> usize {
self.total_len
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.total_len == 0
}
pub fn clear(&mut self) {
self.chunks.clear();
self.total_len = 0;
}
#[must_use]
pub fn as_io_slices(&self) -> Vec<IoSlice<'_>> {
self.chunks.iter().map(|b| IoSlice::new(b)).collect()
}
#[must_use]
pub fn freeze(self) -> Bytes {
if self.chunks.len() == 1 {
return self.chunks.into_iter().next().unwrap();
}
let mut buffer = BytesMut::with_capacity(self.total_len);
for chunk in self.chunks {
buffer.extend_from_slice(&chunk);
}
buffer.freeze()
}
pub fn write_to<W: Write>(&self, writer: &mut W) -> io::Result<usize> {
let slices = self.as_io_slices();
writer.write_vectored(&slices)
}
}
#[derive(Debug)]
pub enum BorrowedView<'a> {
Borrowed(&'a [u8]),
Owned(Bytes),
}
impl<'a> BorrowedView<'a> {
#[must_use]
pub const fn borrowed(data: &'a [u8]) -> Self {
Self::Borrowed(data)
}
#[must_use]
pub fn owned(data: impl Into<Bytes>) -> Self {
Self::Owned(data.into())
}
#[must_use]
pub const fn len(&self) -> usize {
match self {
Self::Borrowed(b) => b.len(),
Self::Owned(b) => b.len(),
}
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
#[must_use]
pub fn as_slice(&self) -> &[u8] {
match self {
Self::Borrowed(b) => b,
Self::Owned(b) => b,
}
}
#[must_use]
pub fn into_owned(self) -> Bytes {
match self {
Self::Borrowed(b) => Bytes::copy_from_slice(b),
Self::Owned(b) => b,
}
}
}
impl Deref for BorrowedView<'_> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl AsRef<[u8]> for BorrowedView<'_> {
fn as_ref(&self) -> &[u8] {
self.as_slice()
}
}
pub trait ZeroCopySource {
fn view(&self) -> BorrowedView<'_>;
fn len(&self) -> usize {
self.view().len()
}
fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl ZeroCopySource for [u8] {
fn view(&self) -> BorrowedView<'_> {
BorrowedView::borrowed(self)
}
}
impl ZeroCopySource for Vec<u8> {
fn view(&self) -> BorrowedView<'_> {
BorrowedView::borrowed(self)
}
}
impl ZeroCopySource for Bytes {
fn view(&self) -> BorrowedView<'_> {
BorrowedView::Owned(self.clone())
}
}
impl ZeroCopySource for BytesBuffer {
fn view(&self) -> BorrowedView<'_> {
BorrowedView::borrowed(&self.inner)
}
}
impl ZeroCopySource for str {
fn view(&self) -> BorrowedView<'_> {
BorrowedView::borrowed(self.as_bytes())
}
}
impl ZeroCopySource for String {
fn view(&self) -> BorrowedView<'_> {
BorrowedView::borrowed(self.as_bytes())
}
}
#[derive(Debug)]
pub struct ReadPool {
buffers: Vec<BytesMut>,
buffer_size: usize,
}
impl ReadPool {
#[must_use]
pub const fn new(buffer_size: usize) -> Self {
Self {
buffers: Vec::new(),
buffer_size,
}
}
pub fn acquire(&mut self) -> BytesMut {
self.buffers
.pop()
.unwrap_or_else(|| BytesMut::with_capacity(self.buffer_size))
}
pub fn release(&mut self, mut buffer: BytesMut) {
buffer.clear();
if buffer.capacity() <= self.buffer_size * 2 {
self.buffers.push(buffer);
}
}
pub fn clear(&mut self) {
self.buffers.clear();
}
#[must_use]
pub const fn available(&self) -> usize {
self.buffers.len()
}
}
impl Default for ReadPool {
fn default() -> Self {
Self::new(8192) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bytes_buffer_basic() {
let mut buffer = BytesBuffer::new();
buffer.extend(b"hello");
buffer.extend(b" world");
assert_eq!(buffer.len(), 11);
assert_eq!(buffer.as_bytes(), b"hello world");
}
#[test]
fn bytes_buffer_slicing() {
let mut buffer = BytesBuffer::with_capacity(20);
buffer.extend(b"hello world");
let slice = buffer.slice(0..5);
assert_eq!(&slice[..], b"hello");
let slice = buffer.slice_ref(6..);
assert_eq!(&slice[..], b"world");
}
#[test]
fn bytes_buffer_find() {
let buffer = BytesBuffer::from("the quick brown fox");
assert_eq!(buffer.find(b"quick"), Some(4));
assert_eq!(buffer.find_str("fox"), Some(16));
assert_eq!(buffer.find(b"lazy"), None);
}
#[test]
fn bytes_buffer_head_tail() {
let buffer = BytesBuffer::from("hello world");
assert_eq!(buffer.head(5), b"hello");
assert_eq!(buffer.tail(5), b"world");
assert_eq!(buffer.head(100), b"hello world");
assert_eq!(buffer.tail(100), b"hello world");
}
#[test]
fn vec_writer_basic() {
let mut writer = VecWriter::new();
writer.push(b"hello".as_slice());
writer.push(b" ".as_slice());
writer.push(b"world".as_slice());
assert_eq!(writer.chunk_count(), 3);
assert_eq!(writer.len(), 11);
let bytes = writer.freeze();
assert_eq!(&bytes[..], b"hello world");
}
#[test]
fn vec_writer_io_slices() {
let mut writer = VecWriter::new();
writer.push_slice(b"hello");
writer.push_slice(b"world");
let slices = writer.as_io_slices();
assert_eq!(slices.len(), 2);
}
#[test]
fn borrowed_view() {
let data = b"hello world";
let borrowed = BorrowedView::borrowed(data);
assert_eq!(borrowed.len(), 11);
assert_eq!(borrowed.as_slice(), data);
let owned = borrowed.into_owned();
assert_eq!(&owned[..], data);
}
#[test]
fn zero_copy_source_trait() {
let vec: Vec<u8> = b"hello".to_vec();
let view = vec.view();
assert_eq!(view.len(), 5);
let string = "world".to_string();
let view = string.view();
assert_eq!(view.len(), 5);
}
#[test]
fn read_pool() {
let mut pool = ReadPool::new(4096);
assert_eq!(pool.available(), 0);
let buf1 = pool.acquire();
assert!(buf1.capacity() >= 4096);
let buf2 = pool.acquire();
pool.release(buf1);
assert_eq!(pool.available(), 1);
let buf3 = pool.acquire();
assert_eq!(pool.available(), 0);
pool.release(buf2);
pool.release(buf3);
assert_eq!(pool.available(), 2);
}
#[test]
fn write_trait() {
use std::io::Write;
let mut buffer = BytesBuffer::new();
write!(buffer, "hello {}", 42).unwrap();
assert_eq!(buffer.as_str_lossy(), "hello 42");
}
}