#![allow(clippy::into_iter_on_ref)]
use crate::memcx::MemCx;
use crate::pg_sys;
use crate::seal::Sealed;
use core::marker::PhantomData;
use core::mem;
use core::ptr::{self, NonNull};
mod flat_list;
#[cfg(feature = "cshim")]
pub mod old_list;
#[cfg(feature = "cshim")]
pub use old_list::*;
#[derive(Debug)]
pub enum List<'cx, T> {
Nil,
Cons(ListHead<'cx, T>),
}
#[derive(Debug)]
pub struct ListHead<'cx, T> {
list: NonNull<pg_sys::List>,
_type: PhantomData<&'cx [T]>,
}
#[repr(transparent)]
pub struct ListCell<T> {
cell: pg_sys::ListCell,
_type: PhantomData<T>,
}
const _: () = {
assert!(mem::size_of::<ListCell<u128>>() == mem::size_of::<pg_sys::ListCell>());
};
pub unsafe trait Enlist: Sealed + Sized {
const LIST_TAG: pg_sys::NodeTag;
#[doc(hidden)]
unsafe fn apoptosis(cell: *mut pg_sys::ListCell) -> *mut Self;
#[doc(hidden)]
fn endocytosis(cell: &mut pg_sys::ListCell, value: Self);
}
impl<'cx, T> Default for List<'cx, T> {
fn default() -> List<'cx, T> {
List::Nil
}
}
impl<T: Enlist> List<'_, T> {
pub unsafe fn downcast_ptr_in_memcx<'cx>(
ptr: *mut pg_sys::List,
memcx: &'cx MemCx<'_>,
) -> Option<List<'cx, T>> {
match NonNull::new(ptr) {
None => Some(List::Nil),
Some(list) => ListHead::downcast_ptr_in_memcx(list, memcx).map(|head| List::Cons(head)),
}
}
}
impl<'cx, T> List<'cx, T> {
#[inline]
pub fn len(&self) -> usize {
match self {
List::Nil => 0,
List::Cons(head) => head.len(),
}
}
#[inline]
pub fn is_empty(&self) -> bool {
matches!(self, List::Nil)
}
#[inline]
pub fn capacity(&self) -> usize {
match self {
List::Nil => 0,
List::Cons(head) => head.capacity(),
}
}
pub fn into_ptr(mut self) -> *mut pg_sys::List {
self.as_mut_ptr()
}
pub fn as_ptr(&self) -> *const pg_sys::List {
match self {
List::Nil => ptr::null_mut(),
List::Cons(head) => head.list.as_ptr(),
}
}
pub fn as_mut_ptr(&mut self) -> *mut pg_sys::List {
match self {
List::Nil => ptr::null_mut(),
List::Cons(head) => head.list.as_ptr(),
}
}
}
impl<'cx, T: Enlist> List<'cx, T> {
pub fn try_push(&mut self, value: T) -> Result<&mut ListHead<'cx, T>, &mut Self> {
match self {
List::Nil => Err(self),
list if list.capacity() - list.len() == 0 => Err(list),
List::Cons(head) => Ok(head.push(value)),
}
}
pub fn try_reserve(&mut self, items: usize) -> Result<&mut ListHead<'cx, T>, &mut Self> {
match self {
List::Nil => Err(self),
List::Cons(head) => Ok(head.reserve(items)),
}
}
}
impl<T: Enlist> ListHead<'_, T> {
pub unsafe fn downcast_ptr_in_memcx<'cx>(
list: NonNull<pg_sys::List>,
_memcx: &'cx MemCx<'_>,
) -> Option<ListHead<'cx, T>> {
(T::LIST_TAG == (*list.as_ptr()).type_).then_some(ListHead { list, _type: PhantomData })
}
}
impl<T> ListHead<'_, T> {
#[inline]
pub fn len(&self) -> usize {
unsafe { (*self.list.as_ptr()).length as usize }
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn list_nil_is_default() {
let list: List<'_, *mut core::ffi::c_void> = List::default();
assert!(matches!(list, List::Nil));
}
#[test]
fn list_nil_is_empty() {
let list: List<'_, *mut core::ffi::c_void> = List::Nil;
assert!(list.is_empty());
assert_eq!(list.len(), 0);
assert_eq!(list.capacity(), 0);
}
#[test]
fn list_nil_as_ptr_is_null() {
let list: List<'_, *mut core::ffi::c_void> = List::Nil;
assert!(list.as_ptr().is_null());
}
#[test]
fn list_nil_into_ptr_is_null() {
let mut list: List<'_, *mut core::ffi::c_void> = List::Nil;
assert!(list.as_mut_ptr().is_null());
assert!(list.into_ptr().is_null());
}
}