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}