|
| 1 | +#![deny(unsafe_op_in_unsafe_fn)] |
| 2 | +use super::*; |
| 3 | +use crate::layout::PassBy; |
| 4 | +use 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. |
| 16 | +pub 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 |
| 72 | +pub(crate) unsafe fn datum_ptr_to_bytes<T>(ptr: ptr::NonNull<Datum<'_>>) -> Option<ptr::NonNull<u8>> |
| 73 | +where |
| 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 | + let ptr = ptr::NonNull::new(datum.sans_lifetime().cast_mut_ptr()); |
| 83 | + ptr |
| 84 | + }, |
| 85 | + } |
| 86 | +} |
| 87 | + |
| 88 | +macro_rules! impl_borrow_fixed_len { |
| 89 | + ($($value_ty:ty),*) => { |
| 90 | + $( |
| 91 | + unsafe impl BorrowDatum for $value_ty { |
| 92 | + const PASS: PassBy = if mem::size_of::<Self>() <= mem::size_of::<Datum>() { |
| 93 | + PassBy::Value |
| 94 | + } else { |
| 95 | + PassBy::Ref |
| 96 | + }; |
| 97 | + |
| 98 | + unsafe fn point_from(ptr: ptr::NonNull<u8>) -> ptr::NonNull<Self> { |
| 99 | + ptr.cast() |
| 100 | + } |
| 101 | + } |
| 102 | + )* |
| 103 | + } |
| 104 | +} |
| 105 | + |
| 106 | +impl_borrow_fixed_len! { |
| 107 | + i8, i16, i32, i64, bool, f32, f64, |
| 108 | + pg_sys::Oid, pg_sys::Point, |
| 109 | + Date, Time, Timestamp, TimestampWithTimeZone |
| 110 | +} |
| 111 | + |
| 112 | +/// It is rare to pass CStr via Datums, but not unheard of |
| 113 | +unsafe impl BorrowDatum for ffi::CStr { |
| 114 | + const PASS: PassBy = PassBy::Ref; |
| 115 | + |
| 116 | + unsafe fn point_from(ptr: ptr::NonNull<u8>) -> ptr::NonNull<Self> { |
| 117 | + let char_ptr: *mut ffi::c_char = ptr.as_ptr().cast(); |
| 118 | + unsafe { |
| 119 | + let len = ffi::CStr::from_ptr(char_ptr).to_bytes_with_nul().len(); |
| 120 | + ptr::NonNull::new_unchecked(ptr::slice_from_raw_parts_mut(char_ptr, len) as *mut Self) |
| 121 | + } |
| 122 | + } |
| 123 | + |
| 124 | + unsafe fn borrow_unchecked<'dat>(ptr: ptr::NonNull<u8>) -> &'dat Self { |
| 125 | + let char_ptr: *const ffi::c_char = ptr.as_ptr().cast(); |
| 126 | + unsafe { ffi::CStr::from_ptr(char_ptr) } |
| 127 | + } |
| 128 | +} |
0 commit comments