use crate::sys;
use core::{
marker::PhantomData,
mem,
ptr::{self, NonNull},
slice,
};
pub struct SliceIter<T> {
pub(super) buf: NonNull<T>,
pub(super) marker: PhantomData<T>,
pub(super) ptr: *mut T,
pub(super) end: *mut T,
}
impl<T> SliceIter<T> {
#[inline]
fn as_raw_mut_slice(&mut self) -> &mut [T] {
unsafe { slice::from_raw_parts_mut(self.ptr, self.len()) }
}
}
impl<T> Drop for SliceIter<T> {
#[inline]
fn drop(&mut self) {
struct DeallocGuard<'a, T: 'a>(&'a mut SliceIter<T>);
impl<'a, T> Drop for DeallocGuard<'a, T> {
#[inline]
fn drop(&mut self) {
unsafe {
sys::free(self.0.buf.as_ptr() as _);
}
}
}
let guard = DeallocGuard(self);
unsafe {
ptr::drop_in_place(guard.0.as_raw_mut_slice());
}
}
}
impl<T> Iterator for SliceIter<T> {
type Item = T;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.ptr == self.end {
None
} else if mem::size_of::<T>() == 0 {
self.ptr = (self.ptr as *mut i8).wrapping_add(1) as *mut T;
Some(unsafe { mem::zeroed() })
} else {
let old = self.ptr;
self.ptr = unsafe { self.ptr.offset(1) };
Some(unsafe { old.read() })
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
(len, Some(len))
}
#[inline]
fn count(self) -> usize {
self.len()
}
#[inline]
fn last(mut self) -> Option<Self::Item> {
self.next_back()
}
}
impl<T> DoubleEndedIterator for SliceIter<T> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
if self.end == self.ptr {
None
} else if mem::size_of::<T>() == 0 {
self.ptr = (self.ptr as *mut i8).wrapping_sub(1) as *mut T;
Some(unsafe { mem::zeroed() })
} else {
self.end = unsafe { self.end.offset(-1) };
Some(unsafe { self.end.read() })
}
}
}
impl<T> ExactSizeIterator for SliceIter<T> {
#[inline]
fn len(&self) -> usize {
let diff = (self.end as usize).wrapping_sub(self.ptr as usize);
match diff.checked_div(mem::size_of::<T>()) {
Some(len) => len,
None => diff,
}
}
}
impl<T> core::iter::FusedIterator for SliceIter<T> {}
#[cfg(test)]
mod tests {
use crate::Malloced;
use alloc::vec::Vec;
use core::fmt::Debug;
mod collect {
use super::*;
#[track_caller]
fn test<T: Copy + Debug + PartialEq>(slice: &[T]) {
let result: Vec<T> = Malloced::alloc(slice).unwrap().into_iter().collect();
assert_eq!(result, slice);
}
#[test]
fn zst() {
test(&[()]);
test(&[(), ()]);
}
#[test]
fn u8() {
test(&[1u8]);
test(&[1u8, 2u8]);
}
#[test]
fn u16() {
test(&[1u16]);
test(&[1u16, 2u16]);
}
#[test]
fn usize() {
test(&[1usize]);
test(&[1usize, 2usize]);
}
}
mod len {
use super::*;
#[track_caller]
fn test(slice: &[impl Copy]) {
let iter = Malloced::alloc(slice).unwrap().into_iter();
assert_eq!(iter.len(), slice.len());
}
#[test]
fn zst() {
test(&[()]);
test(&[(), ()]);
}
#[test]
fn u8() {
test(&[1u8]);
test(&[1u8, 2u8]);
}
#[test]
fn u16() {
test(&[1u16]);
test(&[1u16, 2u16]);
}
#[test]
fn usize() {
test(&[1usize]);
test(&[1usize, 2usize]);
}
}
}