use std::alloc::{alloc, dealloc, realloc, Layout};
use std::fmt;
use std::iter::FromIterator;
use std::mem::{self, ManuallyDrop};
use std::ops::{Deref, DerefMut, Index, IndexMut};
use std::ptr::{self, NonNull};
use std::slice;
pub struct CompactVec<T> {
ptr: NonNull<T>,
len_cap: u64,
}
unsafe impl<T: Send> Send for CompactVec<T> {}
unsafe impl<T: Sync> Sync for CompactVec<T> {}
impl<T> CompactVec<T> {
#[inline(always)]
const fn pack(len: u32, cap: u32) -> u64 {
(len as u64) | ((cap as u64) << 32)
}
#[inline(always)]
const fn unpack_len(len_cap: u64) -> u32 {
len_cap as u32
}
#[inline(always)]
const fn unpack_cap(len_cap: u64) -> u32 {
(len_cap >> 32) as u32
}
#[inline]
pub const fn new() -> Self {
Self {
ptr: NonNull::dangling(),
len_cap: 0,
}
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
if capacity == 0 {
return Self::new();
}
let cap = capacity.min(u32::MAX as usize) as u32;
let layout = Layout::array::<T>(cap as usize).unwrap();
let ptr = unsafe { alloc(layout) as *mut T };
if ptr.is_null() {
std::alloc::handle_alloc_error(layout);
}
Self {
ptr: unsafe { NonNull::new_unchecked(ptr) },
len_cap: Self::pack(0, cap),
}
}
#[inline(always)]
pub const fn len(&self) -> usize {
Self::unpack_len(self.len_cap) as usize
}
#[inline(always)]
pub const fn is_empty(&self) -> bool {
Self::unpack_len(self.len_cap) == 0
}
#[inline(always)]
pub const fn capacity(&self) -> usize {
Self::unpack_cap(self.len_cap) as usize
}
#[inline(always)]
pub unsafe fn set_len(&mut self, new_len: usize) {
let cap = Self::unpack_cap(self.len_cap);
self.len_cap = Self::pack(new_len as u32, cap);
}
#[inline(always)]
pub fn as_ptr(&self) -> *const T {
self.ptr.as_ptr()
}
#[inline(always)]
pub fn as_mut_ptr(&mut self) -> *mut T {
self.ptr.as_ptr()
}
#[inline]
pub fn push(&mut self, value: T) {
let len = Self::unpack_len(self.len_cap) as usize;
let cap = Self::unpack_cap(self.len_cap) as usize;
if len == cap {
self.grow();
}
unsafe {
ptr::write(self.ptr.as_ptr().add(len), value);
self.set_len(len + 1);
}
}
#[inline]
pub fn pop(&mut self) -> Option<T> {
let len = self.len();
if len == 0 {
return None;
}
unsafe {
self.set_len(len - 1);
Some(ptr::read(self.ptr.as_ptr().add(len - 1)))
}
}
#[inline]
pub fn clear(&mut self) {
let len = self.len();
if len == 0 {
return;
}
unsafe {
ptr::drop_in_place(ptr::slice_from_raw_parts_mut(self.ptr.as_ptr(), len));
self.set_len(0);
}
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
let len = Self::unpack_len(self.len_cap) as usize;
let cap = Self::unpack_cap(self.len_cap) as usize;
let required = len.saturating_add(additional);
if required > cap {
let new_cap = required.min(u32::MAX as usize);
self.realloc(new_cap);
}
}
fn grow(&mut self) {
let cap = self.capacity();
let new_cap = if cap == 0 {
4
} else {
cap.saturating_mul(2).min(u32::MAX as usize)
};
self.realloc(new_cap);
}
fn realloc(&mut self, new_cap: usize) {
let len = self.len();
let old_cap = self.capacity();
let new_cap = new_cap as u32;
if mem::size_of::<T>() == 0 {
self.len_cap = Self::pack(len as u32, new_cap);
return;
}
let new_layout = Layout::array::<T>(new_cap as usize).unwrap();
let new_ptr = if old_cap == 0 {
unsafe { alloc(new_layout) as *mut T }
} else {
let old_layout = Layout::array::<T>(old_cap).unwrap();
unsafe {
realloc(self.ptr.as_ptr() as *mut u8, old_layout, new_layout.size()) as *mut T
}
};
if new_ptr.is_null() {
std::alloc::handle_alloc_error(new_layout);
}
self.ptr = unsafe { NonNull::new_unchecked(new_ptr) };
self.len_cap = Self::pack(len as u32, new_cap);
}
#[inline]
pub fn truncate(&mut self, len: usize) {
let current_len = self.len();
if len >= current_len {
return;
}
unsafe {
let remaining = current_len - len;
ptr::drop_in_place(ptr::slice_from_raw_parts_mut(
self.ptr.as_ptr().add(len),
remaining,
));
self.set_len(len);
}
}
#[inline]
pub fn remove(&mut self, index: usize) -> T {
let len = self.len();
assert!(index < len, "removal index out of bounds");
unsafe {
let ptr = self.ptr.as_ptr().add(index);
let value = ptr::read(ptr);
ptr::copy(ptr.add(1), ptr, len - index - 1);
self.set_len(len - 1);
value
}
}
#[inline]
pub fn swap_remove(&mut self, index: usize) -> T {
let len = self.len();
assert!(index < len, "swap_remove index out of bounds");
unsafe {
let ptr = self.ptr.as_ptr();
let value = ptr::read(ptr.add(index));
if index < len - 1 {
ptr::copy_nonoverlapping(ptr.add(len - 1), ptr.add(index), 1);
}
self.set_len(len - 1);
value
}
}
#[inline]
pub fn insert(&mut self, index: usize, element: T) {
let len = self.len();
assert!(index <= len, "insertion index out of bounds");
if len == self.capacity() {
self.grow();
}
unsafe {
let ptr = self.ptr.as_ptr().add(index);
if index < len {
ptr::copy(ptr, ptr.add(1), len - index);
}
ptr::write(ptr, element);
self.set_len(len + 1);
}
}
#[inline]
pub fn retain<F>(&mut self, mut f: F)
where
F: FnMut(&T) -> bool,
{
let len = self.len();
let ptr = self.ptr.as_ptr();
let mut write_idx = 0;
for read_idx in 0..len {
unsafe {
let elem = &*ptr.add(read_idx);
if f(elem) {
if write_idx != read_idx {
ptr::copy_nonoverlapping(ptr.add(read_idx), ptr.add(write_idx), 1);
}
write_idx += 1;
} else {
ptr::drop_in_place(ptr.add(read_idx));
}
}
}
unsafe {
self.set_len(write_idx);
}
}
#[inline]
pub fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
let iter = iter.into_iter();
let (lower, upper) = iter.size_hint();
if Some(lower) == upper && lower > 0 {
self.reserve(lower);
let original_len = self.len();
let cap = self.capacity();
struct ExtendGuard<'a, T> {
vec: &'a mut CompactVec<T>,
written_count: usize,
}
impl<T> Drop for ExtendGuard<'_, T> {
fn drop(&mut self) {
unsafe {
self.vec.set_len(self.written_count);
}
}
}
let mut guard = ExtendGuard {
vec: self,
written_count: original_len,
};
unsafe {
let ptr = guard.vec.ptr.as_ptr();
for item in iter {
if guard.written_count >= cap {
let count = guard.written_count;
guard.vec.set_len(count);
guard.vec.push(item);
guard.written_count = guard.vec.len();
continue;
}
ptr::write(ptr.add(guard.written_count), item);
guard.written_count += 1;
}
}
let final_len = guard.written_count;
mem::forget(guard);
unsafe {
self.set_len(final_len);
}
} else {
self.reserve(lower);
for item in iter {
self.push(item);
}
}
}
#[inline]
pub fn extend_clone(&mut self, slice: &[T])
where
T: Clone,
{
let slice_len = slice.len();
if slice_len == 0 {
return;
}
self.reserve(slice_len);
let original_len = self.len();
struct ExtendCloneGuard<'a, T> {
vec: &'a mut CompactVec<T>,
written_count: usize,
}
impl<T> Drop for ExtendCloneGuard<'_, T> {
fn drop(&mut self) {
unsafe {
self.vec.set_len(self.written_count);
}
}
}
let mut guard = ExtendCloneGuard {
vec: self,
written_count: original_len,
};
unsafe {
let mut dst = guard.vec.ptr.as_ptr().add(original_len);
for item in slice {
ptr::write(dst, item.clone());
guard.written_count += 1;
dst = dst.add(1);
}
}
let final_len = guard.written_count;
mem::forget(guard);
unsafe {
self.set_len(final_len);
}
}
#[inline]
pub fn extend_copy(&mut self, slice: &[T])
where
T: Copy,
{
let slice_len = slice.len();
if slice_len == 0 {
return;
}
self.reserve(slice_len);
let len = self.len();
unsafe {
let dst = self.ptr.as_ptr().add(len);
ptr::copy_nonoverlapping(slice.as_ptr(), dst, slice_len);
self.set_len(len + slice_len);
}
}
#[inline(always)]
pub fn as_slice(&self) -> &[T] {
unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len()) }
}
#[inline(always)]
pub fn as_mut_slice(&mut self) -> &mut [T] {
unsafe { &mut *ptr::slice_from_raw_parts_mut(self.ptr.as_ptr(), self.len()) }
}
#[inline]
pub fn iter(&self) -> slice::Iter<'_, T> {
self.as_slice().iter()
}
#[inline]
pub fn iter_mut(&mut self) -> slice::IterMut<'_, T> {
self.as_mut_slice().iter_mut()
}
#[inline]
pub fn drain(&mut self, range: std::ops::RangeFrom<usize>) -> Drain<'_, T> {
let start = range.start;
let len = self.len();
assert!(start <= len, "drain start index out of bounds");
Drain {
vec: self,
start,
current: start,
end: len,
}
}
#[inline(always)]
pub fn get(&self, index: usize) -> Option<&T> {
if index < self.len() {
unsafe { Some(&*self.ptr.as_ptr().add(index)) }
} else {
None
}
}
#[inline(always)]
pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
if index < self.len() {
unsafe { Some(&mut *self.ptr.as_ptr().add(index)) }
} else {
None
}
}
#[inline]
pub fn into_vec(self) -> Vec<T> {
let len = self.len();
let cap = self.capacity();
if cap == 0 {
mem::forget(self);
return Vec::new();
}
let ptr = self.ptr.as_ptr();
mem::forget(self);
unsafe { Vec::from_raw_parts(ptr, len, cap) }
}
#[inline]
pub fn into_boxed_slice(self) -> Box<[T]> {
self.into_vec().into_boxed_slice()
}
#[inline]
pub fn from_vec(vec: Vec<T>) -> Self {
let len = vec.len().min(u32::MAX as usize) as u32;
let cap = vec.capacity().min(u32::MAX as usize) as u32;
if cap == 0 {
mem::forget(vec);
return Self::new();
}
let mut vec = ManuallyDrop::new(vec);
let ptr = vec.as_mut_ptr();
Self {
ptr: unsafe { NonNull::new_unchecked(ptr) },
len_cap: Self::pack(len, cap),
}
}
}
impl<T: Clone> CompactVec<T> {
pub fn resize(&mut self, new_len: usize, value: T) {
let len = self.len();
if new_len > len {
self.reserve(new_len - len);
for _ in len..new_len {
self.push(value.clone());
}
} else {
self.truncate(new_len);
}
}
}
impl<T> Drop for CompactVec<T> {
fn drop(&mut self) {
if self.capacity() == 0 {
return;
}
let len = self.len();
if len > 0 {
unsafe {
ptr::drop_in_place(ptr::slice_from_raw_parts_mut(self.ptr.as_ptr(), len));
}
}
if mem::size_of::<T>() > 0 {
let layout = Layout::array::<T>(self.capacity()).unwrap();
unsafe {
dealloc(self.ptr.as_ptr() as *mut u8, layout);
}
}
}
}
impl<T: Clone> Clone for CompactVec<T> {
fn clone(&self) -> Self {
let len = self.len();
if len == 0 {
return Self::new();
}
let mut new_vec = Self::with_capacity(len);
struct CloneGuard<'a, T> {
vec: &'a mut CompactVec<T>,
cloned_count: usize,
}
impl<T> Drop for CloneGuard<'_, T> {
fn drop(&mut self) {
unsafe {
self.vec.set_len(self.cloned_count);
}
}
}
let mut guard = CloneGuard {
vec: &mut new_vec,
cloned_count: 0,
};
unsafe {
let src = self.ptr.as_ptr();
let dst = guard.vec.ptr.as_ptr();
for i in 0..len {
ptr::write(dst.add(i), (*src.add(i)).clone());
guard.cloned_count += 1;
}
}
let cloned = guard.cloned_count;
mem::forget(guard);
unsafe {
new_vec.set_len(cloned);
}
new_vec
}
}
impl<T> Default for CompactVec<T> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<T: fmt::Debug> fmt::Debug for CompactVec<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}
impl<T> Deref for CompactVec<T> {
type Target = [T];
#[inline(always)]
fn deref(&self) -> &[T] {
self.as_slice()
}
}
impl<T> DerefMut for CompactVec<T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut [T] {
self.as_mut_slice()
}
}
impl<T> Index<usize> for CompactVec<T> {
type Output = T;
#[inline(always)]
fn index(&self, index: usize) -> &T {
&self.as_slice()[index]
}
}
impl<T> IndexMut<usize> for CompactVec<T> {
#[inline(always)]
fn index_mut(&mut self, index: usize) -> &mut T {
&mut self.as_mut_slice()[index]
}
}
impl<T> Index<std::ops::Range<usize>> for CompactVec<T> {
type Output = [T];
#[inline(always)]
fn index(&self, range: std::ops::Range<usize>) -> &[T] {
&self.as_slice()[range]
}
}
impl<T> Index<std::ops::RangeFrom<usize>> for CompactVec<T> {
type Output = [T];
#[inline(always)]
fn index(&self, range: std::ops::RangeFrom<usize>) -> &[T] {
&self.as_slice()[range]
}
}
impl<T> Index<std::ops::RangeTo<usize>> for CompactVec<T> {
type Output = [T];
#[inline(always)]
fn index(&self, range: std::ops::RangeTo<usize>) -> &[T] {
&self.as_slice()[range]
}
}
impl<T> Index<std::ops::RangeFull> for CompactVec<T> {
type Output = [T];
#[inline(always)]
fn index(&self, _range: std::ops::RangeFull) -> &[T] {
self.as_slice()
}
}
impl<T> FromIterator<T> for CompactVec<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let iter = iter.into_iter();
let (lower, upper) = iter.size_hint();
if Some(lower) == upper && lower > 0 {
let mut vec = Self::with_capacity(lower);
let cap = vec.capacity();
struct FromIterGuard<'a, T> {
vec: &'a mut CompactVec<T>,
written_count: usize,
}
impl<T> Drop for FromIterGuard<'_, T> {
fn drop(&mut self) {
unsafe {
self.vec.set_len(self.written_count);
}
}
}
let mut guard = FromIterGuard {
vec: &mut vec,
written_count: 0,
};
unsafe {
let ptr = guard.vec.ptr.as_ptr();
for item in iter {
if guard.written_count >= cap {
let count = guard.written_count;
guard.vec.set_len(count);
guard.vec.push(item);
guard.written_count = guard.vec.len();
continue;
}
ptr::write(ptr.add(guard.written_count), item);
guard.written_count += 1;
}
}
let final_len = guard.written_count;
mem::forget(guard);
unsafe {
vec.set_len(final_len);
}
vec
} else {
let mut vec = Self::with_capacity(lower);
for item in iter {
vec.push(item);
}
vec
}
}
}
impl<T> IntoIterator for CompactVec<T> {
type Item = T;
type IntoIter = IntoIter<T>;
fn into_iter(self) -> IntoIter<T> {
IntoIter::new(self)
}
}
impl<'a, T> IntoIterator for &'a CompactVec<T> {
type Item = &'a T;
type IntoIter = slice::Iter<'a, T>;
fn into_iter(self) -> slice::Iter<'a, T> {
self.iter()
}
}
impl<'a, T> IntoIterator for &'a mut CompactVec<T> {
type Item = &'a mut T;
type IntoIter = slice::IterMut<'a, T>;
fn into_iter(self) -> slice::IterMut<'a, T> {
self.iter_mut()
}
}
impl<T: PartialEq> PartialEq for CompactVec<T> {
fn eq(&self, other: &Self) -> bool {
self.as_slice() == other.as_slice()
}
}
impl<T: Eq> Eq for CompactVec<T> {}
impl<T> From<Vec<T>> for CompactVec<T> {
fn from(vec: Vec<T>) -> Self {
Self::from_vec(vec)
}
}
impl<T> From<CompactVec<T>> for Vec<T> {
fn from(vec: CompactVec<T>) -> Self {
vec.into_vec()
}
}
pub struct Drain<'a, T> {
vec: &'a mut CompactVec<T>,
start: usize,
current: usize,
end: usize,
}
impl<'a, T> Iterator for Drain<'a, T> {
type Item = T;
#[inline]
fn next(&mut self) -> Option<T> {
if self.current < self.end {
let item = unsafe { ptr::read(self.vec.ptr.as_ptr().add(self.current)) };
self.current += 1;
Some(item)
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.end - self.current;
(remaining, Some(remaining))
}
}
impl<'a, T> ExactSizeIterator for Drain<'a, T> {
fn len(&self) -> usize {
self.end - self.current
}
}
impl<'a, T> Drop for Drain<'a, T> {
fn drop(&mut self) {
while self.current < self.end {
unsafe {
ptr::drop_in_place(self.vec.ptr.as_ptr().add(self.current));
}
self.current += 1;
}
unsafe {
self.vec.set_len(self.start);
}
}
}
pub struct IntoIter<T> {
vec: CompactVec<T>,
index: usize,
}
impl<T> IntoIter<T> {
fn new(vec: CompactVec<T>) -> Self {
Self { vec, index: 0 }
}
}
impl<T> Iterator for IntoIter<T> {
type Item = T;
fn next(&mut self) -> Option<T> {
if self.index < self.vec.len() {
let item = unsafe { ptr::read(self.vec.ptr.as_ptr().add(self.index)) };
self.index += 1;
Some(item)
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.vec.len() - self.index;
(remaining, Some(remaining))
}
}
impl<T> ExactSizeIterator for IntoIter<T> {
fn len(&self) -> usize {
self.vec.len() - self.index
}
}
impl<T> Drop for IntoIter<T> {
fn drop(&mut self) {
let len = self.vec.len();
if self.index < len {
unsafe {
let remaining = len - self.index;
ptr::drop_in_place(ptr::slice_from_raw_parts_mut(
self.vec.ptr.as_ptr().add(self.index),
remaining,
));
}
}
unsafe {
self.vec.set_len(0);
}
}
}
#[macro_export]
macro_rules! compact_vec {
() => {
$crate::common::CompactVec::new()
};
($($elem:expr),+ $(,)?) => {{
let arr = [$($elem),+];
let mut vec = $crate::common::CompactVec::with_capacity(arr.len());
for elem in arr {
vec.push(elem);
}
vec
}};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_size() {
assert_eq!(std::mem::size_of::<CompactVec<u8>>(), 16);
assert_eq!(std::mem::size_of::<CompactVec<u64>>(), 16);
assert_eq!(std::mem::size_of::<Vec<u8>>(), 24);
}
#[test]
fn test_basic_operations() {
let mut vec = CompactVec::new();
assert!(vec.is_empty());
assert_eq!(vec.len(), 0);
vec.push(1);
vec.push(2);
vec.push(3);
assert_eq!(vec.len(), 3);
assert_eq!(vec[0], 1);
assert_eq!(vec[1], 2);
assert_eq!(vec[2], 3);
assert_eq!(vec.pop(), Some(3));
assert_eq!(vec.len(), 2);
}
#[test]
fn test_with_capacity() {
let vec: CompactVec<i32> = CompactVec::with_capacity(100);
assert!(vec.is_empty());
assert!(vec.capacity() >= 100);
}
#[test]
fn test_clone() {
let mut vec = CompactVec::new();
vec.push(1);
vec.push(2);
vec.push(3);
let cloned = vec.clone();
assert_eq!(vec.as_slice(), cloned.as_slice());
}
#[test]
fn test_iteration() {
let mut vec = CompactVec::new();
vec.push(1);
vec.push(2);
vec.push(3);
let sum: i32 = vec.iter().sum();
assert_eq!(sum, 6);
let collected: Vec<i32> = vec.into_iter().collect();
assert_eq!(collected, vec![1, 2, 3]);
}
#[test]
fn test_from_iterator() {
let vec: CompactVec<i32> = (0..5).collect();
assert_eq!(vec.len(), 5);
assert_eq!(vec.as_slice(), &[0, 1, 2, 3, 4]);
}
#[test]
fn test_extend() {
let mut vec = CompactVec::new();
vec.push(1);
vec.extend(vec![2, 3, 4]);
assert_eq!(vec.as_slice(), &[1, 2, 3, 4]);
}
#[test]
fn test_truncate() {
let mut vec: CompactVec<i32> = (0..10).collect();
vec.truncate(5);
assert_eq!(vec.len(), 5);
assert_eq!(vec.as_slice(), &[0, 1, 2, 3, 4]);
}
#[test]
fn test_clear() {
let mut vec: CompactVec<i32> = (0..10).collect();
let cap = vec.capacity();
vec.clear();
assert!(vec.is_empty());
assert_eq!(vec.capacity(), cap); }
#[test]
fn test_swap_remove() {
let mut vec = CompactVec::new();
vec.push(1);
vec.push(2);
vec.push(3);
assert_eq!(vec.swap_remove(0), 1);
assert_eq!(vec.as_slice(), &[3, 2]);
}
#[test]
fn test_into_vec_and_back() {
let compact: CompactVec<i32> = (0..5).collect();
let std_vec: Vec<i32> = compact.into_vec();
assert_eq!(std_vec, vec![0, 1, 2, 3, 4]);
let compact_again = CompactVec::from_vec(std_vec);
assert_eq!(compact_again.as_slice(), &[0, 1, 2, 3, 4]);
}
#[test]
fn test_with_strings() {
let mut vec = CompactVec::new();
vec.push(String::from("hello"));
vec.push(String::from("world"));
assert_eq!(vec[0], "hello");
assert_eq!(vec[1], "world");
let cloned = vec.clone();
assert_eq!(cloned[0], "hello");
}
#[test]
fn test_clone_panic_safety() {
use std::sync::atomic::{AtomicUsize, Ordering};
static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
static CLONE_COUNT: AtomicUsize = AtomicUsize::new(0);
#[derive(Debug)]
struct PanicOnThirdClone(usize);
impl Clone for PanicOnThirdClone {
fn clone(&self) -> Self {
let count = CLONE_COUNT.fetch_add(1, Ordering::SeqCst);
if count == 2 {
panic!("Intentional panic on third clone");
}
PanicOnThirdClone(self.0)
}
}
impl Drop for PanicOnThirdClone {
fn drop(&mut self) {
DROP_COUNT.fetch_add(1, Ordering::SeqCst);
}
}
DROP_COUNT.store(0, Ordering::SeqCst);
CLONE_COUNT.store(0, Ordering::SeqCst);
let mut vec = CompactVec::new();
vec.push(PanicOnThirdClone(1));
vec.push(PanicOnThirdClone(2));
vec.push(PanicOnThirdClone(3));
vec.push(PanicOnThirdClone(4));
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let _cloned = vec.clone();
}));
assert!(result.is_err(), "Should have panicked");
let drops_from_cleanup = DROP_COUNT.load(Ordering::SeqCst);
assert_eq!(
drops_from_cleanup, 2,
"Should have dropped 2 successfully cloned elements, got {}",
drops_from_cleanup
);
drop(vec);
let total_drops = DROP_COUNT.load(Ordering::SeqCst);
assert_eq!(
total_drops, 6,
"Total drops should be 6 (2 from cleanup + 4 from original), got {}",
total_drops
);
}
#[test]
fn test_extend_clone_panic_safety() {
use std::sync::atomic::{AtomicUsize, Ordering};
static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
static CLONE_COUNT: AtomicUsize = AtomicUsize::new(0);
#[derive(Debug)]
struct PanicOnThirdClone(usize);
impl Clone for PanicOnThirdClone {
fn clone(&self) -> Self {
let count = CLONE_COUNT.fetch_add(1, Ordering::SeqCst);
if count == 2 {
panic!("Intentional panic on third clone");
}
PanicOnThirdClone(self.0)
}
}
impl Drop for PanicOnThirdClone {
fn drop(&mut self) {
DROP_COUNT.fetch_add(1, Ordering::SeqCst);
}
}
DROP_COUNT.store(0, Ordering::SeqCst);
CLONE_COUNT.store(0, Ordering::SeqCst);
let source = [
PanicOnThirdClone(1),
PanicOnThirdClone(2),
PanicOnThirdClone(3),
PanicOnThirdClone(4),
];
let mut vec: CompactVec<PanicOnThirdClone> = CompactVec::new();
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
vec.extend_clone(&source);
}));
assert!(result.is_err(), "Should have panicked");
assert_eq!(
vec.len(),
2,
"Guard should have set length to 2 (elements cloned before panic)"
);
drop(vec);
let drops_after_vec_drop = DROP_COUNT.load(Ordering::SeqCst);
assert_eq!(
drops_after_vec_drop, 2,
"Should have dropped 2 successfully cloned elements when vec dropped, got {}",
drops_after_vec_drop
);
drop(source);
let total_drops = DROP_COUNT.load(Ordering::SeqCst);
assert_eq!(
total_drops, 6,
"Total drops should be 6 (2 from vec + 4 from source), got {}",
total_drops
);
}
#[test]
fn test_from_iter_panic_safety() {
use std::sync::atomic::{AtomicUsize, Ordering};
static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
#[derive(Debug)]
#[allow(dead_code)] struct PanicOnThirdNext(usize);
impl Drop for PanicOnThirdNext {
fn drop(&mut self) {
DROP_COUNT.fetch_add(1, Ordering::SeqCst);
}
}
struct PanickingIter {
current: usize,
max: usize,
}
impl Iterator for PanickingIter {
type Item = PanicOnThirdNext;
fn next(&mut self) -> Option<Self::Item> {
if self.current >= self.max {
return None;
}
self.current += 1;
if self.current == 3 {
panic!("Intentional panic on third next()");
}
Some(PanicOnThirdNext(self.current))
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.max - self.current;
(remaining, Some(remaining))
}
}
impl ExactSizeIterator for PanickingIter {}
DROP_COUNT.store(0, Ordering::SeqCst);
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let _vec: CompactVec<PanicOnThirdNext> = PanickingIter { current: 0, max: 5 }.collect();
}));
assert!(result.is_err(), "Should have panicked");
let drops_after_panic = DROP_COUNT.load(Ordering::SeqCst);
assert_eq!(
drops_after_panic, 2,
"Should have dropped 2 successfully written elements, got {}",
drops_after_panic
);
}
}