use std::fmt;
use std::ops::{Deref, DerefMut};
#[cfg(not(feature = "std-vec-payload"))]
use std::{marker::PhantomData, mem::ManuallyDrop, ptr::NonNull};
#[cfg(not(feature = "std-vec-payload"))]
#[repr(C)]
pub struct ReprCVec<T> {
ptr: NonNull<T>,
len: usize,
cap: usize,
_owns: PhantomData<T>,
}
#[cfg(feature = "std-vec-payload")]
#[repr(transparent)]
pub struct ReprCVec<T> {
inner: Vec<T>,
}
const _: () = assert!(
std::mem::size_of::<ReprCVec<crate::GcRef>>() == std::mem::size_of::<Vec<crate::GcRef>>()
);
const _: () = assert!(
std::mem::align_of::<ReprCVec<crate::GcRef>>() == std::mem::align_of::<Vec<crate::GcRef>>()
);
const _: () = assert!(std::mem::size_of::<ReprCVec<crate::GcRef>>() == 24);
#[cfg(not(feature = "std-vec-payload"))]
mod layout {
use super::ReprCVec;
use crate::GcRef;
use std::mem::offset_of;
const _: () = assert!(offset_of!(ReprCVec<GcRef>, ptr) == 0);
const _: () = assert!(offset_of!(ReprCVec<GcRef>, len) == 8);
const _: () = assert!(offset_of!(ReprCVec<GcRef>, cap) == 16);
const _: () = assert!(offset_of!(ReprCVec<u64>, ptr) == 0);
const _: () = assert!(offset_of!(ReprCVec<u64>, len) == 8);
const _: () = assert!(offset_of!(ReprCVec<u64>, cap) == 16);
}
#[cfg(not(feature = "std-vec-payload"))]
pub const REPR_C_VEC_ELEMENTS_OFFSET: usize = std::mem::offset_of!(ReprCVec<crate::GcRef>, ptr);
#[cfg(not(feature = "std-vec-payload"))]
pub const REPR_C_VEC_LEN_OFFSET: usize = std::mem::offset_of!(ReprCVec<crate::GcRef>, len);
#[cfg(not(feature = "std-vec-payload"))]
#[derive(Clone, Copy, Debug)]
pub struct InlineSliceSite {
type_id: crate::descriptor::BuiltinTypeId,
elements_offset: usize,
len_offset: usize,
element_shift: u8,
}
#[cfg(not(feature = "std-vec-payload"))]
impl InlineSliceSite {
pub(crate) const fn new(
type_id: crate::descriptor::BuiltinTypeId,
payload_align: usize,
field_offset: usize,
element_size: usize,
) -> InlineSliceSite {
assert!(
element_size.is_power_of_two(),
"the element scale must be a shift"
);
let base = crate::GcHeader::payload_offset_for(payload_align) + field_offset;
InlineSliceSite {
type_id,
elements_offset: base + REPR_C_VEC_ELEMENTS_OFFSET,
len_offset: base + REPR_C_VEC_LEN_OFFSET,
element_shift: element_size.trailing_zeros() as u8,
}
}
#[must_use]
pub const fn type_id(self) -> crate::descriptor::BuiltinTypeId {
self.type_id
}
#[must_use]
pub const fn elements_offset(self) -> usize {
self.elements_offset
}
#[must_use]
pub const fn len_offset(self) -> usize {
self.len_offset
}
#[must_use]
pub const fn element_shift(self) -> u8 {
self.element_shift
}
}
#[cfg(not(feature = "std-vec-payload"))]
impl<T> ReprCVec<T> {
#[inline]
#[must_use]
pub fn from_vec(vec: Vec<T>) -> Self {
let mut vec = ManuallyDrop::new(vec);
let (ptr, len, cap) = (vec.as_mut_ptr(), vec.len(), vec.capacity());
Self {
ptr: unsafe { NonNull::new_unchecked(ptr) },
len,
cap,
_owns: PhantomData,
}
}
#[inline]
#[must_use]
pub fn into_vec(self) -> Vec<T> {
let me = ManuallyDrop::new(self);
unsafe { Vec::from_raw_parts(me.ptr.as_ptr(), me.len, me.cap) }
}
#[inline]
#[must_use]
pub fn as_slice(&self) -> &[T] {
unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
}
#[inline]
#[must_use]
pub fn as_mut_slice(&mut self) -> &mut [T] {
unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
}
#[inline]
#[must_use]
pub fn capacity(&self) -> usize {
self.cap
}
#[inline]
pub fn vec_mut(&mut self) -> VecMut<'_, T> {
let taken = std::mem::take(self).into_vec();
VecMut {
vec: ManuallyDrop::new(taken),
owner: self,
}
}
}
#[cfg(feature = "std-vec-payload")]
impl<T> ReprCVec<T> {
#[inline]
#[must_use]
pub fn from_vec(vec: Vec<T>) -> Self {
Self { inner: vec }
}
#[inline]
#[must_use]
pub fn into_vec(self) -> Vec<T> {
self.inner
}
#[inline]
#[must_use]
pub fn as_slice(&self) -> &[T] {
self.inner.as_slice()
}
#[inline]
#[must_use]
pub fn as_mut_slice(&mut self) -> &mut [T] {
self.inner.as_mut_slice()
}
#[inline]
#[must_use]
pub fn capacity(&self) -> usize {
self.inner.capacity()
}
#[inline]
pub fn vec_mut(&mut self) -> VecMut<'_, T> {
VecMut { owner: self }
}
}
#[cfg(not(feature = "std-vec-payload"))]
pub struct VecMut<'a, T> {
vec: ManuallyDrop<Vec<T>>,
owner: &'a mut ReprCVec<T>,
}
#[cfg(not(feature = "std-vec-payload"))]
impl<T> Drop for VecMut<'_, T> {
#[inline]
fn drop(&mut self) {
let vec = unsafe { ManuallyDrop::take(&mut self.vec) };
*self.owner = ReprCVec::from_vec(vec);
}
}
#[cfg(not(feature = "std-vec-payload"))]
impl<T> Deref for VecMut<'_, T> {
type Target = Vec<T>;
#[inline]
fn deref(&self) -> &Vec<T> {
&self.vec
}
}
#[cfg(not(feature = "std-vec-payload"))]
impl<T> DerefMut for VecMut<'_, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Vec<T> {
&mut self.vec
}
}
#[cfg(feature = "std-vec-payload")]
pub struct VecMut<'a, T> {
owner: &'a mut ReprCVec<T>,
}
#[cfg(feature = "std-vec-payload")]
impl<T> Deref for VecMut<'_, T> {
type Target = Vec<T>;
#[inline]
fn deref(&self) -> &Vec<T> {
&self.owner.inner
}
}
#[cfg(feature = "std-vec-payload")]
impl<T> DerefMut for VecMut<'_, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Vec<T> {
&mut self.owner.inner
}
}
impl<T> ReprCVec<T> {
#[inline]
#[must_use]
pub fn new() -> Self {
Self::from_vec(Vec::new())
}
#[inline]
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self::from_vec(Vec::with_capacity(capacity))
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.as_slice().len()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn push(&mut self, value: T) {
self.vec_mut().push(value);
}
#[inline]
pub fn pop(&mut self) -> Option<T> {
self.vec_mut().pop()
}
#[inline]
pub fn insert(&mut self, index: usize, value: T) {
self.vec_mut().insert(index, value);
}
#[inline]
pub fn remove(&mut self, index: usize) -> T {
self.vec_mut().remove(index)
}
#[inline]
pub fn swap_remove(&mut self, index: usize) -> T {
self.vec_mut().swap_remove(index)
}
#[inline]
pub fn clear(&mut self) {
self.vec_mut().clear();
}
#[inline]
pub fn truncate(&mut self, len: usize) {
self.vec_mut().truncate(len);
}
#[inline]
pub fn retain(&mut self, f: impl FnMut(&T) -> bool) {
self.vec_mut().retain(f);
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.vec_mut().reserve(additional);
}
#[inline]
pub fn append(&mut self, other: &mut Vec<T>) {
self.vec_mut().append(other);
}
}
impl<T: Clone> ReprCVec<T> {
#[inline]
pub fn extend_from_slice(&mut self, other: &[T]) {
self.vec_mut().extend_from_slice(other);
}
#[inline]
pub fn resize(&mut self, len: usize, value: T) {
self.vec_mut().resize(len, value);
}
}
impl<T> Default for ReprCVec<T> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<T> Deref for ReprCVec<T> {
type Target = [T];
#[inline]
fn deref(&self) -> &[T] {
self.as_slice()
}
}
impl<T> DerefMut for ReprCVec<T> {
#[inline]
fn deref_mut(&mut self) -> &mut [T] {
self.as_mut_slice()
}
}
impl<'a, T> IntoIterator for &'a ReprCVec<T> {
type Item = &'a T;
type IntoIter = std::slice::Iter<'a, T>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.as_slice().iter()
}
}
impl<'a, T> IntoIterator for &'a mut ReprCVec<T> {
type Item = &'a mut T;
type IntoIter = std::slice::IterMut<'a, T>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.as_mut_slice().iter_mut()
}
}
impl<T> IntoIterator for ReprCVec<T> {
type Item = T;
type IntoIter = std::vec::IntoIter<T>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.into_vec().into_iter()
}
}
impl<T> Extend<T> for ReprCVec<T> {
#[inline]
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
self.vec_mut().extend(iter);
}
}
impl<T> FromIterator<T> for ReprCVec<T> {
#[inline]
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
Self::from_vec(iter.into_iter().collect())
}
}
impl<T> From<Vec<T>> for ReprCVec<T> {
#[inline]
fn from(vec: Vec<T>) -> Self {
Self::from_vec(vec)
}
}
impl<T> From<ReprCVec<T>> for Vec<T> {
#[inline]
fn from(vec: ReprCVec<T>) -> Self {
vec.into_vec()
}
}
impl<T: Clone> Clone for ReprCVec<T> {
#[inline]
fn clone(&self) -> Self {
Self::from_vec(self.as_slice().to_vec())
}
}
impl<T: fmt::Debug> fmt::Debug for ReprCVec<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self.as_slice(), f)
}
}
impl<T: PartialEq> PartialEq for ReprCVec<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.as_slice() == other.as_slice()
}
}
impl<T: Eq> Eq for ReprCVec<T> {}
#[cfg(not(feature = "std-vec-payload"))]
impl<T> Drop for ReprCVec<T> {
#[inline]
fn drop(&mut self) {
drop(std::mem::take(self).into_vec());
}
}
#[cfg(not(feature = "std-vec-payload"))]
unsafe impl<T: Send> Send for ReprCVec<T> {}
#[cfg(not(feature = "std-vec-payload"))]
unsafe impl<T: Sync> Sync for ReprCVec<T> {}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
#[test]
fn a_repr_c_vec_is_the_same_three_words_a_std_vec_is() {
assert_eq!(
std::mem::size_of::<ReprCVec<u64>>(),
std::mem::size_of::<Vec<u64>>()
);
assert_eq!(
std::mem::align_of::<ReprCVec<u64>>(),
std::mem::align_of::<Vec<u64>>()
);
}
#[cfg(not(feature = "std-vec-payload"))]
#[test]
fn the_pointer_is_first_the_length_is_second_and_the_capacity_is_last() {
use std::mem::offset_of;
assert_eq!(offset_of!(ReprCVec<u64>, ptr), 0);
assert_eq!(offset_of!(ReprCVec<u64>, len), 8);
assert_eq!(offset_of!(ReprCVec<u64>, cap), 16);
}
#[cfg(not(feature = "std-vec-payload"))]
#[test]
fn the_length_word_is_readable_at_its_declared_offset() {
let v = ReprCVec::from_vec(vec![1_u64, 2, 3, 4, 5]);
let base = std::ptr::addr_of!(v).cast::<u8>();
let len = unsafe { base.add(8).cast::<usize>().read() };
assert_eq!(len, 5);
let ptr = unsafe { base.cast::<*const u64>().read() };
assert_eq!(unsafe { *ptr.add(3) }, 4);
}
#[test]
fn a_vec_survives_the_round_trip_with_its_capacity_intact() {
let mut original: Vec<u64> = Vec::with_capacity(17);
original.extend([10, 20, 30]);
let capacity = original.capacity();
assert!(
capacity >= 17,
"with_capacity should over-reserve, not exact"
);
let wrapped = ReprCVec::from_vec(original);
assert_eq!(wrapped.len(), 3);
assert_eq!(wrapped.capacity(), capacity);
assert_eq!(wrapped.as_slice(), &[10, 20, 30]);
let back = wrapped.into_vec();
assert_eq!(back, vec![10, 20, 30]);
assert_eq!(
back.capacity(),
capacity,
"the capacity is the allocation's, and it must survive the trip"
);
}
#[test]
fn an_empty_vec_round_trips_without_touching_the_allocator() {
let wrapped = ReprCVec::from_vec(Vec::<u64>::new());
assert_eq!(wrapped.capacity(), 0);
assert!(wrapped.is_empty());
let back = wrapped.into_vec();
assert_eq!(back.capacity(), 0);
assert!(back.is_empty());
}
#[test]
fn a_push_that_reallocates_leaves_the_container_pointing_at_the_new_buffer() {
let mut v = ReprCVec::<u64>::new();
for i in 0..1000_u64 {
v.push(i);
}
assert_eq!(v.len(), 1000);
assert_eq!(v[0], 0);
assert_eq!(v[999], 999);
assert!(v.capacity() >= 1000);
assert_eq!(v.iter().sum::<u64>(), (0..1000).sum::<u64>());
}
struct DropProbe(Arc<AtomicUsize>);
impl Drop for DropProbe {
fn drop(&mut self) {
self.0.fetch_add(1, Ordering::SeqCst);
}
}
#[test]
fn dropping_the_container_drops_every_element_exactly_once() {
let count = Arc::new(AtomicUsize::new(0));
{
let mut v = ReprCVec::new();
for _ in 0..64 {
v.push(DropProbe(Arc::clone(&count)));
}
assert_eq!(count.load(Ordering::SeqCst), 0, "no drops while it lives");
}
assert_eq!(count.load(Ordering::SeqCst), 64);
}
#[test]
fn a_round_trip_through_a_vec_does_not_drop_anything() {
let count = Arc::new(AtomicUsize::new(0));
let v = ReprCVec::from_vec(vec![
DropProbe(Arc::clone(&count)),
DropProbe(Arc::clone(&count)),
]);
let back = v.into_vec();
assert_eq!(count.load(Ordering::SeqCst), 0);
drop(back);
assert_eq!(count.load(Ordering::SeqCst), 2);
}
#[test]
fn a_removed_element_is_dropped_by_its_new_owner_and_not_by_the_container() {
let count = Arc::new(AtomicUsize::new(0));
let mut v = ReprCVec::new();
v.push(DropProbe(Arc::clone(&count)));
v.push(DropProbe(Arc::clone(&count)));
let taken = v.pop().expect("two were pushed");
assert_eq!(count.load(Ordering::SeqCst), 0);
drop(taken);
assert_eq!(count.load(Ordering::SeqCst), 1);
drop(v);
assert_eq!(count.load(Ordering::SeqCst), 2);
}
#[test]
fn clear_and_truncate_drop_what_they_remove() {
let count = Arc::new(AtomicUsize::new(0));
let mut v = ReprCVec::new();
for _ in 0..10 {
v.push(DropProbe(Arc::clone(&count)));
}
v.truncate(4);
assert_eq!(count.load(Ordering::SeqCst), 6);
assert_eq!(v.len(), 4);
v.clear();
assert_eq!(count.load(Ordering::SeqCst), 10);
assert!(v.is_empty());
assert!(v.capacity() >= 10);
}
#[cfg(not(feature = "std-vec-payload"))]
#[test]
fn a_forgotten_mutation_guard_leaves_the_container_empty_rather_than_stale() {
let count = Arc::new(AtomicUsize::new(0));
let mut v = ReprCVec::new();
v.push(DropProbe(Arc::clone(&count)));
let mut guard = v.vec_mut();
guard.push(DropProbe(Arc::clone(&count)));
std::mem::forget(guard);
assert_eq!(v.len(), 0, "a forgotten guard leaves an empty container");
assert_eq!(v.capacity(), 0);
assert!(v.iter().next().is_none());
drop(v);
assert_eq!(count.load(Ordering::SeqCst), 0);
}
#[test]
fn a_mutation_that_panics_still_hands_the_elements_back() {
let mut v = ReprCVec::from_vec(vec![1_u64, 2, 3]);
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let mut guard = v.vec_mut();
guard.push(4);
panic!("the wrapper faulted mid-mutation");
}));
assert!(result.is_err());
assert_eq!(
v.as_slice(),
&[1, 2, 3, 4],
"the guard's Drop runs on the unwind path too"
);
}
#[test]
fn the_reading_api_is_the_slice_api() {
let v = ReprCVec::from_vec(vec![3_u64, 1, 2]);
assert_eq!(v.len(), 3);
assert_eq!(v[1], 1);
assert_eq!(v.first().copied(), Some(3));
assert_eq!(v.last().copied(), Some(2));
assert!(v.contains(&2));
assert_eq!(v.iter().copied().max(), Some(3));
let doubled: Vec<u64> = (&v).into_iter().map(|x| x * 2).collect();
assert_eq!(doubled, vec![6, 2, 4]);
}
#[test]
fn the_mutable_slice_api_reaches_through_deref_mut() {
let mut v = ReprCVec::from_vec(vec![3_u64, 1, 2]);
v.sort_unstable();
assert_eq!(v.as_slice(), &[1, 2, 3]);
v.swap(0, 2);
assert_eq!(v.as_slice(), &[3, 2, 1]);
for x in &mut v {
*x += 1;
}
assert_eq!(v.as_slice(), &[4, 3, 2]);
}
#[test]
fn extend_insert_remove_and_retain_agree_with_a_std_vec() {
let mut ours = ReprCVec::<u64>::new();
let mut theirs = Vec::<u64>::new();
ours.extend(0..20);
theirs.extend(0..20);
ours.insert(5, 99);
theirs.insert(5, 99);
assert_eq!(ours.remove(0), theirs.remove(0));
assert_eq!(ours.swap_remove(3), theirs.swap_remove(3));
ours.retain(|x| x % 2 == 0);
theirs.retain(|x| x % 2 == 0);
ours.extend_from_slice(&[7, 7, 7]);
theirs.extend_from_slice(&[7, 7, 7]);
assert_eq!(ours.as_slice(), theirs.as_slice());
assert_eq!(ours.into_vec(), theirs);
}
#[test]
fn collect_clone_and_equality_behave() {
let v: ReprCVec<u64> = (0..5).collect();
let w = v.clone();
assert_eq!(v, w);
assert_eq!(format!("{v:?}"), "[0, 1, 2, 3, 4]");
let owned: Vec<u64> = v.into_iter().collect();
assert_eq!(owned, vec![0, 1, 2, 3, 4]);
}
#[test]
fn a_zero_sized_element_type_round_trips() {
let v = ReprCVec::from_vec(vec![(), (), ()]);
assert_eq!(v.len(), 3);
let back = v.into_vec();
assert_eq!(back.len(), 3);
}
}