use core::alloc::Layout;
use core::mem::size_of;
use core::ptr;
use core::slice::{from_raw_parts, from_raw_parts_mut};
use alloc::alloc::{alloc, dealloc, handle_alloc_error, realloc};
use crate::buf::{max_size_for_align, padding_to};
use crate::{Frame, Write, WriteUnaligned};
use super::Alloc;
pub struct UnalignedBuf {
data: ptr::NonNull<u8>,
capacity: usize,
written: usize,
read: usize,
base: usize,
}
impl UnalignedBuf {
pub(crate) fn new() -> Self {
Self {
data: ptr::NonNull::dangling(),
capacity: 0,
written: 0,
read: 0,
base: 0,
}
}
pub(crate) fn update_base_align(&mut self) {
self.base = self.written;
}
pub(crate) fn alloc<T>(&mut self) -> Alloc<T>
where
T: Frame,
{
self.align_mut::<T>();
let at = self.written;
unsafe {
self.zero(size_of::<T>());
}
Alloc::new(at)
}
pub(crate) fn store_at<T>(&mut self, at: Alloc<T>, frame: T)
where
T: Frame,
{
let at = at.into_usize();
assert!(at + size_of::<T>() <= self.written, "write underflow");
unsafe {
let from = (&frame as *const T).cast::<u8>();
self.data
.as_ptr()
.add(at)
.copy_from_nonoverlapping(from, size_of::<T>());
}
}
pub(crate) fn store<T>(&mut self, frame: T)
where
T: Frame,
{
self.align_mut::<T>();
unsafe {
let src = (&frame as *const T).cast::<u8>();
let dst = self.data.as_ptr().add(self.written);
ptr::copy_nonoverlapping(src, dst, size_of::<T>());
self.written += size_of::<T>();
}
}
pub(crate) fn write<T>(&mut self, value: &T)
where
T: ?Sized + Write,
{
value.write_to_unaligned(self);
}
pub(crate) fn extend_from_slice(&mut self, bytes: &[u8]) {
let requested = self.written + bytes.len();
self.ensure_capacity(requested);
unsafe {
self.data
.as_ptr()
.add(self.written)
.copy_from(bytes.as_ptr(), bytes.len());
}
self.written += bytes.len();
}
pub(crate) fn extend_from_slice_nul(&mut self, bytes: &[u8]) {
let len = bytes.len() + 1;
self.ensure_capacity(self.written + len);
unsafe {
let ptr = self.data.as_ptr().add(self.written);
ptr.copy_from(bytes.as_ptr(), bytes.len());
ptr.add(bytes.len()).write(0u8);
}
self.written += len;
}
pub(crate) fn reserve_bytes(&mut self, bytes: usize) {
let requested = self.written + bytes;
self.ensure_capacity(requested);
}
#[inline]
pub(crate) fn is_empty(&self) -> bool {
self.read == self.written
}
#[inline]
pub(crate) fn len(&self) -> usize {
self.written - self.read
}
pub(crate) fn get(&self) -> &[u8] {
unsafe {
let at = self.data.as_ptr().add(self.read);
from_raw_parts(at, self.len())
}
}
pub(crate) fn get_mut(&mut self) -> &mut [u8] {
unsafe {
let len = self.capacity - self.written;
let at = self.data.as_ptr().add(self.written);
from_raw_parts_mut(at, len)
}
}
pub(crate) fn advance_mut(&mut self, n: usize) {
self.written += n;
}
pub(crate) fn advance(&mut self, n: usize) {
self.read += n;
if self.read == self.written {
self.clear();
}
}
pub(crate) fn clear(&mut self) {
self.read = 0;
self.written = 0;
self.base = 0;
}
fn ensure_capacity(&mut self, capacity: usize) {
if capacity <= self.capacity {
return;
}
let capacity = 16usize.max(capacity.next_power_of_two());
assert!(capacity <= max_size_for_align(1), "capacity overflow");
self.realloc(capacity);
self.capacity = capacity;
}
fn realloc(&mut self, capacity: usize) {
unsafe {
if self.capacity == 0 {
let layout = Layout::from_size_align_unchecked(capacity, 1);
let ptr = alloc(layout);
if ptr.is_null() {
handle_alloc_error(layout);
}
self.data = ptr::NonNull::new_unchecked(ptr);
} else {
let layout = Layout::from_size_align_unchecked(self.capacity, 1);
let ptr = realloc(self.data.as_ptr(), layout, capacity);
if ptr.is_null() {
handle_alloc_error(layout);
}
self.data = ptr::NonNull::new_unchecked(ptr);
}
}
}
pub(crate) fn align_mut<T>(&mut self) {
let padding = padding_to::<T>(self.written - self.base);
let requested = self.written + padding + size_of::<T>();
self.ensure_capacity(requested);
unsafe {
self.zero(padding);
}
}
unsafe fn zero(&mut self, len: usize) {
unsafe {
let at = self.data.as_ptr().wrapping_add(self.written);
at.write_bytes(0, len);
}
self.written += len;
}
}
unsafe impl Send for UnalignedBuf {}
unsafe impl Sync for UnalignedBuf {}
impl Default for UnalignedBuf {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl Drop for UnalignedBuf {
fn drop(&mut self) {
unsafe {
if self.capacity > 0 {
let layout = Layout::from_size_align_unchecked(self.capacity, 1);
dealloc(self.data.as_ptr(), layout);
self.capacity = 0;
}
}
}
}
impl WriteUnaligned for UnalignedBuf {
#[inline]
fn store<T>(&mut self, frame: T)
where
T: Frame,
{
UnalignedBuf::store(self, frame)
}
#[inline]
fn extend_from_slice(&mut self, bytes: &[u8]) {
UnalignedBuf::extend_from_slice(self, bytes)
}
#[inline]
fn extend_from_slice_nul(&mut self, bytes: &[u8]) {
UnalignedBuf::extend_from_slice_nul(self, bytes)
}
}