use crate::collection::{LinearStorageLen, TryExtend};
use core::{mem, ptr, slice};
#[inline]
pub const fn backward_deque_idx(elem_idx: usize, elem_idx_last: usize) -> usize {
elem_idx_last.wrapping_sub(elem_idx) % (usize::MAX >> 1)
}
pub(crate) fn is_char_boundary(idx: usize, slice: &[u8]) -> bool {
if idx == 0 {
return true;
}
if idx >= slice.len() {
idx == slice.len()
} else {
let byte = slice.get(idx).copied().unwrap_or_default();
!(128..192).contains(&byte)
}
}
pub(crate) unsafe fn drop_elements<B, L, T>(
buffer: &mut B,
len: L,
offset: L,
ptr: *mut T,
) -> crate::Result<()>
where
B: TryExtend<[T; 1]>,
L: LinearStorageLen,
{
let data = unsafe { ptr.add(offset.usize()) };
if B::IS_UNIT {
let elements = unsafe { slice::from_raw_parts_mut(data, len.usize()) };
unsafe {
ptr::drop_in_place(elements);
}
} else {
let mut guard = SliceDropGuard { data, begin: 0, len: len.usize() };
while guard.begin < guard.len {
let src = unsafe { guard.data.add(guard.begin) };
guard.begin = guard.begin.wrapping_add(1);
let value = unsafe { ptr::read(src) };
buffer.try_extend([value])?;
}
#[expect(
clippy::mem_forget,
reason = "there is nothing else to drop but it is possible to avoid one arithmetic operation"
)]
mem::forget(guard);
}
Ok(())
}
struct SliceDropGuard<T> {
data: *mut T,
begin: usize,
len: usize,
}
impl<T> Drop for SliceDropGuard<T> {
fn drop(&mut self) {
let remaining_len = self.len.wrapping_sub(self.begin);
if remaining_len > 0 {
let data = unsafe { self.data.add(self.begin) };
let slice_to_drop = unsafe { slice::from_raw_parts_mut(data, remaining_len) };
unsafe {
ptr::drop_in_place(slice_to_drop);
}
}
}
}
#[cfg(test)]
mod tests {
use crate::{
collection::{ArrayVectorU8, Vector, misc::drop_elements},
sync::{Arc, AtomicUsize},
};
use core::sync::atomic::Ordering;
#[derive(Debug)]
struct DropSpyManager {
counter: Arc<AtomicUsize>,
}
impl DropSpyManager {
fn new() -> Self {
Self { counter: Arc::new(AtomicUsize::new(0)) }
}
fn counter(&self) -> usize {
self.counter.load(Ordering::SeqCst)
}
fn spawn(&self) -> DropSpySpawn {
DropSpySpawn { counter: self.counter.clone() }
}
}
#[derive(Debug)]
struct DropSpySpawn {
counter: Arc<AtomicUsize>,
}
impl Drop for DropSpySpawn {
fn drop(&mut self) {
let _ = self.counter.fetch_add(1, Ordering::SeqCst);
}
}
#[test]
fn drops_all_elements_with_buffer() {
let dpm = DropSpyManager::new();
let mut buffer = ArrayVectorU8::<_, 5>::new();
let mut vec = Vector::from_iterator((0..5).map(|_| dpm.spawn())).unwrap();
unsafe {
drop_elements(&mut buffer, 5u32, 0, vec.as_mut_ptr()).unwrap();
vec.set_len(0);
}
assert_eq!(buffer.len(), 5);
assert_eq!(dpm.counter(), 0);
}
#[test]
fn drops_all_elements_without_buffer() {
let dpm = DropSpyManager::new();
let mut vec = Vector::from_iterator((0..5).map(|_| dpm.spawn())).unwrap();
unsafe {
drop_elements(&mut (), 5u32, 0, vec.as_mut_ptr()).unwrap();
vec.set_len(0);
}
assert_eq!(dpm.counter(), 5);
}
#[test]
fn drops_some_elements() {
let dpm = DropSpyManager::new();
let mut buffer = ArrayVectorU8::<_, 2>::new();
let mut vec = Vector::from_iterator((0..5).map(|_| dpm.spawn())).unwrap();
unsafe {
let _rslt = drop_elements(&mut buffer, 5u32, 0, vec.as_mut_ptr());
vec.set_len(0);
}
assert_eq!(buffer.len(), 2);
assert_eq!(dpm.counter(), 3);
}
}