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