use facet_core::{IterVTable, PtrConst, PtrMut, Shape, ShapeLayout};
use super::Peek;
use core::{fmt::Debug, marker::PhantomData, ptr::NonNull};
#[derive(Clone, Copy)]
pub enum ListLikeDef {
List(facet_core::ListDef),
Array(facet_core::ArrayDef),
Slice(facet_core::SliceDef),
}
impl ListLikeDef {
#[inline]
pub const fn t(&self) -> &'static Shape {
match self {
ListLikeDef::List(v) => v.t(),
ListLikeDef::Array(v) => v.t(),
ListLikeDef::Slice(v) => v.t(),
}
}
}
pub struct PeekListLikeIter<'mem, 'facet> {
state: PeekListLikeIterState<'mem>,
index: usize,
len: usize,
def: ListLikeDef,
_list: PhantomData<Peek<'mem, 'facet>>,
}
impl<'mem, 'facet> Iterator for PeekListLikeIter<'mem, 'facet> {
type Item = Peek<'mem, 'facet>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let item_ptr = match &self.state.kind {
PeekListLikeIterStateKind::Ptr { data, stride } => {
if self.index >= self.len {
return None;
}
unsafe { data.field(stride * self.index) }
}
PeekListLikeIterStateKind::Iter { iter, vtable } => unsafe { (vtable.next)(*iter)? },
};
self.index += 1;
Some(unsafe { Peek::unchecked_new(item_ptr, self.def.t()) })
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.len.saturating_sub(self.index);
(remaining, Some(remaining))
}
}
impl<'mem, 'facet> ExactSizeIterator for PeekListLikeIter<'mem, 'facet> {}
impl<'mem, 'facet> IntoIterator for &'mem PeekListLike<'mem, 'facet> {
type Item = Peek<'mem, 'facet>;
type IntoIter = PeekListLikeIter<'mem, 'facet>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
struct PeekListLikeIterState<'mem> {
kind: PeekListLikeIterStateKind,
_phantom: PhantomData<&'mem ()>,
}
enum PeekListLikeIterStateKind {
Ptr {
data: PtrConst,
stride: usize,
},
Iter {
iter: PtrMut,
vtable: &'static IterVTable<PtrConst>,
},
}
impl Drop for PeekListLikeIterState<'_> {
#[inline]
fn drop(&mut self) {
match &self.kind {
PeekListLikeIterStateKind::Iter { iter, vtable } => unsafe { (vtable.dealloc)(*iter) },
PeekListLikeIterStateKind::Ptr { .. } => {
}
}
}
}
#[derive(Clone, Copy)]
pub struct PeekListLike<'mem, 'facet> {
pub(crate) value: Peek<'mem, 'facet>,
pub(crate) def: ListLikeDef,
len: usize,
}
impl<'mem, 'facet> Debug for PeekListLike<'mem, 'facet> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PeekListLike").finish_non_exhaustive()
}
}
impl<'mem, 'facet> PeekListLike<'mem, 'facet> {
#[inline]
pub unsafe fn new(value: Peek<'mem, 'facet>, def: ListLikeDef) -> Self {
let len = match def {
ListLikeDef::List(v) => unsafe { (v.vtable.len)(value.data()) },
ListLikeDef::Slice(_) => {
let slice_as_units = unsafe { value.data.get::<[()]>() };
slice_as_units.len()
}
ListLikeDef::Array(v) => v.n,
};
Self { value, def, len }
}
#[inline]
pub const fn len(&self) -> usize {
self.len
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn get(&self, index: usize) -> Option<Peek<'mem, 'facet>> {
if let ListLikeDef::Slice(_) = &self.def {
if index >= self.len() {
return None;
}
let elem_layout = match self.def.t().layout {
ShapeLayout::Sized(layout) => layout,
ShapeLayout::Unsized => return None,
};
let data_ptr = self.value.data().raw_ptr();
let elem_ptr = unsafe { data_ptr.add(index * elem_layout.size()) };
return Some(unsafe {
Peek::unchecked_new(
PtrConst::new(NonNull::new_unchecked(elem_ptr as *mut u8).as_ptr()),
self.def.t(),
)
});
}
let as_ptr = match self.def {
ListLikeDef::List(def) => {
let item =
unsafe { (def.vtable.get)(self.value.data(), index, self.value.shape())? };
return Some(unsafe { Peek::unchecked_new(item, self.def.t()) });
}
ListLikeDef::Array(def) => def.vtable.as_ptr,
ListLikeDef::Slice(def) => def.vtable.as_ptr,
};
if index >= self.len() {
return None;
}
let base_ptr = unsafe { as_ptr(self.value.data()) };
let elem_layout = match self.def.t().layout {
ShapeLayout::Sized(layout) => layout,
ShapeLayout::Unsized => return None, };
let offset = index * elem_layout.size();
let item_ptr = unsafe { base_ptr.field(offset) };
Some(unsafe { Peek::unchecked_new(item_ptr, self.def.t()) })
}
pub fn iter(self) -> PeekListLikeIter<'mem, 'facet> {
let (as_ptr_fn, iter_vtable) = match self.def {
ListLikeDef::List(def) => (def.vtable.as_ptr, def.iter_vtable()),
ListLikeDef::Array(def) => (Some(def.vtable.as_ptr), None),
ListLikeDef::Slice(def) => (Some(def.vtable.as_ptr), None),
};
let state = match (as_ptr_fn, iter_vtable) {
(Some(as_ptr_fn), _) => {
let data = if let ListLikeDef::Slice(_) = &self.def {
PtrConst::new(unsafe {
NonNull::new_unchecked(self.value.data().raw_ptr() as *mut u8).as_ptr()
})
} else {
unsafe { as_ptr_fn(self.value.data()) }
};
let layout = self
.def
.t()
.layout
.sized_layout()
.expect("can only iterate over sized list elements");
let stride = layout.size();
PeekListLikeIterState {
kind: PeekListLikeIterStateKind::Ptr { data, stride },
_phantom: PhantomData,
}
}
(None, Some(vtable)) => {
let iter = unsafe { (vtable.init_with_value.unwrap())(self.value.data()) };
PeekListLikeIterState {
kind: PeekListLikeIterStateKind::Iter { iter, vtable },
_phantom: PhantomData,
}
}
(None, None) => unreachable!(),
};
PeekListLikeIter {
state,
index: 0,
len: self.len(),
def: self.def(),
_list: PhantomData,
}
}
#[inline]
pub const fn def(&self) -> ListLikeDef {
self.def
}
pub fn as_bytes(&self) -> Option<&'mem [u8]> {
if !self.def.t().is_type::<u8>() {
return None;
}
if self.len == 0 {
return Some(&[]);
}
let base_ptr = match self.def {
ListLikeDef::List(def) => {
if let Some(as_ptr) = def.vtable.as_ptr {
unsafe { as_ptr(self.value.data()) }
} else {
return None;
}
}
ListLikeDef::Array(def) => unsafe { (def.vtable.as_ptr)(self.value.data()) },
ListLikeDef::Slice(_) => {
PtrConst::new(unsafe {
NonNull::new_unchecked(self.value.data().raw_ptr() as *mut u8).as_ptr()
})
}
};
Some(unsafe { core::slice::from_raw_parts(base_ptr.raw_ptr(), self.len) })
}
}