use std::fmt;
use std::iter::FromIterator;
use std::ops::{Deref, DerefMut};
const ALIGN: usize = 32;
pub struct AlignedVec<T> {
ptr: *mut T,
len: usize,
cap: usize,
}
#[allow(unsafe_code)]
unsafe impl<T: Send> Send for AlignedVec<T> {}
#[allow(unsafe_code)]
unsafe impl<T: Sync> Sync for AlignedVec<T> {}
impl<T> Default for AlignedVec<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> AlignedVec<T> {
#[inline]
pub fn new() -> Self {
Self {
ptr: std::ptr::NonNull::dangling().as_ptr(),
len: 0,
cap: 0,
}
}
#[allow(unsafe_code)]
pub fn with_capacity(cap: usize) -> Self {
if cap == 0 {
return Self::new();
}
let ptr = alloc_aligned::<T>(cap);
Self { ptr, len: 0, cap }
}
#[allow(unsafe_code)]
pub fn from_vec(v: Vec<T>) -> Self
where
T: Copy,
{
let len = v.len();
if len == 0 {
return Self::new();
}
let ptr = alloc_aligned::<T>(len);
unsafe {
std::ptr::copy_nonoverlapping(v.as_ptr(), ptr, len);
}
drop(v);
Self { ptr, len, cap: len }
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub fn as_ptr(&self) -> *const T {
self.ptr
}
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut T {
self.ptr
}
#[inline]
#[allow(unsafe_code)]
pub fn as_slice(&self) -> &[T] {
if self.len == 0 {
return &[];
}
unsafe { std::slice::from_raw_parts(self.ptr, self.len) }
}
#[inline]
#[allow(unsafe_code)]
pub fn as_mut_slice(&mut self) -> &mut [T] {
if self.len == 0 {
return &mut [];
}
unsafe { std::slice::from_raw_parts_mut(self.ptr, self.len) }
}
#[allow(unsafe_code)]
pub fn push(&mut self, val: T) {
if self.len == self.cap {
self.grow();
}
unsafe {
self.ptr.add(self.len).write(val);
}
self.len += 1;
}
#[allow(unsafe_code)]
fn grow(&mut self) {
let new_cap = if self.cap == 0 {
4
} else {
self.cap.checked_mul(2).expect("capacity overflow")
};
let new_ptr = alloc_aligned::<T>(new_cap);
if self.cap > 0 {
if self.len > 0 {
unsafe {
std::ptr::copy_nonoverlapping(self.ptr, new_ptr, self.len);
}
}
dealloc_aligned::<T>(self.ptr, self.cap);
}
self.ptr = new_ptr;
self.cap = new_cap;
}
}
impl<T: Copy> AlignedVec<T> {
#[allow(unsafe_code)]
#[inline]
pub fn uninitialized(len: usize) -> Self {
if len == 0 {
return Self::new();
}
let ptr = alloc_aligned::<T>(len);
Self { ptr, len, cap: len }
}
}
impl<T: Default + Copy> AlignedVec<T> {
#[allow(unsafe_code)]
pub fn filled(len: usize, val: T) -> Self {
if len == 0 {
return Self::new();
}
let ptr = alloc_aligned::<T>(len);
unsafe {
for i in 0..len {
ptr.add(i).write(val);
}
}
Self { ptr, len, cap: len }
}
pub fn zeros(len: usize) -> Self {
Self::filled(len, T::default())
}
#[allow(unsafe_code)]
pub fn calloc(len: usize) -> Self {
if len == 0 {
return Self::new();
}
let size = len
.checked_mul(std::mem::size_of::<T>())
.expect("allocation size overflow");
let size = size.max(1);
let layout =
std::alloc::Layout::from_size_align(size, ALIGN).expect("invalid allocation layout");
let ptr = unsafe { std::alloc::alloc_zeroed(layout) };
if ptr.is_null() {
std::alloc::handle_alloc_error(layout);
}
Self {
ptr: ptr as *mut T,
len,
cap: len,
}
}
}
impl<T> Drop for AlignedVec<T> {
#[allow(unsafe_code)]
fn drop(&mut self) {
if self.cap == 0 {
return;
}
unsafe {
std::ptr::drop_in_place(std::ptr::slice_from_raw_parts_mut(self.ptr, self.len));
}
dealloc_aligned::<T>(self.ptr, self.cap);
}
}
impl<T: Clone> Clone for AlignedVec<T> {
#[allow(unsafe_code)]
fn clone(&self) -> Self {
if self.len == 0 {
return Self::new();
}
let ptr = alloc_aligned::<T>(self.len);
unsafe {
for i in 0..self.len {
ptr.add(i).write((*self.ptr.add(i)).clone());
}
}
Self {
ptr,
len: self.len,
cap: self.len,
}
}
}
impl<T: fmt::Debug> fmt::Debug for AlignedVec<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.as_slice().iter()).finish()
}
}
impl<T: PartialEq> PartialEq for AlignedVec<T> {
fn eq(&self, other: &Self) -> bool {
self.as_slice() == other.as_slice()
}
}
impl<T> Deref for AlignedVec<T> {
type Target = [T];
#[inline]
fn deref(&self) -> &[T] {
self.as_slice()
}
}
impl<T> DerefMut for AlignedVec<T> {
#[inline]
fn deref_mut(&mut self) -> &mut [T] {
self.as_mut_slice()
}
}
impl<T> FromIterator<T> for AlignedVec<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let iter = iter.into_iter();
let (lower, _) = iter.size_hint();
let mut v = AlignedVec::with_capacity(lower);
for item in iter {
v.push(item);
}
v
}
}
#[allow(unsafe_code)]
mod alloc_cache {
use std::cell::RefCell;
const MAX_CACHED: usize = 8;
const ALIGN: usize = super::ALIGN;
struct AllocCache {
entries: Vec<(*mut u8, usize)>,
}
impl AllocCache {
const fn new() -> Self {
Self {
entries: Vec::new(),
}
}
}
impl Drop for AllocCache {
fn drop(&mut self) {
for &(ptr, size) in &self.entries {
if !ptr.is_null()
&& let Ok(layout) = std::alloc::Layout::from_size_align(size, ALIGN)
{
unsafe {
std::alloc::dealloc(ptr, layout);
}
}
}
}
}
thread_local! {
static CACHE: RefCell<AllocCache> = const { RefCell::new(AllocCache::new()) };
}
pub(super) fn try_alloc(size: usize) -> Option<*mut u8> {
if cfg!(miri) {
return None;
} CACHE
.try_with(|c| {
let mut cache = c.borrow_mut();
if let Some(pos) = cache.entries.iter().position(|&(_, s)| s == size) {
let (ptr, _) = cache.entries.swap_remove(pos);
Some(ptr)
} else {
None
}
})
.ok()
.flatten()
}
pub(super) fn try_dealloc(ptr: *mut u8, size: usize) -> bool {
if cfg!(miri) {
return false;
} CACHE
.try_with(|c| {
let mut cache = c.borrow_mut();
if cache.entries.len() < MAX_CACHED {
cache.entries.push((ptr, size));
true
} else {
false
}
})
.unwrap_or(false)
}
}
#[allow(unsafe_code)]
fn alloc_aligned<T>(count: usize) -> *mut T {
assert!(count > 0, "cannot allocate zero-sized aligned buffer");
let size = count
.checked_mul(std::mem::size_of::<T>())
.expect("allocation size overflow");
let size = size.max(1);
if let Some(ptr) = alloc_cache::try_alloc(size) {
return ptr as *mut T;
}
let layout =
std::alloc::Layout::from_size_align(size, ALIGN).expect("invalid allocation layout");
let ptr = unsafe { std::alloc::alloc(layout) };
if ptr.is_null() {
std::alloc::handle_alloc_error(layout);
}
ptr as *mut T
}
#[allow(unsafe_code)]
fn dealloc_aligned<T>(ptr: *mut T, cap: usize) {
let size = match cap.checked_mul(std::mem::size_of::<T>()) {
Some(s) => s.max(1),
None => return, };
if alloc_cache::try_dealloc(ptr as *mut u8, size) {
return;
}
if let Ok(layout) = std::alloc::Layout::from_size_align(size, ALIGN) {
unsafe {
std::alloc::dealloc(ptr as *mut u8, layout);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn alignment_is_32_bytes() {
let v = AlignedVec::<f32>::with_capacity(64);
assert_eq!(v.as_ptr() as usize % 32, 0);
}
#[test]
fn filled_and_len() {
let v = AlignedVec::filled(100, 1.0f32);
assert_eq!(v.len(), 100);
assert!(v.iter().all(|&x| x == 1.0));
assert_eq!(v.as_ptr() as usize % 32, 0);
}
#[test]
fn zeros_default() {
let v = AlignedVec::<f32>::zeros(16);
assert_eq!(v.len(), 16);
assert!(v.iter().all(|&x| x == 0.0));
}
#[test]
fn from_vec_copies_and_aligns() {
let orig = vec![1.0f32, 2.0, 3.0, 4.0];
let aligned = AlignedVec::from_vec(orig);
assert_eq!(aligned.as_slice(), &[1.0, 2.0, 3.0, 4.0]);
assert_eq!(aligned.as_ptr() as usize % 32, 0);
}
#[test]
fn push_and_grow() {
let mut v = AlignedVec::<f32>::new();
for i in 0..100 {
v.push(i as f32);
}
assert_eq!(v.len(), 100);
assert_eq!(v[50], 50.0);
assert_eq!(v.as_ptr() as usize % 32, 0);
}
#[test]
fn clone_preserves_alignment() {
let v = AlignedVec::filled(10, 42.0f32);
let v2 = v.clone();
assert_eq!(v.as_slice(), v2.as_slice());
assert_eq!(v2.as_ptr() as usize % 32, 0);
}
#[test]
fn from_iterator() {
let v: AlignedVec<f32> = (0..10).map(|i| i as f32).collect();
assert_eq!(v.len(), 10);
assert_eq!(v[5], 5.0);
assert_eq!(v.as_ptr() as usize % 32, 0);
}
#[test]
fn empty_vec_operations() {
let v = AlignedVec::<f32>::new();
assert!(v.is_empty());
assert_eq!(v.len(), 0);
assert_eq!(v.as_slice(), &[] as &[f32]);
}
#[test]
fn deref_slice_access() {
let v = AlignedVec::filled(5, 3.0f32);
let sum: f32 = v.iter().sum();
assert_eq!(sum, 15.0);
}
}