Skip to main content

pgrx/list/
flat_list.rs

1use super::{Enlist, List, ListCell, ListHead};
2use crate::memcx::MemCx;
3use crate::pg_sys;
4use crate::ptr::PointerExt;
5use crate::seal::Sealed;
6use core::cmp;
7use core::ffi;
8use core::marker::PhantomData;
9use core::mem;
10use core::ops::{Bound, Deref, DerefMut, RangeBounds};
11use core::ptr::{self, NonNull};
12use core::slice;
13
14impl<T: Enlist> Deref for ListCell<T> {
15    type Target = T;
16
17    fn deref(&self) -> &Self::Target {
18        // SAFETY: A brief upgrade of readonly &ListCell<T> to writable *mut pg_sys::ListCell
19        // may seem sus, but is fine: Enlist::apoptosis is defined as pure casting/arithmetic.
20        // So the pointer begins and ends without write permission, and
21        // we essentially just reborrow a ListCell as its inner field type
22        unsafe { &*T::apoptosis(&self.cell as *const _ as *mut _) }
23    }
24}
25
26impl<T: Enlist> DerefMut for ListCell<T> {
27    fn deref_mut(&mut self) -> &mut Self::Target {
28        // SAFETY: we essentially just reborrow a ListCell as its inner field type which
29        // only relies on pgrx::list::{Enlist, List, ListCell} maintaining safety invariants
30        unsafe { &mut *T::apoptosis(&mut self.cell) }
31    }
32}
33
34impl Sealed for *mut ffi::c_void {}
35unsafe impl Enlist for *mut ffi::c_void {
36    const LIST_TAG: pg_sys::NodeTag = pg_sys::NodeTag::T_List;
37
38    unsafe fn apoptosis(cell: *mut pg_sys::ListCell) -> *mut *mut ffi::c_void {
39        unsafe { ptr::addr_of_mut!((*cell).ptr_value) }
40    }
41
42    fn endocytosis(cell: &mut pg_sys::ListCell, value: Self) {
43        cell.ptr_value = value;
44    }
45}
46
47impl Sealed for ffi::c_int {}
48unsafe impl Enlist for ffi::c_int {
49    const LIST_TAG: pg_sys::NodeTag = pg_sys::NodeTag::T_IntList;
50
51    unsafe fn apoptosis(cell: *mut pg_sys::ListCell) -> *mut ffi::c_int {
52        unsafe { ptr::addr_of_mut!((*cell).int_value) }
53    }
54
55    fn endocytosis(cell: &mut pg_sys::ListCell, value: Self) {
56        cell.int_value = value;
57    }
58}
59
60impl Sealed for pg_sys::Oid {}
61unsafe impl Enlist for pg_sys::Oid {
62    const LIST_TAG: pg_sys::NodeTag = pg_sys::NodeTag::T_OidList;
63
64    unsafe fn apoptosis(cell: *mut pg_sys::ListCell) -> *mut pg_sys::Oid {
65        unsafe { ptr::addr_of_mut!((*cell).oid_value) }
66    }
67
68    fn endocytosis(cell: &mut pg_sys::ListCell, value: Self) {
69        cell.oid_value = value;
70    }
71}
72
73#[cfg(any(feature = "pg16", feature = "pg17", feature = "pg18", feature = "pg19"))]
74impl Sealed for pg_sys::TransactionId {}
75#[cfg(any(feature = "pg16", feature = "pg17", feature = "pg18", feature = "pg19"))]
76unsafe impl Enlist for pg_sys::TransactionId {
77    const LIST_TAG: pg_sys::NodeTag = pg_sys::NodeTag::T_XidList;
78
79    unsafe fn apoptosis(cell: *mut pg_sys::ListCell) -> *mut pg_sys::TransactionId {
80        unsafe { ptr::addr_of_mut!((*cell).xid_value) }
81    }
82
83    fn endocytosis(cell: &mut pg_sys::ListCell, value: Self) {
84        cell.xid_value = value;
85    }
86}
87
88impl<'cx, T: Enlist> List<'cx, T> {
89    /// Borrow an item from the List at the index
90    pub fn get(&self, index: usize) -> Option<&T> {
91        self.as_cells().get(index).map(Deref::deref)
92    }
93
94    /// Mutably borrow an item from the List at the index
95    pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
96        self.as_cells_mut().get_mut(index).map(DerefMut::deref_mut)
97    }
98
99    /// Pushes an item into the List
100    ///
101    /// Allocates the entire list in referenced context if it had zero elements,
102    /// otherwise uses the List's own context.
103    ///
104    /// "Unstable" because this may receive breaking changes.
105    pub fn unstable_push_in_context(
106        &mut self,
107        value: T,
108        mcx: &'cx MemCx<'_>,
109    ) -> &mut ListHead<'cx, T> {
110        match self {
111            List::Nil => {
112                // No silly reasoning, simply allocate ~2 cache lines for a list
113                let list_size = 128;
114                unsafe {
115                    let list: *mut pg_sys::List =
116                        mcx.alloc_bytes(list_size).unwrap().cast().as_ptr();
117                    assert!(list.is_non_null());
118                    (*list).type_ = T::LIST_TAG;
119                    (*list).max_length = ((list_size - mem::size_of::<pg_sys::List>())
120                        / mem::size_of::<pg_sys::ListCell>())
121                        as _;
122                    (*list).elements = ptr::addr_of_mut!((*list).initial_elements).cast();
123                    T::endocytosis((*list).elements.as_mut().unwrap(), value);
124                    (*list).length = 1;
125                    *self = Self::downcast_ptr_in_memcx(list, mcx).unwrap();
126                    assert_eq!(1, self.len());
127                    match self {
128                        List::Cons(head) => head,
129                        _ => unreachable!(),
130                    }
131                }
132            }
133            List::Cons(head) => head.push(value),
134        }
135    }
136
137    // Iterate over part of the List while removing elements from it
138    //
139    // Note that if this removes the last item, it deallocates the entire list.
140    // This is to maintain the Postgres List invariant that a 0-len list is always Nil.
141    pub fn drain<R>(&mut self, range: R) -> Drain<'_, 'cx, T>
142    where
143        R: RangeBounds<usize>,
144    {
145        // SAFETY: The Drain invariants are somewhat easier to maintain for List than Vec,
146        // however, they have the complication of the Postgres List invariants
147        let len = self.len();
148        let drain_start = match range.start_bound() {
149            Bound::Unbounded | Bound::Included(0) => 0,
150            Bound::Included(first) => *first,
151            Bound::Excluded(point) => point + 1,
152        };
153        let tail_start = match range.end_bound() {
154            Bound::Unbounded => cmp::min(ffi::c_int::MAX as _, len),
155            Bound::Included(last) => last + 1,
156            Bound::Excluded(tail) => *tail,
157        };
158        let Some(tail_len) = len.checked_sub(tail_start) else {
159            panic!("index out of bounds of list!")
160        };
161        // Let's issue our asserts before mutating state:
162        assert!(drain_start <= len);
163        assert!(tail_start <= len);
164
165        // Postgres assumes Lists fit into c_int, check before shrinking
166        assert!(tail_start <= ffi::c_int::MAX as _);
167        assert!(drain_start + tail_len <= ffi::c_int::MAX as _);
168
169        // If draining all, rip it out of place to contain broken invariants from panics
170        let raw = if drain_start == 0 {
171            mem::take(self).into_ptr()
172        } else {
173            // Leave it in place, but we need a pointer:
174            match self {
175                List::Nil => ptr::null_mut(),
176                List::Cons(head) => head.list.as_ptr().cast(),
177            }
178        };
179
180        // Remember to check that our raw ptr is non-null
181        if raw.is_non_null() {
182            // Shorten the list to prohibit interaction with List's state after drain_start.
183            // Note this breaks List repr invariants in the `drain_start == 0` case, but
184            // we only consider returning the list ptr to `&mut self` if Drop is completed
185            unsafe { (*raw).length = drain_start as _ };
186            let cells_ptr = unsafe { (*raw).elements };
187            let iter = unsafe {
188                RawCellIter {
189                    ptr: cells_ptr.add(drain_start).cast(),
190                    end: cells_ptr.add(tail_start).cast(),
191                }
192            };
193            Drain { tail_len: tail_len as _, tail_start: tail_start as _, raw, origin: self, iter }
194        } else {
195            // If it's not, produce the only valid choice: a 0-len iterator pointing to null
196            // One last doublecheck for old paranoia's sake:
197            assert!(tail_len == 0 && tail_start == 0 && drain_start == 0);
198            Drain { tail_len: 0, tail_start: 0, raw, origin: self, iter: Default::default() }
199        }
200    }
201
202    pub fn iter(&self) -> impl Iterator<Item = &T> {
203        self.as_cells().into_iter().map(Deref::deref)
204    }
205
206    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
207        self.as_cells_mut().into_iter().map(DerefMut::deref_mut)
208    }
209}
210
211impl<T> List<'_, T> {
212    /// Borrow the List's slice of cells
213    ///
214    /// Note that like with Vec, this slice may move after appending to the List!
215    /// Due to lifetimes this isn't a problem until unsafe Rust becomes involved,
216    /// but with Postgres extensions it often does.
217    ///
218    /// Note that if you use this on a 0-item list, you get an empty slice,
219    /// which is not going to be equal to the null pointer.
220    pub fn as_cells(&self) -> &[ListCell<T>] {
221        unsafe {
222            match self {
223                List::Nil => &[],
224                List::Cons(inner) => slice::from_raw_parts(inner.as_cells_ptr(), inner.len()),
225            }
226        }
227    }
228
229    /// Mutably borrow the List's slice of cells
230    ///
231    /// Includes the same caveats as with `List::as_cells`, but with "less" problems:
232    /// `&mut` means you should not have other pointers to the list anyways.
233    ///
234    /// Note that if you use this on a 0-item list, you get an empty slice,
235    /// which is not going to be equal to the null pointer.
236    pub fn as_cells_mut(&mut self) -> &mut [ListCell<T>] {
237        // SAFETY: Note it is unsafe to read a union variant, but safe to set a union variant!
238        // This allows access to `&mut pg_sys::ListCell` to mangle a List's type in safe code.
239        // Also note that we can't yield &mut [T] because Postgres Lists aren't tight-packed.
240        // These facts are why the entire List type's interface isn't much simpler.
241        //
242        // This function is safe as long as ListCell<T> offers no way to corrupt the list,
243        // and as long as we correctly maintain the length of the List's type.
244        unsafe {
245            match self {
246                List::Nil => &mut [],
247                List::Cons(inner) => {
248                    slice::from_raw_parts_mut(inner.as_mut_cells_ptr(), inner.len())
249                }
250            }
251        }
252    }
253}
254
255impl<T> ListHead<'_, T> {
256    #[inline]
257    pub fn capacity(&self) -> usize {
258        unsafe { self.list.as_ref().max_length as usize }
259    }
260
261    /// Borrow the List's slice of cells
262    ///
263    /// Note that like with Vec, this slice may move after appending to the List!
264    /// Due to lifetimes this isn't a problem until unsafe Rust becomes involved,
265    /// but with Postgres extensions it often does.
266    pub fn as_cells(&self) -> &[ListCell<T>] {
267        unsafe { slice::from_raw_parts(self.as_cells_ptr(), self.len()) }
268    }
269
270    pub fn as_cells_ptr(&self) -> *const ListCell<T> {
271        unsafe { (*self.list.as_ptr()).elements.cast() }
272    }
273
274    pub fn as_mut_cells_ptr(&mut self) -> *mut ListCell<T> {
275        unsafe { (*self.list.as_ptr()).elements.cast() }
276    }
277}
278
279impl<T: Enlist> ListHead<'_, T> {
280    pub fn push(&mut self, value: T) -> &mut Self {
281        let list = unsafe { self.list.as_mut() };
282        let pg_sys::List { length, max_length, elements, .. } = list;
283        assert!(*max_length > 0);
284        assert!(*length > 0);
285        assert!(*max_length >= *length);
286        if *max_length - *length < 1 {
287            self.reserve(*max_length as _);
288        }
289
290        // SAFETY: Our list must have been constructed following the list invariants
291        // in order to actually get here, and we have confirmed as in-range of the buffer.
292        let cell = unsafe { &mut *elements.add(*length as _) };
293        T::endocytosis(cell, value);
294        *length += 1;
295        self
296    }
297
298    pub fn reserve(&mut self, count: usize) -> &mut Self {
299        let list = unsafe { self.list.as_mut() };
300        assert!(list.length > 0);
301        assert!(list.max_length > 0);
302        if ((list.max_length - list.length) as usize) < count {
303            let size = i32::try_from(count).unwrap();
304            let size = list.length.checked_add(size).unwrap();
305            let size = usize::try_from(size).unwrap();
306            unsafe { grow_list(list, size) };
307        };
308        self
309    }
310}
311
312unsafe fn grow_list(list: &mut pg_sys::List, target: usize) {
313    assert!((i32::MAX as usize) >= target, "Cannot allocate more than c_int::MAX elements");
314    let alloc_size = target * mem::size_of::<pg_sys::ListCell>();
315    if list.elements == ptr::addr_of_mut!(list.initial_elements).cast() {
316        // first realloc, we can't dealloc the elements ptr, as it isn't its own alloc
317        let context = pg_sys::GetMemoryChunkContext(list as *mut pg_sys::List as *mut _);
318        if context.is_null() {
319            panic!("Context free list?");
320        }
321        let buf = pg_sys::MemoryContextAlloc(context, alloc_size);
322        if buf.is_null() {
323            panic!("List allocation failure");
324        }
325        ptr::copy_nonoverlapping(list.elements, buf.cast(), list.length as _);
326        // This is the "clobber pattern" that Postgres uses.
327        #[cfg(debug_assertions)]
328        ptr::write_bytes(list.elements, 0x7F, list.length as _);
329        list.elements = buf.cast();
330    } else {
331        // We already have a separate buf, making this easy.
332        list.elements = pg_sys::repalloc(list.elements.cast(), alloc_size).cast();
333    }
334
335    list.max_length = target as _;
336}
337
338unsafe fn destroy_list(list: *mut pg_sys::List) {
339    // The only question is if we have two allocations or one?
340    if (*list).elements != ptr::addr_of_mut!((*list).initial_elements).cast() {
341        pg_sys::pfree((*list).elements.cast());
342    }
343    pg_sys::pfree(list.cast());
344}
345
346#[derive(Debug)]
347pub struct ListIter<'a, T> {
348    list: List<'a, T>,
349    iter: RawCellIter<T>,
350}
351
352/// A list being drained.
353#[derive(Debug)]
354pub struct Drain<'a, 'cx, T> {
355    /// Index of tail to preserve
356    tail_start: u32,
357    /// Length of tail
358    tail_len: u32,
359    /// Current remaining range to remove
360    iter: RawCellIter<T>,
361    origin: &'a mut List<'cx, T>,
362    raw: *mut pg_sys::List,
363}
364
365impl<'a, 'cx, T> Drop for Drain<'a, 'cx, T> {
366    fn drop(&mut self) {
367        if self.raw.is_null() {
368            return;
369        }
370
371        // SAFETY: The raw repr accepts null ptrs, but we just checked it's okay.
372        unsafe {
373            // Note that this may be 0, unlike elsewhere!
374            let len = (*self.raw).length;
375            if len == 0 && self.tail_len == 0 {
376                // Can't simply leave it be due to Postgres List invariants, else it leaks
377                destroy_list(self.raw)
378            } else {
379                // Need to weld over the drained part and fix the length
380                let src = (*self.raw).elements.add(self.tail_start as _);
381                let dst = (*self.raw).elements.add(len as _);
382                ptr::copy(src, dst, self.tail_len as _); // may overlap
383                (*self.raw).length = len + (self.tail_len as ffi::c_int);
384
385                // Put it back now that all invariants have been repaired
386                *self.origin = List::Cons(ListHead {
387                    list: NonNull::new_unchecked(self.raw),
388                    _type: PhantomData,
389                });
390            }
391        }
392    }
393}
394
395impl<T: Enlist> Iterator for Drain<'_, '_, T> {
396    type Item = T;
397
398    fn next(&mut self) -> Option<Self::Item> {
399        self.iter.next()
400    }
401}
402
403impl<T: Enlist> Iterator for ListIter<'_, T> {
404    type Item = T;
405
406    fn next(&mut self) -> Option<Self::Item> {
407        self.iter.next()
408    }
409}
410
411impl<'a, T: Enlist> IntoIterator for List<'a, T> {
412    type IntoIter = ListIter<'a, T>;
413    type Item = T;
414
415    fn into_iter(mut self) -> Self::IntoIter {
416        let len = self.len();
417        let iter = match &mut self {
418            List::Nil => Default::default(),
419            List::Cons(head) => {
420                let ptr = head.as_mut_cells_ptr();
421                let end = unsafe { ptr.add(len) };
422                RawCellIter { ptr, end }
423            }
424        };
425        ListIter { list: self, iter }
426    }
427}
428
429impl<T> Drop for ListIter<'_, T> {
430    fn drop(&mut self) {
431        if let List::Cons(head) = &mut self.list {
432            unsafe { destroy_list(head.list.as_ptr()) }
433        }
434    }
435}
436
437/// Needed because otherwise List hits incredibly irritating lifetime issues.
438///
439/// This must remain a private type, as casual usage of it is wildly unsound.
440///
441/// # Safety
442/// None. Repent that you made this.
443///
444/// This atrocity assumes pointers passed in are valid or that ptr >= end.
445#[derive(Debug, PartialEq)]
446struct RawCellIter<T> {
447    ptr: *mut ListCell<T>,
448    end: *mut ListCell<T>,
449}
450
451impl<T> Default for RawCellIter<T> {
452    fn default() -> Self {
453        RawCellIter { ptr: ptr::null_mut(), end: ptr::null_mut() }
454    }
455}
456
457impl<T: Enlist> Iterator for RawCellIter<T> {
458    type Item = T;
459
460    #[inline]
461    fn next(&mut self) -> Option<T> {
462        if self.ptr < self.end {
463            let ptr = self.ptr;
464            // SAFETY: It's assumed that the pointers are valid on construction
465            unsafe {
466                self.ptr = ptr.add(1);
467                Some(T::apoptosis(ptr.cast()).read())
468            }
469        } else {
470            None
471        }
472    }
473}