use crate::concurrent::atomic_buffer::AtomicBuffer;
use crate::concurrent::logbuffer::data_frame_header;
use crate::concurrent::logbuffer::header::Header;
use crate::utils::bit_utils;
use crate::utils::errors::{AeronError, IllegalArgumentError, IllegalStateError};
use crate::utils::misc::{alloc_buffer_aligned, dealloc_buffer_aligned};
use crate::utils::types::Index;
const BUFFER_BUILDER_MAX_CAPACITY: Index = i32::MAX as Index - 8;
pub struct BufferBuilder {
capacity: Index,
limit: Index,
buffer: *mut u8,
}
impl Drop for BufferBuilder {
fn drop(&mut self) {
dealloc_buffer_aligned(self.buffer, self.capacity)
}
}
impl BufferBuilder {
pub fn new(initial_length: isize) -> Self {
let len = bit_utils::find_next_power_of_two_i64(initial_length as i64) as Index;
Self {
capacity: len,
limit: data_frame_header::LENGTH,
buffer: alloc_buffer_aligned(len),
}
}
pub fn buffer(&self) -> *mut u8 {
self.buffer
}
pub fn limit(&self) -> Index {
self.limit
}
pub fn set_limit(&mut self, limit: Index) -> Result<(), AeronError> {
if limit >= self.capacity {
return Err(IllegalArgumentError::LimitOutsideRange {
capacity: self.capacity,
limit,
}
.into());
}
self.limit = limit;
Ok(())
}
pub fn reset(&mut self) -> &mut BufferBuilder {
self.limit = data_frame_header::LENGTH;
self
}
pub fn append(
&mut self,
buffer: &AtomicBuffer,
offset: Index,
length: Index,
_header: &Header,
) -> Result<&BufferBuilder, AeronError> {
self.ensure_capacity(length)?;
unsafe {
std::ptr::copy(
buffer.buffer().offset(offset as isize),
self.buffer.offset(self.limit as isize),
length as usize,
);
}
self.limit += length;
Ok(self)
}
fn find_suitable_capacity(current_capacity: Index, required_capacity: Index) -> Result<Index, AeronError> {
let mut capacity = current_capacity;
loop {
let new_capacity = capacity + (capacity >> 1);
if new_capacity < capacity || new_capacity > BUFFER_BUILDER_MAX_CAPACITY {
if capacity == BUFFER_BUILDER_MAX_CAPACITY {
return Err(IllegalStateError::MaxCapacityReached(BUFFER_BUILDER_MAX_CAPACITY).into());
}
capacity = BUFFER_BUILDER_MAX_CAPACITY;
} else {
capacity = new_capacity;
}
if capacity >= required_capacity {
break;
}
}
Ok(capacity)
}
fn ensure_capacity(&mut self, additional_capacity: Index) -> Result<(), AeronError> {
let required_capacity = self.limit + additional_capacity;
if required_capacity > self.capacity {
let new_capacity = BufferBuilder::find_suitable_capacity(self.capacity, required_capacity)?;
let new_buffer = alloc_buffer_aligned(new_capacity);
unsafe {
std::ptr::copy(self.buffer, new_buffer, self.limit as usize);
dealloc_buffer_aligned(self.buffer, self.capacity)
}
self.buffer = new_buffer;
self.capacity = new_capacity;
}
Ok(())
}
}