pgrx_pg_sys/
port.rs

1use crate as pg_sys;
2use core::mem::offset_of;
3use core::str::FromStr;
4
5/// this comes from `postgres_ext.h`
6pub const InvalidOid: crate::Oid = crate::Oid::INVALID;
7pub const InvalidOffsetNumber: super::OffsetNumber = 0;
8pub const FirstOffsetNumber: super::OffsetNumber = 1;
9pub const MaxOffsetNumber: super::OffsetNumber =
10    (super::BLCKSZ as usize / std::mem::size_of::<super::ItemIdData>()) as super::OffsetNumber;
11pub const InvalidBlockNumber: u32 = 0xFFFF_FFFF as crate::BlockNumber;
12pub const VARHDRSZ: usize = std::mem::size_of::<super::int32>();
13pub const InvalidCommandId: super::CommandId = (!(0 as super::CommandId)) as super::CommandId;
14pub const FirstCommandId: super::CommandId = 0 as super::CommandId;
15pub const InvalidTransactionId: crate::TransactionId = crate::TransactionId::INVALID;
16pub const BootstrapTransactionId: crate::TransactionId = crate::TransactionId::BOOTSTRAP;
17pub const FrozenTransactionId: crate::TransactionId = crate::TransactionId::FROZEN;
18pub const FirstNormalTransactionId: crate::TransactionId = crate::TransactionId::FIRST_NORMAL;
19pub const MaxTransactionId: crate::TransactionId = crate::TransactionId::MAX;
20
21/// Given a valid HeapTuple pointer, return address of the user data
22///
23/// # Safety
24///
25/// This function cannot determine if the `tuple` argument is really a non-null pointer to a [`pg_sys::HeapTuple`].
26#[inline(always)]
27pub unsafe fn GETSTRUCT(tuple: crate::HeapTuple) -> *mut std::os::raw::c_char {
28    // #define GETSTRUCT(TUP) ((char *) ((TUP)->t_data) + (TUP)->t_data->t_hoff)
29
30    // SAFETY:  The caller has asserted `tuple` is a valid HeapTuple and is properly aligned
31    // Additionally, t_data.t_hoff is an a u8, so it'll fit inside a usize
32    (*tuple).t_data.cast::<std::os::raw::c_char>().add((*(*tuple).t_data).t_hoff as _)
33}
34
35//
36// TODO: [`TYPEALIGN`] and [`MAXALIGN`] are also part of PR #948 and when that's all merged,
37//       their uses should be switched to these
38//
39
40#[allow(non_snake_case)]
41#[inline(always)]
42pub const unsafe fn TYPEALIGN(alignval: usize, len: usize) -> usize {
43    // #define TYPEALIGN(ALIGNVAL,LEN)  \
44    // (((uintptr_t) (LEN) + ((ALIGNVAL) - 1)) & ~((uintptr_t) ((ALIGNVAL) - 1)))
45    ((len) + ((alignval) - 1)) & !((alignval) - 1)
46}
47
48#[allow(non_snake_case)]
49#[inline(always)]
50pub const unsafe fn MAXALIGN(len: usize) -> usize {
51    // #define MAXALIGN(LEN) TYPEALIGN(MAXIMUM_ALIGNOF, (LEN))
52    TYPEALIGN(pg_sys::MAXIMUM_ALIGNOF as _, len)
53}
54
55///  Given a currently-allocated chunk of Postgres allocated memory, determine the context
56///  it belongs to.
57///
58/// All chunks allocated by any memory context manager are required to be
59/// preceded by the corresponding MemoryContext stored, without padding, in the
60/// preceding sizeof(void*) bytes.  A currently-allocated chunk must contain a
61/// backpointer to its owning context.  The backpointer is used by pfree() and
62/// repalloc() to find the context to call.
63///
64/// # Safety
65///
66/// The specified `pointer` **must** be one allocated by Postgres (via [`palloc`] and friends).
67///
68///
69/// # Panics
70///
71/// This function will panic if `pointer` is null, if it's not properly aligned, or if the memory
72/// it points to doesn't have a prefix that looks like a memory context pointer
73///
74/// [`palloc`]: crate::palloc
75#[allow(non_snake_case)]
76pub unsafe fn GetMemoryChunkContext(pointer: *mut std::os::raw::c_void) -> pg_sys::MemoryContext {
77    #[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
78    {
79        // Postgres versions <16 don't export the "GetMemoryChunkContext" function.  It's a "static inline"
80        // function in `memutils.h`, so we port it to Rust right here
81        /*
82         * Try to detect bogus pointers handed to us, poorly though we can.
83         * Presumably, a pointer that isn't MAXALIGNED isn't pointing at an
84         * allocated chunk.
85         */
86        assert!(!pointer.is_null());
87        assert_eq!(pointer, MAXALIGN(pointer as usize) as *mut ::std::os::raw::c_void);
88
89        /*
90         * OK, it's probably safe to look at the context.
91         */
92        // 	context = *(MemoryContext *) (((char *) pointer) - sizeof(void *));
93        let context = unsafe {
94            // SAFETY: the caller has assured us that `pointer` points to palloc'd memory, which
95            // means it'll have this header before it
96            *(pointer
97                .cast::<::std::os::raw::c_char>()
98                .sub(std::mem::size_of::<*mut ::std::os::raw::c_void>())
99                .cast())
100        };
101
102        assert!(MemoryContextIsValid(context));
103
104        context
105    }
106    #[cfg(any(feature = "pg16", feature = "pg17"))]
107    {
108        #[pgrx_macros::pg_guard]
109        extern "C-unwind" {
110            #[link_name = "GetMemoryChunkContext"]
111            pub fn extern_fn(pointer: *mut std::os::raw::c_void) -> pg_sys::MemoryContext;
112        }
113        extern_fn(pointer)
114    }
115}
116
117/// Returns true if memory context is tagged correctly according to Postgres.
118///
119/// # Safety
120///
121/// The caller must only attempt this on a pointer to a Node.
122/// This may clarify if the pointee is correctly-initialized [`pg_sys::MemoryContextData`].
123///
124/// # Implementation Note
125///
126/// If Postgres adds more memory context types in the future, we need to do that here too.
127#[allow(non_snake_case)]
128#[inline(always)]
129pub unsafe fn MemoryContextIsValid(context: crate::MemoryContext) -> bool {
130    // #define MemoryContextIsValid(context) \
131    // 	((context) != NULL && \
132    // 	 (IsA((context), AllocSetContext) || \
133    // 	  IsA((context), SlabContext) || \
134    // 	  IsA((context), GenerationContext)))
135
136    !context.is_null()
137        && unsafe {
138            // SAFETY:  we just determined the pointer isn't null, and
139            // the caller asserts that it is being used on a Node.
140            let tag = (*context.cast::<crate::Node>()).type_;
141            use crate::NodeTag::*;
142            matches!(tag, T_AllocSetContext | T_SlabContext | T_GenerationContext)
143        }
144}
145
146pub const VARHDRSZ_EXTERNAL: usize = offset_of!(super::varattrib_1b_e, va_data);
147pub const VARHDRSZ_SHORT: usize = offset_of!(super::varattrib_1b, va_data);
148
149#[inline]
150pub fn get_pg_major_version_string() -> &'static str {
151    super::PG_MAJORVERSION.to_str().unwrap()
152}
153
154#[inline]
155pub fn get_pg_major_version_num() -> u16 {
156    u16::from_str(super::get_pg_major_version_string()).unwrap()
157}
158
159#[cfg(any(not(target_env = "msvc"), feature = "pg17"))]
160#[inline]
161pub fn get_pg_version_string() -> &'static str {
162    super::PG_VERSION_STR.to_str().unwrap()
163}
164
165#[cfg(all(
166    target_env = "msvc",
167    any(feature = "pg13", feature = "pg14", feature = "pg15", feature = "pg16")
168))]
169#[inline]
170pub fn get_pg_version_string() -> &'static str {
171    // bindgen cannot get value of PG_VERSION_STR
172    // PostgreSQL @0@ on @1@-@2@, compiled by @3@-@4@, @5@-bit
173    static PG_VERSION_STR: [u8; 256] = const {
174        let major = super::PG_MAJORVERSION_NUM;
175        let minor = super::PG_MINORVERSION_NUM;
176        #[cfg(target_pointer_width = "32")]
177        let pointer_width = 32_u32;
178        #[cfg(target_pointer_width = "64")]
179        let pointer_width = 64_u32;
180        // a fake value
181        let msc_ver = b"1700";
182        let mut buffer = [0u8; 256];
183        let mut pointer = 0;
184        {
185            let s = b"PostgreSQL ";
186            let mut i = 0;
187            while i < s.len() {
188                buffer[pointer + i] = s[i];
189                i += 1;
190            }
191            pointer += s.len();
192        }
193        {
194            buffer[pointer + 0] = b'0' + (major / 10) as u8;
195            buffer[pointer + 1] = b'0' + (major % 10) as u8;
196            pointer += 2;
197        }
198        {
199            let s = b".";
200            let mut i = 0;
201            while i < s.len() {
202                buffer[pointer + i] = s[i];
203                i += 1;
204            }
205            pointer += s.len();
206        }
207        if minor < 10 {
208            buffer[pointer + 0] = b'0' + (minor % 10) as u8;
209            pointer += 1;
210        } else {
211            buffer[pointer + 0] = b'0' + (minor / 10) as u8;
212            buffer[pointer + 1] = b'0' + (minor % 10) as u8;
213            pointer += 2;
214        }
215        {
216            let s = b", compiled by Visual C++ build ";
217            let mut i = 0;
218            while i < s.len() {
219                buffer[pointer + i] = s[i];
220                i += 1;
221            }
222            pointer += s.len();
223        }
224        {
225            let s = msc_ver;
226            let mut i = 0;
227            while i < s.len() {
228                buffer[pointer + i] = s[i];
229                i += 1;
230            }
231            pointer += s.len();
232        }
233        {
234            let s = b", ";
235            let mut i = 0;
236            while i < s.len() {
237                buffer[pointer + i] = s[i];
238                i += 1;
239            }
240            pointer += s.len();
241        }
242        {
243            buffer[pointer + 0] = b'0' + (pointer_width / 10) as u8;
244            buffer[pointer + 1] = b'0' + (pointer_width % 10) as u8;
245            pointer += 2;
246        }
247        {
248            let s = b"-bit";
249            let mut i = 0;
250            while i < s.len() {
251                buffer[pointer + i] = s[i];
252                i += 1;
253            }
254            pointer += s.len();
255        }
256        buffer[pointer] = 0;
257        buffer
258    };
259    unsafe { std::ffi::CStr::from_ptr(PG_VERSION_STR.as_ptr().cast()).to_str().unwrap() }
260}
261
262#[inline]
263pub fn get_pg_major_minor_version_string() -> &'static str {
264    super::PG_VERSION.to_str().unwrap()
265}
266
267#[inline]
268pub fn TransactionIdIsNormal(xid: super::TransactionId) -> bool {
269    xid >= FirstNormalTransactionId
270}
271
272/// ```c
273///     #define type_is_array(typid)  (get_element_type(typid) != InvalidOid)
274/// ```
275#[inline]
276pub unsafe fn type_is_array(typoid: super::Oid) -> bool {
277    super::get_element_type(typoid) != InvalidOid
278}
279
280/// #define BufferGetPage(buffer) ((Page)BufferGetBlock(buffer))
281#[inline]
282pub unsafe fn BufferGetPage(buffer: crate::Buffer) -> crate::Page {
283    BufferGetBlock(buffer) as crate::Page
284}
285
286/// #define BufferGetBlock(buffer) \
287/// ( \
288///      AssertMacro(BufferIsValid(buffer)), \
289///      BufferIsLocal(buffer) ? \
290///            LocalBufferBlockPointers[-(buffer) - 1] \
291///      : \
292///            (Block) (BufferBlocks + ((Size) ((buffer) - 1)) * BLCKSZ) \
293/// )
294#[inline]
295pub unsafe fn BufferGetBlock(buffer: crate::Buffer) -> crate::Block {
296    if BufferIsLocal(buffer) {
297        *crate::LocalBufferBlockPointers.offset(((-buffer) - 1) as isize)
298    } else {
299        crate::BufferBlocks.add(((buffer as crate::Size) - 1) * crate::BLCKSZ as usize)
300            as crate::Block
301    }
302}
303
304/// #define BufferIsLocal(buffer)      ((buffer) < 0)
305#[inline]
306pub unsafe fn BufferIsLocal(buffer: crate::Buffer) -> bool {
307    buffer < 0
308}
309
310/// Retrieve the "user data" of the specified [`pg_sys::HeapTuple`] as a specific type. Typically this
311/// will be a struct that represents a Postgres system catalog, such as [`FormData_pg_class`].
312///
313/// # Returns
314///
315/// A pointer to the [`pg_sys::HeapTuple`]'s "user data", cast as a mutable pointer to `T`.  If the
316/// specified `htup` pointer is null, the null pointer is returned.
317///
318/// # Safety
319///
320/// This function cannot verify that the specified `htup` points to a valid [`pg_sys::HeapTuple`] nor
321/// that if it does, that its bytes are bitwise compatible with `T`.
322///
323/// [`FormData_pg_class`]: crate::FormData_pg_class
324#[inline]
325pub unsafe fn heap_tuple_get_struct<T>(htup: super::HeapTuple) -> *mut T {
326    if htup.is_null() {
327        std::ptr::null_mut()
328    } else {
329        unsafe {
330            // SAFETY:  The caller has told us `htop` is a valid HeapTuple
331            GETSTRUCT(htup).cast()
332        }
333    }
334}
335
336// All of this weird code is in response to Postgres having a relatively cavalier attitude about types:
337// - https://github.com/postgres/postgres/commit/1c27d16e6e5c1f463bbe1e9ece88dda811235165
338//
339// As a result, we redeclare their functions with the arguments they should have on earlier Postgres
340// and we route people to the old symbols they were using before on later ones.
341#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
342#[::pgrx_macros::pg_guard]
343extern "C-unwind" {
344    pub fn planstate_tree_walker(
345        planstate: *mut super::PlanState,
346        walker: ::core::option::Option<
347            unsafe extern "C-unwind" fn(*mut super::PlanState, *mut ::core::ffi::c_void) -> bool,
348        >,
349        context: *mut ::core::ffi::c_void,
350    ) -> bool;
351
352    pub fn query_tree_walker(
353        query: *mut super::Query,
354        walker: ::core::option::Option<
355            unsafe extern "C-unwind" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
356        >,
357        context: *mut ::core::ffi::c_void,
358        flags: ::core::ffi::c_int,
359    ) -> bool;
360
361    pub fn query_or_expression_tree_walker(
362        node: *mut super::Node,
363        walker: ::core::option::Option<
364            unsafe extern "C-unwind" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
365        >,
366        context: *mut ::core::ffi::c_void,
367        flags: ::core::ffi::c_int,
368    ) -> bool;
369
370    pub fn range_table_entry_walker(
371        rte: *mut super::RangeTblEntry,
372        walker: ::core::option::Option<
373            unsafe extern "C-unwind" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
374        >,
375        context: *mut ::core::ffi::c_void,
376        flags: ::core::ffi::c_int,
377    ) -> bool;
378
379    pub fn range_table_walker(
380        rtable: *mut super::List,
381        walker: ::core::option::Option<
382            unsafe extern "C-unwind" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
383        >,
384        context: *mut ::core::ffi::c_void,
385        flags: ::core::ffi::c_int,
386    ) -> bool;
387
388    pub fn expression_tree_walker(
389        node: *mut super::Node,
390        walker: ::core::option::Option<
391            unsafe extern "C-unwind" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
392        >,
393        context: *mut ::core::ffi::c_void,
394    ) -> bool;
395
396    pub fn raw_expression_tree_walker(
397        node: *mut super::Node,
398        walker: ::core::option::Option<
399            unsafe extern "C-unwind" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
400        >,
401        context: *mut ::core::ffi::c_void,
402    ) -> bool;
403}
404
405#[cfg(any(feature = "pg16", feature = "pg17"))]
406pub unsafe fn planstate_tree_walker(
407    planstate: *mut super::PlanState,
408    walker: ::core::option::Option<
409        unsafe extern "C-unwind" fn(*mut super::PlanState, *mut ::core::ffi::c_void) -> bool,
410    >,
411    context: *mut ::core::ffi::c_void,
412) -> bool {
413    crate::planstate_tree_walker_impl(planstate, walker, context)
414}
415
416#[cfg(any(feature = "pg16", feature = "pg17"))]
417pub unsafe fn query_tree_walker(
418    query: *mut super::Query,
419    walker: ::core::option::Option<
420        unsafe extern "C-unwind" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
421    >,
422    context: *mut ::core::ffi::c_void,
423    flags: ::core::ffi::c_int,
424) -> bool {
425    crate::query_tree_walker_impl(query, walker, context, flags)
426}
427
428#[cfg(any(feature = "pg16", feature = "pg17"))]
429pub unsafe fn query_or_expression_tree_walker(
430    node: *mut super::Node,
431    walker: ::core::option::Option<
432        unsafe extern "C-unwind" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
433    >,
434    context: *mut ::core::ffi::c_void,
435    flags: ::core::ffi::c_int,
436) -> bool {
437    crate::query_or_expression_tree_walker_impl(node, walker, context, flags)
438}
439
440#[cfg(any(feature = "pg16", feature = "pg17"))]
441pub unsafe fn expression_tree_walker(
442    node: *mut crate::Node,
443    walker: Option<unsafe extern "C-unwind" fn(*mut crate::Node, *mut ::core::ffi::c_void) -> bool>,
444    context: *mut ::core::ffi::c_void,
445) -> bool {
446    crate::expression_tree_walker_impl(node, walker, context)
447}
448
449#[cfg(any(feature = "pg16", feature = "pg17"))]
450pub unsafe fn range_table_entry_walker(
451    rte: *mut super::RangeTblEntry,
452    walker: ::core::option::Option<
453        unsafe extern "C-unwind" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
454    >,
455    context: *mut ::core::ffi::c_void,
456    flags: ::core::ffi::c_int,
457) -> bool {
458    crate::range_table_entry_walker_impl(rte, walker, context, flags)
459}
460
461#[cfg(any(feature = "pg16", feature = "pg17"))]
462pub unsafe fn range_table_walker(
463    rtable: *mut super::List,
464    walker: ::core::option::Option<
465        unsafe extern "C-unwind" fn(*mut super::Node, *mut ::core::ffi::c_void) -> bool,
466    >,
467    context: *mut ::core::ffi::c_void,
468    flags: ::core::ffi::c_int,
469) -> bool {
470    crate::range_table_walker_impl(rtable, walker, context, flags)
471}
472
473#[cfg(any(feature = "pg16", feature = "pg17"))]
474pub unsafe fn raw_expression_tree_walker(
475    node: *mut crate::Node,
476    walker: Option<unsafe extern "C-unwind" fn(*mut crate::Node, *mut ::core::ffi::c_void) -> bool>,
477    context: *mut ::core::ffi::c_void,
478) -> bool {
479    crate::raw_expression_tree_walker_impl(node, walker, context)
480}
481
482#[inline(always)]
483pub unsafe fn MemoryContextSwitchTo(context: crate::MemoryContext) -> crate::MemoryContext {
484    let old = crate::CurrentMemoryContext;
485
486    crate::CurrentMemoryContext = context;
487    old
488}
489
490#[allow(non_snake_case)]
491#[inline(always)]
492#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
493pub unsafe fn BufferGetPageSize(buffer: pg_sys::Buffer) -> pg_sys::Size {
494    // #define BufferGetPageSize(buffer) \
495    // ( \
496    //     AssertMacro(BufferIsValid(buffer)), \
497    //     (Size)BLCKSZ \
498    // )
499    assert!(BufferIsValid(buffer));
500    pg_sys::BLCKSZ as pg_sys::Size
501}
502
503#[allow(non_snake_case)]
504#[inline(always)]
505#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
506pub unsafe fn ItemIdGetOffset(item_id: pg_sys::ItemId) -> u32 {
507    // #define ItemIdGetOffset(itemId) \
508    // ((itemId)->lp_off)
509    (*item_id).lp_off()
510}
511
512#[allow(non_snake_case)]
513#[inline(always)]
514#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
515pub unsafe fn PageIsValid(page: pg_sys::Page) -> bool {
516    // #define PageIsValid(page) PointerIsValid(page)
517    !page.is_null()
518}
519
520#[allow(non_snake_case)]
521#[inline(always)]
522#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
523pub unsafe fn PageIsEmpty(page: pg_sys::Page) -> bool {
524    // #define PageIsEmpty(page) \
525    // (((PageHeader) (page))->pd_lower <= SizeOfPageHeaderData)
526    const SizeOfPageHeaderData: pg_sys::Size =
527        core::mem::offset_of!(pg_sys::PageHeaderData, pd_linp);
528    let page_header = page as *mut pg_sys::PageHeaderData;
529    (*page_header).pd_lower <= SizeOfPageHeaderData as u16
530}
531
532#[allow(non_snake_case)]
533#[inline(always)]
534#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
535pub unsafe fn PageIsNew(page: pg_sys::Page) -> bool {
536    // #define PageIsNew(page) (((PageHeader) (page))->pd_upper == 0)
537    let page_header = page as *mut pg_sys::PageHeaderData;
538    (*page_header).pd_upper == 0
539}
540
541#[allow(non_snake_case)]
542#[inline(always)]
543#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
544pub unsafe fn PageGetItemId(page: pg_sys::Page, offset: pg_sys::OffsetNumber) -> pg_sys::ItemId {
545    // #define PageGetItemId(page, offsetNumber) \
546    // ((ItemId) (&((PageHeader) (page))->pd_linp[(offsetNumber) - 1]))
547    let page_header = page as *mut pg_sys::PageHeaderData;
548    (*page_header).pd_linp.as_mut_ptr().add(offset as usize - 1)
549}
550
551#[allow(non_snake_case)]
552#[inline(always)]
553#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
554pub unsafe fn PageGetContents(page: pg_sys::Page) -> *mut ::core::ffi::c_char {
555    // #define PageGetContents(page) \
556    // ((char *) (page) + MAXALIGN(SizeOfPageHeaderData))
557    const SizeOfPageHeaderData: pg_sys::Size =
558        core::mem::offset_of!(pg_sys::PageHeaderData, pd_linp);
559    page.add(pg_sys::MAXALIGN(SizeOfPageHeaderData) as usize) as *mut ::core::ffi::c_char
560}
561
562#[allow(non_snake_case)]
563#[inline(always)]
564#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
565pub fn PageSizeIsValid(page_size: usize) -> bool {
566    // #define PageSizeIsValid(pageSize) ((pageSize) == BLCKSZ)
567    page_size == pg_sys::BLCKSZ as usize
568}
569
570#[allow(non_snake_case)]
571#[inline(always)]
572#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
573pub unsafe fn PageGetPageSize(page: pg_sys::Page) -> usize {
574    // #define PageGetPageSize(page) \
575    // ((Size) (((PageHeader) (page))->pd_pagesize_version & (uint16) 0xFF00))
576    let page_header = page as *mut pg_sys::PageHeaderData;
577    ((*page_header).pd_pagesize_version & 0xFF00) as usize
578}
579
580#[allow(non_snake_case)]
581#[inline(always)]
582#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
583pub unsafe fn PageGetPageLayoutVersion(page: pg_sys::Page) -> ::core::ffi::c_char {
584    // #define PageGetPageLayoutVersion(page) \
585    // (((PageHeader) (page))->pd_pagesize_version & 0x00FF)
586    let page_header = page as *mut pg_sys::PageHeaderData;
587    ((*page_header).pd_pagesize_version & 0x00FF) as ::core::ffi::c_char
588}
589
590#[allow(non_snake_case)]
591#[inline(always)]
592#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
593pub unsafe fn PageSetPageSizeAndVersion(page: pg_sys::Page, size: u16, version: u8) {
594    // #define PageSetPageSizeAndVersion(page, size, version) \
595    // ((PageHeader) (page))->pd_pagesize_version = (size) | (version)
596    let page_header = page as *mut pg_sys::PageHeaderData;
597    (*page_header).pd_pagesize_version = size | (version as u16);
598}
599
600#[allow(non_snake_case)]
601#[inline(always)]
602#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
603pub unsafe fn PageGetSpecialSize(page: pg_sys::Page) -> u16 {
604    // #define PageGetSpecialSize(page) \
605    // ((uint16) (PageGetPageSize(page) - ((PageHeader)(page))->pd_special))
606    let page_header = page as *mut pg_sys::PageHeaderData;
607    PageGetPageSize(page) as u16 - (*page_header).pd_special
608}
609
610#[allow(non_snake_case)]
611#[inline(always)]
612#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
613pub unsafe fn PageGetSpecialPointer(page: pg_sys::Page) -> *mut ::core::ffi::c_char {
614    // #define PageGetSpecialPointer(page) \
615    // ((char *) ((char *) (page) + ((PageHeader) (page))->pd_special))
616    let page_header = page as *mut pg_sys::PageHeaderData;
617    page.add((*page_header).pd_special as usize) as *mut ::core::ffi::c_char
618}
619
620#[allow(non_snake_case)]
621#[inline(always)]
622#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
623pub unsafe fn PageGetItem(page: pg_sys::Page, item_id: pg_sys::ItemId) -> *mut ::core::ffi::c_char {
624    // #define PageGetItem(page, itemId) \
625    // (((char *)(page)) + ItemIdGetOffset(itemId))
626    page.add(ItemIdGetOffset(item_id) as usize)
627}
628
629#[allow(non_snake_case)]
630#[inline(always)]
631#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
632pub unsafe fn PageGetMaxOffsetNumber(page: pg_sys::Page) -> pg_sys::OffsetNumber {
633    // #define PageGetMaxOffsetNumber(page) \
634    // (((PageHeader) (page))->pd_lower <= SizeOfPageHeaderData ? 0 : \
635    // ((((PageHeader) (page))->pd_lower - SizeOfPageHeaderData) / sizeof(ItemIdData)))
636    const SizeOfPageHeaderData: pg_sys::Size =
637        core::mem::offset_of!(pg_sys::PageHeaderData, pd_linp);
638    let page_header = page as *mut pg_sys::PageHeaderData;
639    if (*page_header).pd_lower <= SizeOfPageHeaderData as u16 {
640        0
641    } else {
642        ((*page_header).pd_lower - SizeOfPageHeaderData as u16)
643            / std::mem::size_of::<pg_sys::ItemIdData>() as u16
644    }
645}
646
647#[allow(non_snake_case)]
648#[inline(always)]
649#[cfg(any(feature = "pg13", feature = "pg14", feature = "pg15"))]
650pub unsafe fn BufferIsValid(buffer: pg_sys::Buffer) -> bool {
651    // static inline bool
652    // BufferIsValid(Buffer bufnum)
653    // {
654    //     Assert(bufnum <= NBuffers);
655    //     Assert(bufnum >= -NLocBuffer);
656
657    //     return bufnum != InvalidBuffer;
658    // }
659    assert!(buffer <= pg_sys::NBuffers);
660    assert!(buffer >= -pg_sys::NLocBuffer);
661    buffer != pg_sys::InvalidBuffer as pg_sys::Buffer
662}