use crate::alloc::memory_pool::{ALIGNMENT, AlignedBuffer};
use super::memory_pool::{AlignedBufferExt, memory_pool};
use bytes::Bytes;
use compio::buf::{IoBuf, IoBufMut, SetLen};
use std::{
mem::MaybeUninit,
ops::{Deref, DerefMut},
};
#[derive(Debug)]
pub struct PooledBuffer {
from_pool: bool,
original_capacity: usize,
original_bucket_idx: Option<usize>,
inner: AlignedBuffer,
}
impl Default for PooledBuffer {
fn default() -> Self {
Self::empty()
}
}
impl PooledBuffer {
pub fn with_capacity(capacity: usize) -> Self {
let (buffer, was_pool_allocated) = memory_pool().acquire_buffer(capacity.max(ALIGNMENT));
let original_capacity = buffer.capacity();
let original_bucket_idx = if was_pool_allocated {
memory_pool().best_fit(original_capacity)
} else {
None
};
debug_assert_eq!(
buffer.as_ptr() as usize % ALIGNMENT,
0,
"PooledBuffer not aligned to {} bytes",
ALIGNMENT
);
Self {
from_pool: was_pool_allocated,
original_capacity,
original_bucket_idx,
inner: buffer,
}
}
pub fn from_existing(existing: AlignedBuffer) -> Self {
Self {
from_pool: false,
original_capacity: existing.capacity(),
original_bucket_idx: None,
inner: existing,
}
}
pub fn empty() -> Self {
Self {
from_pool: false,
original_capacity: 0,
original_bucket_idx: None,
inner: AlignedBuffer::new(ALIGNMENT),
}
}
pub fn check_for_resize(&mut self) {
if !self.from_pool {
return;
}
let current_capacity = self.inner.capacity();
if current_capacity != self.original_capacity {
memory_pool().inc_resize_events();
if let Some(orig_idx) = self.original_bucket_idx {
memory_pool().dec_bucket_in_use(orig_idx);
if let Some(new_idx) = memory_pool().best_fit(current_capacity) {
memory_pool().inc_bucket_alloc(new_idx);
memory_pool().inc_bucket_in_use(new_idx);
self.original_bucket_idx = Some(new_idx);
} else {
memory_pool().inc_external_allocations();
self.original_bucket_idx = None;
}
}
self.original_capacity = current_capacity;
}
}
pub fn reserve(&mut self, additional: usize) {
let before_cap = self.inner.capacity();
self.inner.reserve(additional);
if self.inner.capacity() != before_cap {
self.check_for_resize();
}
}
pub fn split_to(&mut self, at: usize) -> PooledBuffer {
assert!(
at <= self.len(),
"split_to out of bounds: at={}, len={}",
at,
self.len()
);
let mut new_buff = PooledBuffer::with_capacity(at);
new_buff.inner.extend_from_slice(&self.inner[..at]);
let new_len = self.len() - at;
if new_len > 0 {
unsafe {
std::ptr::copy(
self.inner.as_ptr().add(at),
self.inner.as_mut_ptr(),
new_len,
);
self.inner.set_len(new_len);
}
} else {
self.inner.clear();
}
new_buff
}
pub fn put<T: AsRef<[u8]>>(&mut self, src: T) {
self.extend_from_slice(src.as_ref());
}
pub fn extend_from_slice(&mut self, extend_from: &[u8]) {
let before_cap = self.inner.capacity();
self.inner.extend_from_slice(extend_from);
if self.inner.capacity() != before_cap {
self.check_for_resize();
}
}
pub fn put_bytes(&mut self, byte: u8, len: usize) {
let before_cap = self.inner.capacity();
let start = self.inner.len();
self.inner.resize(start + len, byte);
if self.inner.capacity() != before_cap {
self.check_for_resize();
}
}
pub fn put_slice(&mut self, src: &[u8]) {
self.extend_from_slice(src);
}
pub fn put_u32_le(&mut self, value: u32) {
let before_cap = self.inner.capacity();
self.inner.extend_from_slice(&value.to_le_bytes());
if self.inner.capacity() != before_cap {
self.check_for_resize();
}
}
pub fn put_u64_le(&mut self, value: u64) {
let before_cap = self.inner.capacity();
self.inner.extend_from_slice(&value.to_le_bytes());
if self.inner.capacity() != before_cap {
self.check_for_resize();
}
}
pub fn capacity(&self) -> usize {
self.inner.capacity()
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn into_inner(self) -> AlignedBuffer {
let mut this = std::mem::ManuallyDrop::new(self);
std::mem::replace(&mut this.inner, AlignedBuffer::new(ALIGNMENT))
}
pub fn freeze(&mut self) -> Bytes {
let buf = std::mem::replace(&mut self.inner, AlignedBuffer::new(ALIGNMENT));
if self.from_pool
&& let Some(bucket_idx) = self.original_bucket_idx
{
memory_pool().dec_bucket_in_use(bucket_idx);
}
self.from_pool = false;
self.original_capacity = 0;
self.original_bucket_idx = None;
Bytes::from_owner(buf)
}
}
impl Deref for PooledBuffer {
type Target = AlignedBuffer;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl DerefMut for PooledBuffer {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl Drop for PooledBuffer {
fn drop(&mut self) {
if self.from_pool {
let buf = std::mem::replace(&mut self.inner, AlignedBuffer::new(ALIGNMENT));
buf.return_to_pool(self.original_capacity, true);
}
}
}
impl From<&[u8]> for PooledBuffer {
fn from(slice: &[u8]) -> Self {
let mut buf = PooledBuffer::with_capacity(slice.len());
buf.inner.extend_from_slice(slice);
buf
}
}
impl AsRef<[u8]> for PooledBuffer {
fn as_ref(&self) -> &[u8] {
&self.inner
}
}
impl From<AlignedBuffer> for PooledBuffer {
fn from(buffer: AlignedBuffer) -> Self {
Self::from_existing(buffer)
}
}
impl SetLen for PooledBuffer {
unsafe fn set_len(&mut self, len: usize) {
unsafe { self.inner.set_len(len) };
}
}
impl IoBuf for PooledBuffer {
fn as_init(&self) -> &[u8] {
&self.inner[..]
}
}
impl IoBufMut for PooledBuffer {
fn as_uninit(&mut self) -> &mut [MaybeUninit<u8>] {
let ptr = self.inner.as_mut_ptr().cast::<MaybeUninit<u8>>();
let cap = self.inner.capacity();
unsafe { std::slice::from_raw_parts_mut(ptr, cap) }
}
}