#![doc = include_str!("../README.md")]
use bytes::BytesMut;
use std::cell::Cell;
mod local_buf {
use super::{BytesMut, Cell};
thread_local! {
static POOL: Cell<Option<BytesMut>> = const { Cell::new(None) };
}
pub fn clear_buffer() -> Option<BytesMut> {
POOL.with(Cell::take)
}
pub fn take_buffer(capacity: usize) -> BytesMut {
POOL.with(|cell| {
if let Some(mut buf) = cell.take() {
if buf.capacity() >= capacity {
buf.clear();
return buf;
}
cell.set(Some(buf));
}
#[cfg(feature = "tracing")]
tracing::debug!("current thread LocalBuf is None, allocate {capacity}B");
BytesMut::with_capacity(capacity)
})
}
pub fn return_buffer(buf: BytesMut) {
POOL.with(|cell| {
let mut slot = cell.take();
if slot.as_ref().map_or(0, BytesMut::capacity) < buf.capacity() {
slot = Some(buf);
}
cell.set(slot);
});
}
}
pub struct LocalBuf {
inner: Option<BytesMut>,
}
impl LocalBuf {
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
let buf = local_buf::take_buffer(capacity);
Self { inner: Some(buf) }
}
#[must_use]
pub const fn from_bytes(buf: BytesMut) -> Self {
Self { inner: Some(buf) }
}
#[must_use]
#[allow(clippy::missing_panics_doc)]
pub fn into_bytes(mut self) -> BytesMut {
self.inner.take().expect("LocalBuf 已被意外消费")
}
#[allow(clippy::must_use_candidate)]
pub fn clear_buffer() -> Option<BytesMut> {
local_buf::clear_buffer()
}
}
impl std::ops::Deref for LocalBuf {
type Target = BytesMut;
fn deref(&self) -> &Self::Target {
self.inner.as_ref().expect("LocalBuf 已被意外消费")
}
}
impl std::ops::DerefMut for LocalBuf {
fn deref_mut(&mut self) -> &mut Self::Target {
self.inner.as_mut().expect("LocalBuf 已被意外消费")
}
}
impl Drop for LocalBuf {
fn drop(&mut self) {
if let Some(buf) = self.inner.take() {
local_buf::return_buffer(buf);
}
}
}
impl From<BytesMut> for LocalBuf {
fn from(value: BytesMut) -> Self {
Self::from_bytes(value)
}
}