use core::{fmt::Debug, marker::PhantomData, mem::ManuallyDrop, ptr::NonNull};
use facet_core::{FieldError, PtrMut, PtrUninit, Shape, ShapeLayout};
use crate::{Guard, HeapValue, ReflectError, ReflectErrorKind, peek::ListLikeDef};
use super::Poke;
pub struct PokeListLike<'mem, 'facet> {
value: Poke<'mem, 'facet>,
def: ListLikeDef,
len: usize,
}
impl<'mem, 'facet> Debug for PokeListLike<'mem, 'facet> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PokeListLike").finish_non_exhaustive()
}
}
pub struct PokeListLikeIter<'mem, 'facet> {
data: PtrMut,
stride: usize,
index: usize,
len: usize,
elem_shape: &'static Shape,
_list: PhantomData<Poke<'mem, 'facet>>,
}
impl<'mem, 'facet> Iterator for PokeListLikeIter<'mem, 'facet> {
type Item = Poke<'mem, 'facet>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.len {
return None;
}
let item_ptr = unsafe { self.data.field(self.stride * self.index) };
self.index += 1;
Some(unsafe { Poke::from_raw_parts(item_ptr, self.elem_shape) })
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.len.saturating_sub(self.index);
(remaining, Some(remaining))
}
}
impl<'mem, 'facet> ExactSizeIterator for PokeListLikeIter<'mem, 'facet> {}
impl<'mem, 'facet> PokeListLike<'mem, 'facet> {
#[inline]
pub unsafe fn new(value: Poke<'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 }
}
fn err(&self, kind: ReflectErrorKind) -> ReflectError {
self.value.err(kind)
}
#[inline]
pub const fn len(&self) -> usize {
self.len
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub const fn def(&self) -> ListLikeDef {
self.def
}
#[inline]
pub fn get(&self, index: usize) -> Option<crate::Peek<'_, 'facet>> {
self.as_peek_list_like().get(index)
}
pub fn get_mut(&mut self, index: usize) -> Option<Poke<'_, 'facet>> {
if index >= self.len {
return None;
}
let item_ptr = match self.def {
ListLikeDef::List(def) => {
let get_mut_fn = def.vtable.get_mut?;
unsafe { get_mut_fn(self.value.data_mut(), index, self.value.shape())? }
}
ListLikeDef::Array(def) => {
let elem_layout = match self.def.t().layout {
ShapeLayout::Sized(layout) => layout,
ShapeLayout::Unsized => return None,
};
let base = unsafe { (def.vtable.as_mut_ptr)(self.value.data_mut()) };
unsafe { base.field(index * elem_layout.size()) }
}
ListLikeDef::Slice(def) => {
let elem_layout = match self.def.t().layout {
ShapeLayout::Sized(layout) => layout,
ShapeLayout::Unsized => return None,
};
let base = unsafe { (def.vtable.as_mut_ptr)(self.value.data_mut()) };
unsafe { base.field(index * elem_layout.size()) }
}
};
Some(unsafe { Poke::from_raw_parts(item_ptr, self.def.t()) })
}
pub fn iter_mut(self) -> Result<PokeListLikeIter<'mem, 'facet>, ReflectError> {
let elem_shape = self.def.t();
let stride = match elem_shape.layout {
ShapeLayout::Sized(layout) => layout.size(),
ShapeLayout::Unsized => {
return Err(self.err(ReflectErrorKind::OperationFailed {
shape: self.value.shape,
operation: "iter_mut requires sized element type",
}));
}
};
let data = match self.def {
ListLikeDef::List(def) => match def.vtable.as_mut_ptr {
Some(as_mut_ptr_fn) => unsafe { as_mut_ptr_fn(self.value.data) },
None => {
return Err(self.err(ReflectErrorKind::OperationFailed {
shape: self.value.shape,
operation:
"iter_mut requires a contiguous `as_mut_ptr` vtable entry; use `get_mut` per index",
}));
}
},
ListLikeDef::Array(def) => unsafe { (def.vtable.as_mut_ptr)(self.value.data) },
ListLikeDef::Slice(def) => unsafe { (def.vtable.as_mut_ptr)(self.value.data) },
};
Ok(PokeListLikeIter {
data,
stride,
index: 0,
len: self.len,
elem_shape,
_list: PhantomData,
})
}
pub fn push<T: facet_core::Facet<'facet>>(&mut self, value: T) -> Result<(), ReflectError> {
if self.def.t() != T::SHAPE {
return Err(self.err(ReflectErrorKind::WrongShape {
expected: self.def.t(),
actual: T::SHAPE,
}));
}
let push_fn = self.push_fn()?;
let mut value = ManuallyDrop::new(value);
unsafe {
let item_ptr = PtrMut::new(&mut value as *mut ManuallyDrop<T> as *mut u8);
push_fn(self.value.data_mut(), item_ptr);
}
self.len += 1;
Ok(())
}
pub fn push_from_heap<const BORROW: bool>(
&mut self,
value: HeapValue<'facet, BORROW>,
) -> Result<(), ReflectError> {
if self.def.t() != value.shape() {
return Err(self.err(ReflectErrorKind::WrongShape {
expected: self.def.t(),
actual: value.shape(),
}));
}
let push_fn = self.push_fn()?;
let mut value = value;
let guard = value
.guard
.take()
.expect("HeapValue guard was already taken");
unsafe {
let item_ptr = PtrMut::new(guard.ptr.as_ptr());
push_fn(self.value.data_mut(), item_ptr);
}
drop(guard);
self.len += 1;
Ok(())
}
pub fn pop(&mut self) -> Result<Option<HeapValue<'facet, true>>, ReflectError> {
let list_def = match self.def {
ListLikeDef::List(def) => def,
_ => {
return Err(self.err(ReflectErrorKind::OperationFailed {
shape: self.value.shape(),
operation: "pop: only list-backed list-likes support pop",
}));
}
};
let pop_fn = list_def.pop().ok_or_else(|| {
self.err(ReflectErrorKind::OperationFailed {
shape: self.value.shape(),
operation: "pop: list type does not support pop",
})
})?;
let elem_shape = self.def.t();
let layout = elem_shape.layout.sized_layout().map_err(|_| {
self.err(ReflectErrorKind::Unsized {
shape: elem_shape,
operation: "pop",
})
})?;
let ptr = if layout.size() == 0 {
NonNull::<u8>::dangling()
} else {
let raw = unsafe { alloc::alloc::alloc(layout) };
match NonNull::new(raw) {
Some(p) => p,
None => alloc::alloc::handle_alloc_error(layout),
}
};
let out = PtrUninit::new(ptr.as_ptr());
let popped = unsafe { pop_fn(self.value.data_mut(), out) };
if !popped {
if layout.size() != 0 {
unsafe { alloc::alloc::dealloc(ptr.as_ptr(), layout) };
}
return Ok(None);
}
self.len -= 1;
Ok(Some(HeapValue {
guard: Some(Guard {
ptr,
layout,
should_dealloc: layout.size() != 0,
}),
shape: elem_shape,
phantom: PhantomData,
}))
}
pub fn swap(&mut self, a: usize, b: usize) -> Result<(), ReflectError> {
let len = self.len;
if a >= len || b >= len {
let out_of_bounds = if a >= len { a } else { b };
return Err(self.err(ReflectErrorKind::FieldError {
shape: self.value.shape(),
field_error: FieldError::IndexOutOfBounds {
index: out_of_bounds,
bound: len,
},
}));
}
if a == b {
return Ok(());
}
match self.def {
ListLikeDef::List(def) => {
let swap_fn = def.vtable.swap.ok_or_else(|| {
self.err(ReflectErrorKind::OperationFailed {
shape: self.value.shape(),
operation: "swap: list type does not support swap",
})
})?;
let ok = unsafe { swap_fn(self.value.data_mut(), a, b, self.value.shape()) };
if !ok {
return Err(self.err(ReflectErrorKind::OperationFailed {
shape: self.value.shape(),
operation: "swap: vtable refused the operation",
}));
}
Ok(())
}
ListLikeDef::Array(def) => {
let elem_size = match self.def.t().layout {
ShapeLayout::Sized(l) => l.size(),
ShapeLayout::Unsized => {
return Err(self.err(ReflectErrorKind::Unsized {
shape: self.def.t(),
operation: "swap",
}));
}
};
unsafe {
let base = (def.vtable.as_mut_ptr)(self.value.data_mut());
let pa = base.field(a * elem_size);
let pb = base.field(b * elem_size);
core::ptr::swap_nonoverlapping(
pa.as_mut_byte_ptr(),
pb.as_mut_byte_ptr(),
elem_size,
);
}
Ok(())
}
ListLikeDef::Slice(def) => {
let elem_size = match self.def.t().layout {
ShapeLayout::Sized(l) => l.size(),
ShapeLayout::Unsized => {
return Err(self.err(ReflectErrorKind::Unsized {
shape: self.def.t(),
operation: "swap",
}));
}
};
unsafe {
let base = (def.vtable.as_mut_ptr)(self.value.data_mut());
let pa = base.field(a * elem_size);
let pb = base.field(b * elem_size);
core::ptr::swap_nonoverlapping(
pa.as_mut_byte_ptr(),
pb.as_mut_byte_ptr(),
elem_size,
);
}
Ok(())
}
}
}
#[inline]
fn push_fn(&self) -> Result<facet_core::ListPushFn, ReflectError> {
match self.def {
ListLikeDef::List(def) => def.push().ok_or_else(|| {
self.err(ReflectErrorKind::OperationFailed {
shape: self.value.shape(),
operation: "push: list type does not support push",
})
}),
_ => Err(self.err(ReflectErrorKind::OperationFailed {
shape: self.value.shape(),
operation: "push: only list-backed list-likes support push",
})),
}
}
#[inline]
pub fn into_inner(self) -> Poke<'mem, 'facet> {
self.value
}
#[inline]
pub fn as_peek_list_like(&self) -> crate::PeekListLike<'_, 'facet> {
unsafe { crate::PeekListLike::new(self.value.as_peek(), self.def) }
}
}
#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use super::*;
#[test]
fn poke_list_like_vec_len_and_get_mut() {
let mut v: Vec<i32> = alloc::vec![1, 2, 3];
let poke = Poke::new(&mut v);
let mut ll = poke.into_list_like().unwrap();
assert_eq!(ll.len(), 3);
{
let mut item = ll.get_mut(1).unwrap();
item.set(200i32).unwrap();
}
assert_eq!(v, alloc::vec![1, 200, 3]);
}
#[test]
fn poke_list_like_array_get_mut() {
let mut arr: [i32; 3] = [10, 20, 30];
let poke = Poke::new(&mut arr);
let mut ll = poke.into_list_like().unwrap();
assert_eq!(ll.len(), 3);
{
let mut item = ll.get_mut(0).unwrap();
item.set(99i32).unwrap();
}
assert_eq!(arr, [99, 20, 30]);
}
#[test]
fn poke_list_like_iter_mut() {
let mut v: Vec<i32> = alloc::vec![1, 2, 3];
let poke = Poke::new(&mut v);
let ll = poke.into_list_like().unwrap();
for mut item in ll.iter_mut().unwrap() {
let cur = *item.get::<i32>().unwrap();
item.set(cur * 10).unwrap();
}
assert_eq!(v, alloc::vec![10, 20, 30]);
}
#[test]
fn poke_list_like_vec_push_pop() {
let mut v: Vec<i32> = alloc::vec![];
{
let poke = Poke::new(&mut v);
let mut ll = poke.into_list_like().unwrap();
ll.push(10i32).unwrap();
ll.push(20i32).unwrap();
assert_eq!(ll.len(), 2);
let popped = ll.pop().unwrap().unwrap();
assert_eq!(popped.materialize::<i32>().unwrap(), 20);
assert_eq!(ll.len(), 1);
}
assert_eq!(v, alloc::vec![10]);
}
#[test]
fn poke_list_like_array_push_fails() {
let mut arr: [i32; 3] = [1, 2, 3];
let poke = Poke::new(&mut arr);
let mut ll = poke.into_list_like().unwrap();
let res = ll.push(4i32);
assert!(matches!(
res,
Err(ref err) if matches!(err.kind, ReflectErrorKind::OperationFailed { .. })
));
}
#[test]
fn poke_list_like_array_pop_fails() {
let mut arr: [i32; 3] = [1, 2, 3];
let poke = Poke::new(&mut arr);
let mut ll = poke.into_list_like().unwrap();
let res = ll.pop();
assert!(matches!(
res,
Err(ref err) if matches!(err.kind, ReflectErrorKind::OperationFailed { .. })
));
}
#[test]
fn poke_list_like_vec_swap() {
let mut v: Vec<i32> = alloc::vec![1, 2, 3];
let poke = Poke::new(&mut v);
let mut ll = poke.into_list_like().unwrap();
ll.swap(0, 2).unwrap();
assert_eq!(v, alloc::vec![3, 2, 1]);
}
#[test]
fn poke_list_like_array_swap() {
let mut arr: [i32; 3] = [10, 20, 30];
let poke = Poke::new(&mut arr);
let mut ll = poke.into_list_like().unwrap();
ll.swap(0, 2).unwrap();
assert_eq!(arr, [30, 20, 10]);
}
#[test]
fn poke_list_like_swap_out_of_bounds_fails() {
let mut v: Vec<i32> = alloc::vec![1, 2, 3];
let poke = Poke::new(&mut v);
let mut ll = poke.into_list_like().unwrap();
let res = ll.swap(5, 0);
assert!(matches!(
res,
Err(ref err) if matches!(err.kind, ReflectErrorKind::FieldError { .. })
));
}
}