pgrx/datum/borrow.rs
1#![deny(unsafe_op_in_unsafe_fn)]
2use super::*;
3use crate::layout::PassBy;
4use core::{ffi, mem, ptr};
5
6/// Types which can be "borrowed from" [`&Datum<'_>`] via simple cast, deref, or slicing
7///
8/// # Safety
9/// Despite its pleasant-sounding name, this implements a fairly low-level detail.
10/// It exists to allow other code to use that nice-sounding BorrowDatum bound.
11/// Outside of the pgrx library, it is probably incorrect to call and rely on this:
12/// instead use convenience functions like `Datum::borrow_as`.
13///
14/// Its behavior is trusted for ABI details, and it should not be implemented if any doubt
15/// exists of whether the type would be suitable for passing via Postgres.
16pub unsafe trait BorrowDatum {
17 /// The "native" passing convention for this type.
18 ///
19 /// - `PassBy::Value` implies [`mem::size_of<T>()`][size_of] <= [`mem::size_of::<Datum>()`][Datum].
20 /// - `PassBy::Ref` means the pointee will occupy at least 1 byte for variable-sized types.
21 ///
22 /// Note that this means a zero-sized type is inappropriate for `BorrowDatum`.
23 const PASS: PassBy;
24
25 /// Cast a pointer to this blob of bytes to a pointer to this type.
26 ///
27 /// This is not a simple `ptr.cast()` because it may be *unsizing*, which may require
28 /// reading varlena headers. For all fixed-size types, `ptr.cast()` should be correct.
29 ///
30 /// # Safety
31 /// - This must be correctly invoked for the pointee type, as it may deref and read one or more
32 /// bytes in its implementation in order to read the inline metadata and unsize the type.
33 /// - This must be invoked with a pointee initialized for the dynamically specified length.
34 ///
35 /// ## For Implementers
36 /// Reading the **first** byte pointed to is permitted if `T::PASS = PassBy::Ref`, assuming you
37 /// are implementing a varlena type. As the other dynamic length type, CStr also does this.
38 /// This function
39 /// - must NOT mutate the pointee
40 /// - must point to the entire datum's length (`size_of_val` must not lose bytes)
41 ///
42 /// Do not attempt to handle pass-by-value versus pass-by-ref in this fn's body!
43 /// A caller may be in a context where all types are handled by-reference, for instance.
44 unsafe fn point_from(ptr: ptr::NonNull<u8>) -> ptr::NonNull<Self>;
45
46 /// Cast a pointer to aligned varlena headers to this type
47 ///
48 /// This version allows you to assume the pointer is aligned to, and readable for, 4 bytes.
49 /// This optimization is not required. When in doubt, avoid implementing it, and rely on your
50 /// `point_from` implementation alone.
51 ///
52 /// # Safety
53 /// - This must be correctly invoked for the pointee type, as it may deref.
54 /// - This must be 4-byte aligned!
55 unsafe fn point_from_align4(ptr: ptr::NonNull<u32>) -> ptr::NonNull<Self> {
56 debug_assert!(ptr.is_aligned());
57 unsafe { BorrowDatum::point_from(ptr.cast()) }
58 }
59
60 /// Optimization for borrowing the referent
61 unsafe fn borrow_unchecked<'dat>(ptr: ptr::NonNull<u8>) -> &'dat Self {
62 unsafe { BorrowDatum::point_from(ptr).as_ref() }
63 }
64}
65
66/// From a pointer to a Datum, obtain a pointer to T's bytes
67///
68/// This may be None if T is PassBy::Ref
69///
70/// # Safety
71/// Assumes the Datum is init
72pub(crate) unsafe fn datum_ptr_to_bytes<T>(ptr: ptr::NonNull<Datum<'_>>) -> Option<ptr::NonNull<u8>>
73where
74 T: BorrowDatum,
75{
76 match T::PASS {
77 // Ptr<Datum> casts to Ptr<T>
78 PassBy::Value => Some(ptr.cast()),
79 // Ptr<Datum> derefs to Datum which to Ptr
80 PassBy::Ref => unsafe {
81 let datum = ptr.read();
82 ptr::NonNull::new(datum.sans_lifetime().cast_mut_ptr())
83 },
84 }
85}
86
87macro_rules! impl_borrow_fixed_len {
88 ($($value_ty:ty),*) => {
89 $(
90 unsafe impl BorrowDatum for $value_ty {
91 const PASS: PassBy = if mem::size_of::<Self>() <= mem::size_of::<Datum>() {
92 PassBy::Value
93 } else {
94 PassBy::Ref
95 };
96
97 unsafe fn point_from(ptr: ptr::NonNull<u8>) -> ptr::NonNull<Self> {
98 ptr.cast()
99 }
100 }
101 )*
102 }
103}
104
105impl_borrow_fixed_len! {
106 i8, i16, i32, i64, bool, f32, f64,
107 pg_sys::Oid, pg_sys::Point,
108 Date, Time, Timestamp, TimestampWithTimeZone
109}
110
111/// It is rare to pass CStr via Datums, but not unheard of
112unsafe impl BorrowDatum for ffi::CStr {
113 const PASS: PassBy = PassBy::Ref;
114
115 unsafe fn point_from(ptr: ptr::NonNull<u8>) -> ptr::NonNull<Self> {
116 let char_ptr: *mut ffi::c_char = ptr.as_ptr().cast();
117 unsafe {
118 let len = ffi::CStr::from_ptr(char_ptr).to_bytes_with_nul().len();
119 ptr::NonNull::new_unchecked(ptr::slice_from_raw_parts_mut(char_ptr, len) as *mut Self)
120 }
121 }
122
123 unsafe fn borrow_unchecked<'dat>(ptr: ptr::NonNull<u8>) -> &'dat Self {
124 let char_ptr: *const ffi::c_char = ptr.as_ptr().cast();
125 unsafe { ffi::CStr::from_ptr(char_ptr) }
126 }
127}