use core::alloc::Layout;
use core::hash::Hash;
use core::marker::PhantomData;
use core::mem::{align_of, size_of, size_of_val, ManuallyDrop};
use core::ptr::{self, NonNull};
use core::slice;
use ::alloc::alloc;
use ::alloc::vec::Vec;
use crate::buf::{Buf, BufMut, DefaultAlignment, MaybeUninit, StructPadder, Visit};
use crate::error::Error;
use crate::map::{Entry, MapRef};
use crate::pointer::{DefaultSize, Ref, Size, Slice, Unsized};
use crate::set::SetRef;
use crate::traits::{UnsizedZeroCopy, ZeroCopy};
pub struct AlignedBuf<O: Size = DefaultSize> {
data: NonNull<u8>,
len: usize,
capacity: usize,
requested: usize,
align: usize,
_marker: PhantomData<O>,
}
impl AlignedBuf {
pub const fn new() -> Self {
Self::with_alignment::<DefaultAlignment>()
}
pub fn with_capacity(capacity: usize) -> Self {
Self::with_capacity_and_alignment::<DefaultAlignment>(capacity)
}
pub const fn with_alignment<T>() -> Self
where
T: ZeroCopy,
{
let align = align_of::<T>();
Self {
data: unsafe { dangling(align) },
len: 0,
capacity: 0,
requested: align,
align,
_marker: PhantomData,
}
}
}
impl<O: Size> AlignedBuf<O> {
pub fn with_capacity_and_alignment<T>(capacity: usize) -> Self
where
T: ZeroCopy,
{
unsafe { Self::with_capacity_and_custom_alignment(capacity, align_of::<T>()) }
}
pub(crate) unsafe fn with_capacity_and_custom_alignment(capacity: usize, align: usize) -> Self where
{
if capacity == 0 {
return Self {
data: dangling(align),
len: 0,
capacity: 0,
requested: align,
align,
_marker: PhantomData,
};
}
let layout = Layout::from_size_align(capacity, align).expect("Illegal memory layout");
unsafe {
let data = alloc::alloc(layout);
if data.is_null() {
alloc::handle_alloc_error(layout);
}
Self {
data: NonNull::new_unchecked(data),
len: 0,
capacity,
requested: align,
align,
_marker: PhantomData,
}
}
}
pub fn len(&self) -> usize {
self.len
}
pub unsafe fn set_len(&mut self, len: usize) {
self.len = len;
}
pub fn clear(&mut self) {
self.len = 0;
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn capacity(&self) -> usize {
self.capacity
}
pub fn requested(&self) -> usize {
self.requested
}
pub fn align(&self) -> usize {
self.align
}
pub fn reserve(&mut self, capacity: usize) {
let new_capacity = self.len.wrapping_add(capacity);
self.ensure_capacity(new_capacity);
}
pub fn as_ptr(&self) -> *const u8 {
self.data.as_ptr() as *const _
}
pub fn as_ptr_mut(&mut self) -> *mut u8 {
self.data.as_ptr()
}
pub fn as_slice(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.as_ptr(), self.len()) }
}
pub fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.as_ptr_mut(), self.len()) }
}
#[inline]
pub fn store_uninit<T>(&mut self) -> Ref<MaybeUninit<T>, O>
where
T: ZeroCopy,
{
unsafe {
let len = self.next_offset_with(align_of::<T>(), size_of::<T>());
self.data.as_ptr().add(len).write_bytes(0, size_of::<T>());
self.len = self.len.wrapping_add(size_of::<T>());
Ref::new_raw(len)
}
}
#[inline]
pub fn load_uninit_mut<T>(&mut self, reference: Ref<MaybeUninit<T>>) -> &mut MaybeUninit<T>
where
T: ZeroCopy,
{
let at = reference.offset();
assert!(
at.wrapping_add(size_of::<T>()) <= self.len,
"Capacity overflow"
);
unsafe { &mut *(self.data.as_ptr().add(at) as *mut MaybeUninit<T>) }
}
#[inline]
pub fn store<T>(&mut self, value: &T) -> Ref<T, O>
where
T: ZeroCopy,
{
unsafe {
let ptr = self.next_offset_with(align_of::<T>(), size_of::<T>());
value.store_to(self);
Ref::new(ptr)
}
}
#[inline]
pub fn store_unsized<T>(&mut self, value: &T) -> Unsized<T, O>
where
T: ?Sized + UnsizedZeroCopy,
{
unsafe {
let ptr = self.next_offset_with(T::ALIGN, value.size());
value.store_to(self);
Unsized::new(ptr, value.size())
}
}
#[inline]
pub fn store_slice<T>(&mut self, values: &[T]) -> Slice<T, O>
where
T: ZeroCopy,
{
let ptr = self.store_array(values);
Slice::new(ptr, values.len())
}
pub fn store_set<T>(&mut self, entries: &mut [T]) -> Result<SetRef<T, O>, Error>
where
T: Visit + ZeroCopy,
T::Target: Hash,
{
let mut hash_state = {
let buf = self.as_aligned();
crate::phf::generator::generate_hash(buf, entries, |value| value)?
};
for a in 0..hash_state.map.len() {
loop {
let b = hash_state.map[a];
if hash_state.map[a] != a {
entries.swap(a, b);
hash_state.map.swap(a, b);
continue;
}
break;
}
}
let entries = self.store_slice(entries);
let mut displacements = Vec::new();
for (a, b) in hash_state.displacements {
displacements.push(Entry { key: a, value: b });
}
let displacements = self.store_slice(&displacements);
Ok(SetRef::new(hash_state.key, entries, displacements))
}
pub fn store_map<K, V>(&mut self, entries: &mut [Entry<K, V>]) -> Result<MapRef<K, V, O>, Error>
where
K: Visit + ZeroCopy,
V: ZeroCopy,
K::Target: Hash,
{
let mut hash_state = {
let buf = self.as_aligned();
crate::phf::generator::generate_hash(buf, entries, |entry| &entry.key)?
};
for a in 0..hash_state.map.len() {
loop {
let b = hash_state.map[a];
if hash_state.map[a] != a {
entries.swap(a, b);
hash_state.map.swap(a, b);
continue;
}
break;
}
}
let entries = self.store_slice(entries);
let mut displacements = Vec::new();
for (a, b) in hash_state.displacements {
displacements.push(Entry { key: a, value: b });
}
let displacements = self.store_slice(&displacements);
Ok(MapRef::new(hash_state.key, entries, displacements))
}
pub fn extend_from_slice(&mut self, bytes: &[u8]) {
let new_capacity = self.capacity + bytes.len();
self.ensure_capacity(new_capacity);
unsafe {
self.store_bytes(bytes);
}
}
#[inline]
pub unsafe fn store_bytes(&mut self, bytes: &[u8]) {
let dst = self.as_ptr_mut().wrapping_add(self.len);
dst.copy_from_nonoverlapping(bytes.as_ptr(), bytes.len());
self.len = self.len.wrapping_add(bytes.len());
}
#[inline]
pub fn as_aligned_owned_buf(&self) -> Self {
let mut new = unsafe { Self::with_capacity_and_custom_alignment(self.len, self.requested) };
unsafe {
new.as_ptr_mut()
.copy_from_nonoverlapping(self.as_ptr(), self.len);
new.set_len(self.len);
}
new
}
#[inline]
pub fn as_aligned(&mut self) -> &Buf {
if self.requested > self.align {
let (old_layout, new_layout) = self.layouts(self.capacity);
self.alloc_new(old_layout, new_layout);
}
Buf::new(self.as_slice())
}
#[inline]
pub fn as_mut_aligned(&mut self) -> &mut Buf {
if self.requested > self.align {
let (old_layout, new_layout) = self.layouts(self.capacity);
self.alloc_new(old_layout, new_layout);
}
Buf::new_mut(self.as_mut_slice())
}
#[inline]
pub fn is_aligned_to(&self, align: usize) -> bool {
crate::buf::is_aligned_to(self.as_ptr(), align)
}
#[inline]
pub fn request_align<T>(&mut self)
where
T: ZeroCopy,
{
self.requested = self.requested.max(align_of::<T>());
self.ensure_aligned(align_of::<T>(), size_of::<T>());
}
#[inline]
fn store_bits<T>(&mut self, value: T)
where
T: ZeroCopy,
{
let len = self.len.wrapping_add(size_of::<T>());
self.ensure_capacity(len);
let start = self.as_ptr_mut().wrapping_add(self.len);
unsafe {
ptr::write_unaligned(start.cast::<T>(), value);
}
self.len = len;
}
#[inline]
unsafe fn store_struct<T>(&mut self, value: &T) -> StructPadder<'_, T>
where
T: ZeroCopy,
{
let len = self.len.wrapping_add(size_of::<T>());
self.ensure_capacity(len);
let start = self.as_ptr_mut().wrapping_add(self.len);
self.len = len;
unsafe {
start.copy_from_nonoverlapping((value as *const T).cast::<u8>(), size_of::<T>());
}
StructPadder::new(start)
}
#[inline]
unsafe fn store_inner<T>(&mut self, value: &T)
where
T: ZeroCopy,
{
self.request_align::<T>();
value.store_to(self);
}
#[inline]
fn ensure_aligned(&mut self, align: usize, reserve: usize) {
let extra = crate::buf::padding_to(self.len, align);
self.reserve(extra.wrapping_add(reserve));
unsafe {
self.data.as_ptr().add(self.len).write_bytes(0, extra);
self.len = self.len.wrapping_add(extra);
}
}
#[inline]
unsafe fn next_offset_with(&mut self, align: usize, reserve: usize) -> usize {
self.requested = self.requested.max(align);
self.ensure_aligned(align, reserve);
self.len
}
#[inline]
pub fn next_offset<T>(&mut self) -> usize
where
T: ZeroCopy,
{
unsafe { self.next_offset_with(align_of::<T>(), 0) }
}
#[inline]
fn ensure_capacity(&mut self, new_capacity: usize) {
let new_capacity = new_capacity.max(self.requested);
if self.capacity >= new_capacity {
return;
}
let (old_layout, new_layout) = self.layouts(new_capacity);
if old_layout.size() == 0 {
self.alloc_init(new_layout);
} else if new_layout.align() == old_layout.align() {
self.alloc_realloc(old_layout, new_layout);
} else {
self.alloc_new(old_layout, new_layout);
}
}
#[inline]
fn layouts(&self, new_capacity: usize) -> (Layout, Layout) {
let old_layout = unsafe { Layout::from_size_align_unchecked(self.capacity, self.align) };
let layout =
Layout::from_size_align(new_capacity, self.requested).expect("Proposed layout invalid");
(old_layout, layout)
}
fn alloc_init(&mut self, new_layout: Layout) {
unsafe {
let ptr = alloc::alloc(new_layout);
if ptr.is_null() {
alloc::handle_alloc_error(new_layout);
}
self.data = NonNull::new_unchecked(ptr);
self.capacity = new_layout.size();
self.align = self.requested;
}
}
fn alloc_realloc(&mut self, old_layout: Layout, new_layout: Layout) {
debug_assert_eq!(old_layout.align(), new_layout.align());
unsafe {
let ptr = alloc::realloc(self.as_ptr_mut(), old_layout, new_layout.size());
if ptr.is_null() {
alloc::handle_alloc_error(old_layout);
}
self.data = NonNull::new_unchecked(ptr);
self.capacity = new_layout.size();
}
}
fn alloc_new(&mut self, old_layout: Layout, new_layout: Layout) {
unsafe {
let ptr = alloc::alloc(new_layout);
if ptr.is_null() {
alloc::handle_alloc_error(new_layout);
}
ptr.copy_from_nonoverlapping(self.as_ptr(), self.len);
alloc::dealloc(self.as_ptr_mut(), old_layout);
self.data = NonNull::new_unchecked(ptr);
self.capacity = new_layout.size();
self.align = self.requested;
}
}
fn store_array<T>(&mut self, values: &[T]) -> usize
where
T: ZeroCopy,
{
unsafe {
let size = size_of_val(values);
let offset = self.next_offset_with(align_of::<T>(), size);
if T::PADDED {
for value in values {
value.store_to(self);
}
} else {
self.data
.as_ptr()
.add(self.len)
.copy_from_nonoverlapping(values.as_ptr().cast::<u8>(), size);
self.len = self.len.wrapping_add(size);
}
offset
}
}
}
unsafe impl Send for AlignedBuf {}
unsafe impl Sync for AlignedBuf {}
impl<O: Size> AsRef<Buf> for AlignedBuf<O> {
#[inline]
fn as_ref(&self) -> &Buf {
Buf::new(self.as_slice())
}
}
impl<O: Size> AsMut<Buf> for AlignedBuf<O> {
#[inline]
fn as_mut(&mut self) -> &mut Buf {
Buf::new_mut(self.as_mut_slice())
}
}
impl<O: Size> Clone for AlignedBuf<O> {
fn clone(&self) -> Self {
unsafe {
let mut new = ManuallyDrop::new(Self::with_capacity_and_custom_alignment(
self.len, self.align,
));
new.as_ptr_mut()
.copy_from_nonoverlapping(self.as_ptr(), self.len);
new.requested = self.requested;
new.set_len(self.len);
ManuallyDrop::into_inner(new)
}
}
}
impl<O: Size> Drop for AlignedBuf<O> {
fn drop(&mut self) {
unsafe {
if self.capacity != 0 {
let layout = Layout::from_size_align_unchecked(self.capacity, self.align);
alloc::dealloc(self.as_ptr_mut(), layout);
}
}
}
}
impl<O: Size> BufMut for AlignedBuf<O> {
#[inline]
unsafe fn store_bytes(&mut self, bytes: &[u8]) {
AlignedBuf::store_bytes(self, bytes)
}
#[inline]
unsafe fn store_bits<T>(&mut self, value: T)
where
T: ZeroCopy,
{
AlignedBuf::store_bits(self, value)
}
#[inline]
unsafe fn store<T>(&mut self, value: &T)
where
T: ZeroCopy,
{
AlignedBuf::store_inner(self, value)
}
#[inline]
unsafe fn store_struct<T>(&mut self, value: &T) -> StructPadder<'_, T>
where
T: ZeroCopy,
{
AlignedBuf::store_struct::<T>(self, value)
}
#[inline]
unsafe fn store_array<T>(&mut self, values: &[T])
where
T: ZeroCopy,
{
self.store_array(values);
}
}
const unsafe fn dangling(align: usize) -> NonNull<u8> {
NonNull::new_unchecked(invalid_mut(align))
}
#[allow(clippy::useless_transmute)]
const fn invalid_mut<T>(addr: usize) -> *mut T {
unsafe { core::mem::transmute(addr) }
}