use std::cell::RefCell;
pub(crate) const POOL_MAX_CAPACITY: usize = 1024;
pub(crate) const POOL_BUF_SHRINK_TO_CAPACITY: usize = 16_384;
pub(crate) const POOL_BUF_PRE_WRITE_SPACE: usize = 128;
#[derive(Clone, PartialEq, Eq)]
pub struct PoolBuf(Option<(usize, Vec<u8>)>);
impl std::fmt::Debug for PoolBuf {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let inner = self.0.as_ref().unwrap();
let byte_count = inner.1.len() - inner.0;
f.debug_struct("PoolBuf")
.field("byte_count", &byte_count)
.finish()
}
}
impl std::io::Write for PoolBuf {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
std::io::Write::write(&mut self.0.as_mut().unwrap().1, buf)
}
fn flush(&mut self) -> std::io::Result<()> {
std::io::Write::flush(&mut self.0.as_mut().unwrap().1)
}
}
impl serde::Serialize for PoolBuf {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_bytes(self.as_ref())
}
}
struct VisitBytes;
impl<'de> serde::de::Visitor<'de> for VisitBytes {
type Value = PoolBuf;
fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(fmt, "raw bytes")
}
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
let mut out = PoolBuf::new();
out.extend_from_slice(v);
Ok(out)
}
}
impl<'de> serde::Deserialize<'de> for PoolBuf {
fn deserialize<D>(deserializer: D) -> Result<PoolBuf, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_bytes(VisitBytes)
}
}
thread_local! {
pub(crate) static BUF_POOL: RefCell<Vec<Vec<u8>>> = RefCell::new(Vec::with_capacity(POOL_MAX_CAPACITY));
}
impl Drop for PoolBuf {
fn drop(&mut self) {
if let Some((_, mut inner)) = self.0.take() {
BUF_POOL.with(|p| {
let mut p = p.borrow_mut();
if p.len() < POOL_MAX_CAPACITY {
reset(&mut inner, true);
p.push(inner);
}
});
}
}
}
fn reset(v: &mut Vec<u8>, do_truncate: bool) {
crate::metrics::metric_push_pool_buf_release_size(v.capacity() as u64);
if do_truncate && v.capacity() > POOL_BUF_SHRINK_TO_CAPACITY {
v.truncate(POOL_BUF_SHRINK_TO_CAPACITY);
v.shrink_to_fit();
}
v.resize(POOL_BUF_PRE_WRITE_SPACE, 0);
}
impl PoolBuf {
pub fn new() -> Self {
let inner = BUF_POOL.with(|p| {
let mut p = p.borrow_mut();
if p.is_empty() {
vec![0; POOL_BUF_PRE_WRITE_SPACE]
} else {
p.remove(0)
}
});
Self(Some((POOL_BUF_PRE_WRITE_SPACE, inner)))
}
pub fn clear(&mut self) {
let inner = self.0.as_mut().unwrap();
reset(&mut inner.1, false);
inner.0 = inner.1.len();
}
pub fn shift_data_forward(&mut self, len: usize) {
if len == 0 {
return;
}
let inner = self.0.as_mut().unwrap();
let start = inner.0;
let data_len = inner.1.len() - start;
if len >= data_len {
reset(&mut inner.1, false);
inner.0 = POOL_BUF_PRE_WRITE_SPACE;
return;
}
let r = len + start..inner.1.len();
inner.1.copy_within(r, start);
inner.1.truncate(inner.1.len() - len);
}
pub fn cheap_move_start(&mut self, len: usize) {
if len == 0 {
return;
}
let inner = self.0.as_mut().unwrap();
let start = inner.0;
let data_len = inner.1.len() - start;
if len >= data_len {
reset(&mut inner.1, false);
inner.0 = POOL_BUF_PRE_WRITE_SPACE;
return;
}
inner.0 += len;
}
pub fn reserve(&mut self, want_size: usize) {
let inner = self.0.as_mut().unwrap();
inner.1.reserve(want_size + inner.0);
}
pub fn extend_from_slice(&mut self, src: &[u8]) {
let inner = self.0.as_mut().unwrap();
inner.1.extend_from_slice(src);
}
#[allow(clippy::uninit_vec)]
pub fn reserve_front(&mut self, mut len: usize) {
let inner = self.0.as_mut().unwrap();
if len < inner.0 {
return;
}
len += POOL_BUF_PRE_WRITE_SPACE;
let prev_len = inner.1.len();
let new_len = prev_len + len;
inner.1.reserve(new_len);
unsafe {
inner.1.set_len(new_len);
}
inner.1.copy_within(inner.0..prev_len, inner.0 + len);
inner.0 += len;
}
pub fn prepend_from_slice(&mut self, src: &[u8]) {
self.reserve_front(src.len());
let inner = self.0.as_mut().unwrap();
inner.1[inner.0 - src.len()..inner.0].copy_from_slice(src);
inner.0 -= src.len();
}
}
impl Default for PoolBuf {
fn default() -> Self {
Self::new()
}
}
impl std::ops::Deref for PoolBuf {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl AsRef<[u8]> for PoolBuf {
fn as_ref(&self) -> &[u8] {
let inner = self.0.as_ref().unwrap();
&inner.1[inner.0..]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pool_buf_prepend() {
let mut b = PoolBuf::new();
b.extend_from_slice(b"World!");
b.prepend_from_slice(b"Hello ");
assert_eq!("Hello World!", String::from_utf8_lossy(&b));
}
#[test]
fn pool_buf_prepend_large() {
const D: [u8; 512] = [0xdb; 512];
let mut b = PoolBuf::new();
b.extend_from_slice(b"apple");
b.prepend_from_slice(&D[..]);
b.prepend_from_slice(b"banana");
assert_eq!(b"banana", &b[0..6]);
assert_eq!(b"apple", &b[518..523]);
assert_eq!(&D[..], &b[6..518]);
assert_eq!(523, b.len());
}
#[test]
fn pool_buf_grow_shrink_reset_reuse() {
let mut b = PoolBuf::new();
b.extend_from_slice(b"bar");
assert_eq!(b"bar", &*b);
b.prepend_from_slice(b"foo");
assert_eq!(b"foobar", &*b);
b.cheap_move_start(3);
assert_eq!(b"bar", &*b);
b.clear();
b.extend_from_slice(b"ab");
assert_eq!(b"ab", &*b);
}
}