use core::{fmt, iter::FusedIterator, marker::PhantomData};
use crate::{Flat, Patch, emitter::Pos};
#[repr(C)]
pub struct NearList<T> {
head: i32,
len: u32,
_type: PhantomData<T>,
}
unsafe impl<T: Flat> Flat for NearList<T> {
unsafe fn deep_copy(&self, p: &mut impl Patch, at: Pos) {
let len = self.len;
if len == 0 {
unsafe { p.patch_list_header::<T>(at, Pos::ZERO, 0) };
return;
}
let seg_pos = p.alloc_segment::<T>(len);
let values_off = size_of::<Segment<T>>();
for (i, elem) in self.iter().enumerate() {
unsafe { elem.deep_copy(p, seg_pos.offset(values_off + i * size_of::<T>())) };
}
unsafe { p.patch_list_header::<T>(at, seg_pos, len) };
}
fn validate(addr: usize, buf: &[u8]) -> Result<(), crate::ValidateError> {
crate::validate::validate_list_impl::<T>(addr, buf)
}
fn validate_option(addr: usize, buf: &[u8]) -> Result<(), crate::ValidateError> {
crate::ValidateError::check::<Option<Self>>(addr, buf)?;
let disc = buf[addr];
match disc {
0 => Ok(()), 1 => {
let inner = addr + core::mem::offset_of!(Option<Self>, Some.0);
crate::validate::validate_list_impl::<T>(inner, buf)
}
_ => Err(crate::ValidateError::InvalidDiscriminant { addr, value: disc, max: 1 }),
}
}
}
#[repr(C)]
pub struct Segment<T> {
pub(crate) next: i32,
pub(crate) len: u32,
pub(crate) _values: [T; 0],
}
impl<T: Flat> NearList<T> {
#[must_use]
pub const fn len(&self) -> usize {
self.len as usize
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
pub(crate) const fn head_offset(&self) -> i32 {
self.head
}
#[must_use]
pub fn segment_count(&self) -> usize {
if self.len == 0 {
return 0;
}
let mut count = 1usize;
let mut seg_addr =
core::ptr::from_ref(&self.head).cast::<u8>().addr().wrapping_add_signed(self.head as isize);
loop {
let seg = unsafe { &*core::ptr::with_exposed_provenance::<Segment<T>>(seg_addr) };
if seg.next == 0 {
break;
}
seg_addr =
core::ptr::from_ref(&seg.next).cast::<u8>().addr().wrapping_add_signed(seg.next as isize);
count += 1;
}
count
}
#[must_use]
pub fn last(&self) -> Option<&T> {
if self.len == 0 {
return None;
}
let mut seg_addr =
core::ptr::from_ref(&self.head).cast::<u8>().addr().wrapping_add_signed(self.head as isize);
loop {
let seg = unsafe { &*core::ptr::with_exposed_provenance::<Segment<T>>(seg_addr) };
if seg.next == 0 {
let last_idx = seg.len as usize - 1;
let val_addr =
seg_addr.wrapping_add(size_of::<Segment<T>>()).wrapping_add(last_idx * size_of::<T>());
return Some(unsafe { &*core::ptr::with_exposed_provenance::<T>(val_addr) });
}
seg_addr =
core::ptr::from_ref(&seg.next).cast::<u8>().addr().wrapping_add_signed(seg.next as isize);
}
}
#[must_use]
pub fn contains(&self, item: &T) -> bool
where
T: PartialEq,
{
self.iter().any(|x| x == item)
}
#[must_use]
pub fn first(&self) -> Option<&T> {
if self.len == 0 {
return None;
}
unsafe {
let addr =
core::ptr::from_ref(&self.head).cast::<u8>().addr().wrapping_add_signed(self.head as isize);
let val_ptr =
core::ptr::with_exposed_provenance::<T>(addr.wrapping_add(size_of::<Segment<T>>()));
Some(&*val_ptr)
}
}
#[must_use]
pub fn iter(&self) -> NearListIter<'_, T> {
if self.len == 0 {
return NearListIter {
current_value: core::ptr::null(),
remaining_in_seg: 0,
current_seg: core::ptr::null(),
remaining_total: 0,
_type: PhantomData,
};
}
let seg_addr =
core::ptr::from_ref(&self.head).cast::<u8>().addr().wrapping_add_signed(self.head as isize);
let seg_ptr = core::ptr::with_exposed_provenance::<Segment<T>>(seg_addr);
let seg = unsafe { &*seg_ptr };
let val_ptr =
core::ptr::with_exposed_provenance::<T>(seg_addr.wrapping_add(size_of::<Segment<T>>()));
NearListIter {
current_value: val_ptr,
remaining_in_seg: seg.len,
current_seg: seg_ptr,
remaining_total: self.len,
_type: PhantomData,
}
}
}
pub struct NearListIter<'a, T: Flat> {
current_value: *const T,
remaining_in_seg: u32,
current_seg: *const Segment<T>,
remaining_total: u32,
_type: PhantomData<&'a T>,
}
impl<'a, T: Flat> Iterator for NearListIter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<&'a T> {
if self.remaining_total == 0 {
return None;
}
if self.remaining_in_seg == 0 {
unsafe {
let seg = &*self.current_seg;
let next_addr =
core::ptr::from_ref(&seg.next).cast::<u8>().addr().wrapping_add_signed(seg.next as isize);
self.current_seg = core::ptr::with_exposed_provenance::<Segment<T>>(next_addr);
let new_seg = &*self.current_seg;
self.remaining_in_seg = new_seg.len;
self.current_value =
core::ptr::with_exposed_provenance::<T>(next_addr.wrapping_add(size_of::<Segment<T>>()));
}
}
unsafe {
let val = &*self.current_value;
self.remaining_total -= 1;
self.remaining_in_seg -= 1;
self.current_value = self.current_value.add(1);
Some(val)
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let r = self.remaining_total as usize;
(r, Some(r))
}
}
impl<T: Flat> ExactSizeIterator for NearListIter<'_, T> {}
impl<T: Flat> FusedIterator for NearListIter<'_, T> {}
impl<'a, T: Flat> IntoIterator for &'a NearList<T> {
type Item = &'a T;
type IntoIter = NearListIter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<T: Flat + PartialEq> PartialEq for NearList<T> {
fn eq(&self, other: &Self) -> bool {
self.len() == other.len() && self.iter().zip(other.iter()).all(|(a, b)| a == b)
}
}
impl<T: Flat + Eq> Eq for NearList<T> {}
impl<T: Flat + fmt::Debug> fmt::Debug for NearList<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}
impl<T: Flat + fmt::Display> fmt::Display for NearList<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("[")?;
for (i, item) in self.iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
}
fmt::Display::fmt(item, f)?;
}
f.write_str("]")
}
}
impl<T: Flat> NearList<T> {
#[must_use]
pub fn get(&self, index: usize) -> Option<&T> {
if index >= self.len as usize {
return None;
}
unsafe {
let seg_addr =
core::ptr::from_ref(&self.head).cast::<u8>().addr().wrapping_add_signed(self.head as isize);
let seg = &*core::ptr::with_exposed_provenance::<Segment<T>>(seg_addr);
if (seg.len as usize) > index {
let val_addr =
seg_addr.wrapping_add(size_of::<Segment<T>>()).wrapping_add(index * size_of::<T>());
return Some(&*core::ptr::with_exposed_provenance::<T>(val_addr));
}
}
self.iter().nth(index)
}
}
impl<T: Flat> core::ops::Index<usize> for NearList<T> {
type Output = T;
fn index(&self, index: usize) -> &T {
self.get(index).expect("NearList index out of bounds")
}
}