Skip to main content

samp_sdk/
cell.rs

1//! Smart pointers for accessing AMX VM cells from safe Rust code.
2//!
3//! A "cell" is the native Pawn VM type: a 32-bit integer (`i32`) that can
4//! represent `int`, `float` (via bit reinterpretation) or a pointer relative
5//! to the AMX heap/data. The types in this module wrap those cells with
6//! Rust semantics:
7//!
8//! - [`Ref<T>`]: typed pointer to a cell (by-reference output of natives).
9//! - [`Buffer`] / [`UnsizedBuffer`]: array of contiguous cells.
10//! - [`AmxString`]: native Pawn string (cell vector with `0` terminator).
11//! - [`AmxCell`], [`AmxPrimitive`], [`CellConvert`]: conversion traits.
12
13use std::marker::PhantomData;
14use std::ops::{Deref, DerefMut};
15
16use crate::amx::Amx;
17use crate::error::AmxResult;
18
19pub mod buffer;
20pub mod repr;
21pub mod string;
22
23pub use buffer::{Buffer, UnsizedBuffer};
24pub use repr::{AmxCell, AmxPrimitive, CellConvert};
25pub use string::AmxString;
26
27/// Typed pointer to a live cell in the AMX heap/data.
28///
29/// Implements [`Deref`] and [`DerefMut`] for `T`, allowing reading and writing
30/// the cell directly (`*r`). The `'amx` lifetime ensures the `Ref` does not
31/// outlive its `Amx`, avoiding dangling pointers.
32pub struct Ref<'amx, T: Sized + AmxPrimitive> {
33    amx_addr: i32,
34    phys_addr: *mut T,
35    marker: PhantomData<&'amx Amx>,
36}
37
38impl<'amx, T: Sized + AmxPrimitive> Ref<'amx, T> {
39    /// Creates a `Ref` from the (AMX address, physical address) pair, already resolved.
40    ///
41    /// Prefer obtaining `Ref` via [`Amx::get_ref`] or via automatic parsing of
42    /// native arguments — this direct API is the low-level path used
43    /// internally by the SDK.
44    ///
45    /// # Safety
46    /// `phys_addr` must point to a live `T` cell for as long as this
47    /// `Ref` exists and must be aligned for `T` (debug asserts validate both
48    /// conditions in builds with `debug_assertions`).
49    ///
50    /// [`Amx::get_ref`]: crate::amx::Amx::get_ref
51    pub unsafe fn new(amx_addr: i32, phys_addr: *mut T) -> Ref<'amx, T> {
52        debug_assert!(!phys_addr.is_null(), "Ref::new() received null pointer");
53        debug_assert!(
54            (phys_addr as usize).is_multiple_of(std::mem::align_of::<T>()),
55            "Ref::new() received misaligned pointer for {}",
56            std::any::type_name::<T>()
57        );
58        Ref {
59            amx_addr,
60            phys_addr,
61            marker: PhantomData,
62        }
63    }
64
65    /// Address of the cell in the AMX address space (not the physical pointer).
66    ///
67    /// This is the value the VM sees — useful when passing the cell back into
68    /// calls to other AMX functions.
69    #[inline]
70    #[must_use]
71    pub fn address(&self) -> i32 {
72        self.amx_addr
73    }
74
75    /// Physical (host) pointer to the cell.
76    #[inline]
77    #[must_use]
78    pub fn as_ptr(&self) -> *const T {
79        self.phys_addr
80    }
81
82    /// Mutable physical pointer to the cell.
83    #[inline]
84    pub fn as_mut_ptr(&mut self) -> *mut T {
85        self.phys_addr
86    }
87}
88
89impl<T: Sized + AmxPrimitive> Deref for Ref<'_, T> {
90    type Target = T;
91
92    fn deref(&self) -> &T {
93        unsafe { &*self.phys_addr }
94    }
95}
96
97impl<T: Sized + AmxPrimitive> DerefMut for Ref<'_, T> {
98    fn deref_mut(&mut self) -> &mut T {
99        unsafe { &mut *self.phys_addr }
100    }
101}
102
103impl<'amx, T: Sized + AmxPrimitive> AmxCell<'amx> for Ref<'amx, T> {
104    fn from_raw(amx: &'amx Amx, cell: i32) -> AmxResult<Ref<'amx, T>> {
105        amx.get_ref(cell)
106    }
107
108    fn as_cell(&self) -> i32 {
109        self.address()
110    }
111}