use super::{Enlist, List, ListCell, ListHead};
use crate::memcx::MemCx;
use crate::pg_sys;
use crate::ptr::PointerExt;
use crate::seal::Sealed;
use core::cmp;
use core::ffi;
use core::marker::PhantomData;
use core::mem;
use core::ops::{Bound, Deref, DerefMut, RangeBounds};
use core::ptr::{self, NonNull};
use core::slice;
impl<T: Enlist> Deref for ListCell<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*T::apoptosis(&self.cell as *const _ as *mut _) }
}
}
impl<T: Enlist> DerefMut for ListCell<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *T::apoptosis(&mut self.cell) }
}
}
impl Sealed for *mut ffi::c_void {}
unsafe impl Enlist for *mut ffi::c_void {
const LIST_TAG: pg_sys::NodeTag = pg_sys::NodeTag::T_List;
unsafe fn apoptosis(cell: *mut pg_sys::ListCell) -> *mut *mut ffi::c_void {
unsafe { ptr::addr_of_mut!((*cell).ptr_value) }
}
fn endocytosis(cell: &mut pg_sys::ListCell, value: Self) {
cell.ptr_value = value;
}
}
impl Sealed for ffi::c_int {}
unsafe impl Enlist for ffi::c_int {
const LIST_TAG: pg_sys::NodeTag = pg_sys::NodeTag::T_IntList;
unsafe fn apoptosis(cell: *mut pg_sys::ListCell) -> *mut ffi::c_int {
unsafe { ptr::addr_of_mut!((*cell).int_value) }
}
fn endocytosis(cell: &mut pg_sys::ListCell, value: Self) {
cell.int_value = value;
}
}
impl Sealed for pg_sys::Oid {}
unsafe impl Enlist for pg_sys::Oid {
const LIST_TAG: pg_sys::NodeTag = pg_sys::NodeTag::T_OidList;
unsafe fn apoptosis(cell: *mut pg_sys::ListCell) -> *mut pg_sys::Oid {
unsafe { ptr::addr_of_mut!((*cell).oid_value) }
}
fn endocytosis(cell: &mut pg_sys::ListCell, value: Self) {
cell.oid_value = value;
}
}
#[cfg(any(feature = "pg16", feature = "pg17"))]
impl Sealed for pg_sys::TransactionId {}
#[cfg(any(feature = "pg16", feature = "pg17"))]
unsafe impl Enlist for pg_sys::TransactionId {
const LIST_TAG: pg_sys::NodeTag = pg_sys::NodeTag::T_XidList;
unsafe fn apoptosis(cell: *mut pg_sys::ListCell) -> *mut pg_sys::TransactionId {
unsafe { ptr::addr_of_mut!((*cell).xid_value) }
}
fn endocytosis(cell: &mut pg_sys::ListCell, value: Self) {
cell.xid_value = value;
}
}
impl<'cx, T: Enlist> List<'cx, T> {
pub fn get(&self, index: usize) -> Option<&T> {
self.as_cells().get(index).map(Deref::deref)
}
pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
self.as_cells_mut().get_mut(index).map(DerefMut::deref_mut)
}
pub fn unstable_push_in_context(
&mut self,
value: T,
mcx: &'cx MemCx<'_>,
) -> &mut ListHead<'cx, T> {
match self {
List::Nil => {
let list_size = 128;
unsafe {
let list: *mut pg_sys::List = mcx.alloc_bytes(list_size).cast();
assert!(list.is_non_null());
(*list).type_ = T::LIST_TAG;
(*list).max_length = ((list_size - mem::size_of::<pg_sys::List>())
/ mem::size_of::<pg_sys::ListCell>())
as _;
(*list).elements = ptr::addr_of_mut!((*list).initial_elements).cast();
T::endocytosis((*list).elements.as_mut().unwrap(), value);
(*list).length = 1;
*self = Self::downcast_ptr_in_memcx(list, mcx).unwrap();
assert_eq!(1, self.len());
match self {
List::Cons(head) => head,
_ => unreachable!(),
}
}
}
List::Cons(head) => head.push(value),
}
}
pub fn drain<R>(&mut self, range: R) -> Drain<'_, 'cx, T>
where
R: RangeBounds<usize>,
{
let len = self.len();
let drain_start = match range.start_bound() {
Bound::Unbounded | Bound::Included(0) => 0,
Bound::Included(first) => *first,
Bound::Excluded(point) => point + 1,
};
let tail_start = match range.end_bound() {
Bound::Unbounded => cmp::min(ffi::c_int::MAX as _, len),
Bound::Included(last) => last + 1,
Bound::Excluded(tail) => *tail,
};
let Some(tail_len) = len.checked_sub(tail_start) else {
panic!("index out of bounds of list!")
};
assert!(drain_start <= len);
assert!(tail_start <= len);
assert!(tail_start <= ffi::c_int::MAX as _);
assert!(drain_start + tail_len <= ffi::c_int::MAX as _);
let raw = if drain_start == 0 {
mem::take(self).into_ptr()
} else {
match self {
List::Nil => ptr::null_mut(),
List::Cons(head) => head.list.as_ptr().cast(),
}
};
if raw.is_non_null() {
unsafe { (*raw).length = drain_start as _ };
let cells_ptr = unsafe { (*raw).elements };
let iter = unsafe {
RawCellIter {
ptr: cells_ptr.add(drain_start).cast(),
end: cells_ptr.add(tail_start).cast(),
}
};
Drain { tail_len: tail_len as _, tail_start: tail_start as _, raw, origin: self, iter }
} else {
assert!(tail_len == 0 && tail_start == 0 && drain_start == 0);
Drain { tail_len: 0, tail_start: 0, raw, origin: self, iter: Default::default() }
}
}
pub fn iter(&self) -> impl Iterator<Item = &T> {
self.as_cells().into_iter().map(Deref::deref)
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
self.as_cells_mut().into_iter().map(DerefMut::deref_mut)
}
}
impl<T> List<'_, T> {
pub fn as_cells(&self) -> &[ListCell<T>] {
unsafe {
match self {
List::Nil => &[],
List::Cons(inner) => slice::from_raw_parts(inner.as_cells_ptr(), inner.len()),
}
}
}
pub fn as_cells_mut(&mut self) -> &mut [ListCell<T>] {
unsafe {
match self {
List::Nil => &mut [],
List::Cons(inner) => {
slice::from_raw_parts_mut(inner.as_mut_cells_ptr(), inner.len())
}
}
}
}
}
impl<T> ListHead<'_, T> {
#[inline]
pub fn capacity(&self) -> usize {
unsafe { self.list.as_ref().max_length as usize }
}
pub fn as_cells(&self) -> &[ListCell<T>] {
unsafe { slice::from_raw_parts(self.as_cells_ptr(), self.len()) }
}
pub fn as_cells_ptr(&self) -> *const ListCell<T> {
unsafe { (*self.list.as_ptr()).elements.cast() }
}
pub fn as_mut_cells_ptr(&mut self) -> *mut ListCell<T> {
unsafe { (*self.list.as_ptr()).elements.cast() }
}
}
impl<T: Enlist> ListHead<'_, T> {
pub fn push(&mut self, value: T) -> &mut Self {
let list = unsafe { self.list.as_mut() };
let pg_sys::List { length, max_length, elements, .. } = list;
assert!(*max_length > 0);
assert!(*length > 0);
assert!(*max_length >= *length);
if *max_length - *length < 1 {
self.reserve(*max_length as _);
}
let cell = unsafe { &mut *elements.add(*length as _) };
T::endocytosis(cell, value);
*length += 1;
self
}
pub fn reserve(&mut self, count: usize) -> &mut Self {
let list = unsafe { self.list.as_mut() };
assert!(list.length > 0);
assert!(list.max_length > 0);
if ((list.max_length - list.length) as usize) < count {
let size = i32::try_from(count).unwrap();
let size = list.length.checked_add(size).unwrap();
let size = usize::try_from(size).unwrap();
unsafe { grow_list(list, size) };
};
self
}
}
unsafe fn grow_list(list: &mut pg_sys::List, target: usize) {
assert!((i32::MAX as usize) >= target, "Cannot allocate more than c_int::MAX elements");
let alloc_size = target * mem::size_of::<pg_sys::ListCell>();
if list.elements == ptr::addr_of_mut!(list.initial_elements).cast() {
let context = pg_sys::GetMemoryChunkContext(list as *mut pg_sys::List as *mut _);
if context.is_null() {
panic!("Context free list?");
}
let buf = pg_sys::MemoryContextAlloc(context, alloc_size);
if buf.is_null() {
panic!("List allocation failure");
}
ptr::copy_nonoverlapping(list.elements, buf.cast(), list.length as _);
#[cfg(debug_assertions)]
ptr::write_bytes(list.elements, 0x7F, list.length as _);
list.elements = buf.cast();
} else {
list.elements = pg_sys::repalloc(list.elements.cast(), alloc_size).cast();
}
list.max_length = target as _;
}
unsafe fn destroy_list(list: *mut pg_sys::List) {
if (*list).elements != ptr::addr_of_mut!((*list).initial_elements).cast() {
pg_sys::pfree((*list).elements.cast());
}
pg_sys::pfree(list.cast());
}
#[derive(Debug)]
pub struct ListIter<'a, T> {
list: List<'a, T>,
iter: RawCellIter<T>,
}
#[derive(Debug)]
pub struct Drain<'a, 'cx, T> {
tail_start: u32,
tail_len: u32,
iter: RawCellIter<T>,
origin: &'a mut List<'cx, T>,
raw: *mut pg_sys::List,
}
impl<'a, 'cx, T> Drop for Drain<'a, 'cx, T> {
fn drop(&mut self) {
if self.raw.is_null() {
return;
}
unsafe {
let len = (*self.raw).length;
if len == 0 && self.tail_len == 0 {
destroy_list(self.raw)
} else {
let src = (*self.raw).elements.add(self.tail_start as _);
let dst = (*self.raw).elements.add(len as _);
ptr::copy(src, dst, self.tail_len as _); (*self.raw).length = len + (self.tail_len as ffi::c_int);
*self.origin = List::Cons(ListHead {
list: NonNull::new_unchecked(self.raw),
_type: PhantomData,
});
}
}
}
}
impl<T: Enlist> Iterator for Drain<'_, '_, T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
impl<T: Enlist> Iterator for ListIter<'_, T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
impl<'a, T: Enlist> IntoIterator for List<'a, T> {
type IntoIter = ListIter<'a, T>;
type Item = T;
fn into_iter(mut self) -> Self::IntoIter {
let len = self.len();
let iter = match &mut self {
List::Nil => Default::default(),
List::Cons(head) => {
let ptr = head.as_mut_cells_ptr();
let end = unsafe { ptr.add(len) };
RawCellIter { ptr, end }
}
};
ListIter { list: self, iter }
}
}
impl<'a, T> Drop for ListIter<'a, T> {
fn drop(&mut self) {
if let List::Cons(head) = &mut self.list {
unsafe { destroy_list(head.list.as_ptr()) }
}
}
}
#[derive(Debug, PartialEq)]
struct RawCellIter<T> {
ptr: *mut ListCell<T>,
end: *mut ListCell<T>,
}
impl<T> Default for RawCellIter<T> {
fn default() -> Self {
RawCellIter { ptr: ptr::null_mut(), end: ptr::null_mut() }
}
}
impl<T: Enlist> Iterator for RawCellIter<T> {
type Item = T;
#[inline]
fn next(&mut self) -> Option<T> {
if self.ptr < self.end {
let ptr = self.ptr;
unsafe {
self.ptr = ptr.add(1);
Some(T::apoptosis(ptr.cast()).read())
}
} else {
None
}
}
}