Skip to main content

jiminy_core/
abi.rs

1//! Jiminy ABI field primitives — alignment-1 wire types.
2//!
3//! These are the canonical field types for `zero_copy_layout!` structs.
4//! Each type is `#[repr(transparent)]` over a byte array, guaranteeing
5//! alignment 1 and eliminating UB from unaligned references on any target.
6//!
7//! ## Why
8//!
9//! SBF has 1-byte alignment for everything, so raw `u64` overlays work
10//! on-chain. But native tests, Miri, and future VMs may enforce real
11//! alignment — and taking `&u64` to an unaligned pointer is instant UB.
12//!
13//! These wrappers store fields as `[u8; N]` (LE) and access via
14//! `from_le_bytes` / `to_le_bytes`. Zero overhead on SBF. Safe everywhere.
15//!
16//! ## Usage
17//!
18//! ```rust,ignore
19//! use jiminy_core::abi::{LeU64, LeBool};
20//!
21//! zero_copy_layout! {
22//!     pub struct Vault, discriminator = 1, version = 1 {
23//!         header:    AccountHeader = 16,
24//!         balance:   LeU64         = 8,
25//!         is_frozen: LeBool        = 1,
26//!         authority: Address       = 32,
27//!     }
28//! }
29//!
30//! let vault = Vault::overlay(&data)?;
31//! let bal: u64 = vault.balance.get();
32//! ```
33
34use crate::account::{FixedLayout, Pod};
35
36// ── Macro to stamp out unsigned LE integer wrappers ──────────────────────────
37
38macro_rules! impl_le_unsigned {
39    ($name:ident, $inner:ty, $size:literal) => {
40        #[doc = concat!("Alignment-1, little-endian `", stringify!($inner), "` for on-chain ABI fields.")]
41        #[repr(transparent)]
42        #[derive(Clone, Copy, PartialEq, Eq)]
43        pub struct $name(pub [u8; $size]);
44
45        const _: () = assert!(core::mem::size_of::<$name>() == $size);
46        const _: () = assert!(core::mem::align_of::<$name>() == 1);
47
48        impl $name {
49            /// Zero value.
50            pub const ZERO: Self = Self([0u8; $size]);
51
52            /// Wrap a native value.
53            #[inline(always)]
54            pub const fn new(v: $inner) -> Self {
55                Self(v.to_le_bytes())
56            }
57
58            /// Read the native value.
59            #[inline(always)]
60            pub const fn get(&self) -> $inner {
61                <$inner>::from_le_bytes(self.0)
62            }
63
64            /// Write a native value.
65            #[inline(always)]
66            pub fn set(&mut self, v: $inner) {
67                self.0 = v.to_le_bytes();
68            }
69        }
70
71        impl Default for $name {
72            #[inline(always)]
73            fn default() -> Self {
74                Self::ZERO
75            }
76        }
77
78        impl core::fmt::Debug for $name {
79            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
80                write!(f, "{}({})", stringify!($name), self.get())
81            }
82        }
83
84        impl core::fmt::Display for $name {
85            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
86                write!(f, "{}", self.get())
87            }
88        }
89
90        impl PartialOrd for $name {
91            #[inline(always)]
92            fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
93                Some(self.cmp(other))
94            }
95        }
96
97        impl Ord for $name {
98            #[inline(always)]
99            fn cmp(&self, other: &Self) -> core::cmp::Ordering {
100                self.get().cmp(&other.get())
101            }
102        }
103
104        impl From<$inner> for $name {
105            #[inline(always)]
106            fn from(v: $inner) -> Self {
107                Self::new(v)
108            }
109        }
110
111        impl From<$name> for $inner {
112            #[inline(always)]
113            fn from(v: $name) -> Self {
114                v.get()
115            }
116        }
117
118        // SAFETY: repr(transparent) over [u8; N], all bit patterns valid.
119        unsafe impl Pod for $name {}
120        impl FixedLayout for $name {
121            const SIZE: usize = $size;
122        }
123    };
124}
125
126// ── Macro to stamp out signed LE integer wrappers ────────────────────────────
127
128macro_rules! impl_le_signed {
129    ($name:ident, $inner:ty, $size:literal) => {
130        #[doc = concat!("Alignment-1, little-endian `", stringify!($inner), "` for on-chain ABI fields.")]
131        #[repr(transparent)]
132        #[derive(Clone, Copy, PartialEq, Eq)]
133        pub struct $name(pub [u8; $size]);
134
135        const _: () = assert!(core::mem::size_of::<$name>() == $size);
136        const _: () = assert!(core::mem::align_of::<$name>() == 1);
137
138        impl $name {
139            /// Zero value.
140            pub const ZERO: Self = Self([0u8; $size]);
141
142            /// Wrap a native value.
143            #[inline(always)]
144            pub const fn new(v: $inner) -> Self {
145                Self(v.to_le_bytes())
146            }
147
148            /// Read the native value.
149            #[inline(always)]
150            pub const fn get(&self) -> $inner {
151                <$inner>::from_le_bytes(self.0)
152            }
153
154            /// Write a native value.
155            #[inline(always)]
156            pub fn set(&mut self, v: $inner) {
157                self.0 = v.to_le_bytes();
158            }
159        }
160
161        impl Default for $name {
162            #[inline(always)]
163            fn default() -> Self {
164                Self::ZERO
165            }
166        }
167
168        impl core::fmt::Debug for $name {
169            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
170                write!(f, "{}({})", stringify!($name), self.get())
171            }
172        }
173
174        impl core::fmt::Display for $name {
175            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
176                write!(f, "{}", self.get())
177            }
178        }
179
180        impl PartialOrd for $name {
181            #[inline(always)]
182            fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
183                Some(self.cmp(other))
184            }
185        }
186
187        impl Ord for $name {
188            #[inline(always)]
189            fn cmp(&self, other: &Self) -> core::cmp::Ordering {
190                self.get().cmp(&other.get())
191            }
192        }
193
194        impl From<$inner> for $name {
195            #[inline(always)]
196            fn from(v: $inner) -> Self {
197                Self::new(v)
198            }
199        }
200
201        impl From<$name> for $inner {
202            #[inline(always)]
203            fn from(v: $name) -> Self {
204                v.get()
205            }
206        }
207
208        // SAFETY: repr(transparent) over [u8; N], all bit patterns valid.
209        unsafe impl Pod for $name {}
210        impl FixedLayout for $name {
211            const SIZE: usize = $size;
212        }
213    };
214}
215
216// ── Unsigned types ───────────────────────────────────────────────────────────
217
218impl_le_unsigned!(LeU16, u16, 2);
219impl_le_unsigned!(LeU32, u32, 4);
220impl_le_unsigned!(LeU64, u64, 8);
221impl_le_unsigned!(LeU128, u128, 16);
222
223// ── Signed types ─────────────────────────────────────────────────────────────
224
225impl_le_signed!(LeI16, i16, 2);
226impl_le_signed!(LeI32, i32, 4);
227impl_le_signed!(LeI64, i64, 8);
228impl_le_signed!(LeI128, i128, 16);
229
230// ── Bool ─────────────────────────────────────────────────────────────────────
231
232/// Alignment-1 boolean for on-chain ABI fields.
233///
234/// Stored as a single byte: `0` = false, any non-zero = true.
235#[repr(transparent)]
236#[derive(Clone, Copy, PartialEq, Eq)]
237pub struct LeBool(pub [u8; 1]);
238
239const _: () = assert!(core::mem::size_of::<LeBool>() == 1);
240const _: () = assert!(core::mem::align_of::<LeBool>() == 1);
241
242impl LeBool {
243    /// False value.
244    pub const FALSE: Self = Self([0]);
245    /// True value.
246    pub const TRUE: Self = Self([1]);
247
248    /// Wrap a native bool.
249    #[inline(always)]
250    pub const fn new(v: bool) -> Self {
251        Self([v as u8])
252    }
253
254    /// Read the native bool.
255    #[inline(always)]
256    pub const fn get(&self) -> bool {
257        self.0[0] != 0
258    }
259
260    /// Write a native bool.
261    #[inline(always)]
262    pub fn set(&mut self, v: bool) {
263        self.0[0] = v as u8;
264    }
265}
266
267impl Default for LeBool {
268    #[inline(always)]
269    fn default() -> Self {
270        Self::FALSE
271    }
272}
273
274impl core::fmt::Debug for LeBool {
275    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
276        write!(f, "LeBool({})", self.get())
277    }
278}
279
280impl core::fmt::Display for LeBool {
281    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
282        write!(f, "{}", self.get())
283    }
284}
285
286impl From<bool> for LeBool {
287    #[inline(always)]
288    fn from(v: bool) -> Self {
289        Self::new(v)
290    }
291}
292
293impl From<LeBool> for bool {
294    #[inline(always)]
295    fn from(v: LeBool) -> Self {
296        v.get()
297    }
298}
299
300// SAFETY: repr(transparent) over [u8; 1], all bit patterns valid.
301unsafe impl Pod for LeBool {}
302impl FixedLayout for LeBool {
303    const SIZE: usize = 1;
304}
305
306// ── Field reference wrappers for borrow-splitting ────────────────────────────
307
308/// Immutable typed view over a field-sized byte slice.
309///
310/// Produced by `split_fields` (generated by `zero_copy_layout!`).
311/// Provides typed access without holding a reference to the whole struct.
312pub struct FieldRef<'a> {
313    data: &'a [u8],
314}
315
316impl<'a> FieldRef<'a> {
317    /// Create a field reference from a byte slice.
318    #[inline(always)]
319    pub const fn new(data: &'a [u8]) -> Self {
320        Self { data }
321    }
322
323    /// The byte length of this field.
324    #[inline(always)]
325    pub const fn len(&self) -> usize {
326        self.data.len()
327    }
328
329    /// Whether the field is zero-length.
330    #[inline(always)]
331    pub const fn is_empty(&self) -> bool {
332        self.data.is_empty()
333    }
334
335    /// Read the raw bytes.
336    #[inline(always)]
337    pub const fn as_bytes(&self) -> &[u8] {
338        self.data
339    }
340
341    /// Read as an `Address` (32-byte public key).
342    ///
343    /// Copies the bytes into an owned `Address`.
344    #[inline(always)]
345    pub fn read_address(&self) -> pinocchio::Address {
346        let mut addr = [0u8; 32];
347        addr.copy_from_slice(&self.data[..32]);
348        pinocchio::Address::from(addr)
349    }
350
351    /// Borrow the field bytes as an `&Address` reference.
352    ///
353    /// # Panics
354    ///
355    /// Panics if the field is smaller than 32 bytes.
356    #[inline(always)]
357    pub fn as_address(&self) -> &pinocchio::Address {
358        // SAFETY: Address is repr(transparent) over [u8; 32], alignment 1.
359        // The slice has been bounds-checked to 32 bytes.
360        let ptr = self.data[..32].as_ptr() as *const pinocchio::Address;
361        unsafe { &*ptr }
362    }
363
364    /// Read a `u64` from the field (LE).
365    #[inline(always)]
366    pub fn read_u64(&self) -> u64 {
367        u64::from_le_bytes([
368            self.data[0],
369            self.data[1],
370            self.data[2],
371            self.data[3],
372            self.data[4],
373            self.data[5],
374            self.data[6],
375            self.data[7],
376        ])
377    }
378
379    /// Read a `u32` from the field (LE).
380    #[inline(always)]
381    pub fn read_u32(&self) -> u32 {
382        u32::from_le_bytes([self.data[0], self.data[1], self.data[2], self.data[3]])
383    }
384
385    /// Read a `u16` from the field (LE).
386    #[inline(always)]
387    pub fn read_u16(&self) -> u16 {
388        u16::from_le_bytes([self.data[0], self.data[1]])
389    }
390
391    /// Read a `u8` from the field.
392    #[inline(always)]
393    pub fn read_u8(&self) -> u8 {
394        self.data[0]
395    }
396
397    /// Read a `bool` from the field.
398    #[inline(always)]
399    pub fn read_bool(&self) -> bool {
400        self.data[0] != 0
401    }
402
403    /// Read an `i64` from the field (LE).
404    #[inline(always)]
405    pub fn read_i64(&self) -> i64 {
406        i64::from_le_bytes([
407            self.data[0],
408            self.data[1],
409            self.data[2],
410            self.data[3],
411            self.data[4],
412            self.data[5],
413            self.data[6],
414            self.data[7],
415        ])
416    }
417
418    /// Read an `i32` from the field (LE).
419    #[inline(always)]
420    pub fn read_i32(&self) -> i32 {
421        i32::from_le_bytes([self.data[0], self.data[1], self.data[2], self.data[3]])
422    }
423
424    /// Read an `i16` from the field (LE).
425    #[inline(always)]
426    pub fn read_i16(&self) -> i16 {
427        i16::from_le_bytes([self.data[0], self.data[1]])
428    }
429
430    /// Read a `u128` from the field (LE).
431    #[inline(always)]
432    pub fn read_u128(&self) -> u128 {
433        u128::from_le_bytes([
434            self.data[0],  self.data[1],  self.data[2],  self.data[3],
435            self.data[4],  self.data[5],  self.data[6],  self.data[7],
436            self.data[8],  self.data[9],  self.data[10], self.data[11],
437            self.data[12], self.data[13], self.data[14], self.data[15],
438        ])
439    }
440
441    /// Read an `i128` from the field (LE).
442    #[inline(always)]
443    pub fn read_i128(&self) -> i128 {
444        i128::from_le_bytes([
445            self.data[0],  self.data[1],  self.data[2],  self.data[3],
446            self.data[4],  self.data[5],  self.data[6],  self.data[7],
447            self.data[8],  self.data[9],  self.data[10], self.data[11],
448            self.data[12], self.data[13], self.data[14], self.data[15],
449        ])
450    }
451
452    /// Read an `i8` from the field.
453    #[inline(always)]
454    pub fn read_i8(&self) -> i8 {
455        self.data[0] as i8
456    }
457}
458
459/// Mutable typed view over a field-sized byte slice.
460///
461/// Produced by `split_fields_mut` (generated by `zero_copy_layout!`).
462/// Provides typed mutation without holding `&mut` to the whole struct.
463pub struct FieldMut<'a> {
464    data: &'a mut [u8],
465}
466
467impl<'a> FieldMut<'a> {
468    /// Create a mutable field reference from a byte slice.
469    #[inline(always)]
470    pub fn new(data: &'a mut [u8]) -> Self {
471        Self { data }
472    }
473
474    /// The byte length of this field.
475    #[inline(always)]
476    pub fn len(&self) -> usize {
477        self.data.len()
478    }
479
480    /// Whether the field is zero-length.
481    #[inline(always)]
482    pub fn is_empty(&self) -> bool {
483        self.data.is_empty()
484    }
485
486    /// Read the raw bytes.
487    #[inline(always)]
488    pub fn as_bytes(&self) -> &[u8] {
489        self.data
490    }
491
492    /// Get the raw mutable bytes.
493    #[inline(always)]
494    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
495        self.data
496    }
497
498    /// Read as an `Address` (32-byte public key).
499    #[inline(always)]
500    pub fn read_address(&self) -> pinocchio::Address {
501        let mut addr = [0u8; 32];
502        addr.copy_from_slice(&self.data[..32]);
503        pinocchio::Address::from(addr)
504    }
505
506    /// Borrow the field bytes as an `&Address` reference.
507    #[inline(always)]
508    pub fn as_address(&self) -> &pinocchio::Address {
509        // SAFETY: Address is repr(transparent) over [u8; 32], alignment 1.
510        let ptr = self.data[..32].as_ptr() as *const pinocchio::Address;
511        unsafe { &*ptr }
512    }
513
514    /// Write an `Address` (32-byte public key).
515    #[inline(always)]
516    pub fn write_address(&mut self, addr: &pinocchio::Address) {
517        self.data[..32].copy_from_slice(addr.as_ref());
518    }
519
520    /// Write a `u64` (LE).
521    #[inline(always)]
522    pub fn write_u64(&mut self, v: u64) {
523        self.data[..8].copy_from_slice(&v.to_le_bytes());
524    }
525
526    /// Write a `u32` (LE).
527    #[inline(always)]
528    pub fn write_u32(&mut self, v: u32) {
529        self.data[..4].copy_from_slice(&v.to_le_bytes());
530    }
531
532    /// Write a `u16` (LE).
533    #[inline(always)]
534    pub fn write_u16(&mut self, v: u16) {
535        self.data[..2].copy_from_slice(&v.to_le_bytes());
536    }
537
538    /// Write a `u8`.
539    #[inline(always)]
540    pub fn write_u8(&mut self, v: u8) {
541        self.data[0] = v;
542    }
543
544    /// Write a `bool`.
545    #[inline(always)]
546    pub fn write_bool(&mut self, v: bool) {
547        self.data[0] = v as u8;
548    }
549
550    /// Write an `i64` (LE).
551    #[inline(always)]
552    pub fn write_i64(&mut self, v: i64) {
553        self.data[..8].copy_from_slice(&v.to_le_bytes());
554    }
555
556    /// Write an `i32` (LE).
557    #[inline(always)]
558    pub fn write_i32(&mut self, v: i32) {
559        self.data[..4].copy_from_slice(&v.to_le_bytes());
560    }
561
562    /// Write an `i16` (LE).
563    #[inline(always)]
564    pub fn write_i16(&mut self, v: i16) {
565        self.data[..2].copy_from_slice(&v.to_le_bytes());
566    }
567
568    /// Write an `i8`.
569    #[inline(always)]
570    pub fn write_i8(&mut self, v: i8) {
571        self.data[0] = v as u8;
572    }
573
574    /// Write a `u128` (LE).
575    #[inline(always)]
576    pub fn write_u128(&mut self, v: u128) {
577        self.data[..16].copy_from_slice(&v.to_le_bytes());
578    }
579
580    /// Write an `i128` (LE).
581    #[inline(always)]
582    pub fn write_i128(&mut self, v: i128) {
583        self.data[..16].copy_from_slice(&v.to_le_bytes());
584    }
585
586    /// Read a `u64` (LE).
587    #[inline(always)]
588    pub fn read_u64(&self) -> u64 {
589        u64::from_le_bytes([
590            self.data[0],
591            self.data[1],
592            self.data[2],
593            self.data[3],
594            self.data[4],
595            self.data[5],
596            self.data[6],
597            self.data[7],
598        ])
599    }
600
601    /// Read a `u32` (LE).
602    #[inline(always)]
603    pub fn read_u32(&self) -> u32 {
604        u32::from_le_bytes([self.data[0], self.data[1], self.data[2], self.data[3]])
605    }
606
607    /// Read a `u16` (LE).
608    #[inline(always)]
609    pub fn read_u16(&self) -> u16 {
610        u16::from_le_bytes([self.data[0], self.data[1]])
611    }
612
613    /// Read a `u8`.
614    #[inline(always)]
615    pub fn read_u8(&self) -> u8 {
616        self.data[0]
617    }
618
619    /// Read a `bool`.
620    #[inline(always)]
621    pub fn read_bool(&self) -> bool {
622        self.data[0] != 0
623    }
624
625    /// Read an `i64` (LE).
626    #[inline(always)]
627    pub fn read_i64(&self) -> i64 {
628        i64::from_le_bytes([
629            self.data[0],
630            self.data[1],
631            self.data[2],
632            self.data[3],
633            self.data[4],
634            self.data[5],
635            self.data[6],
636            self.data[7],
637        ])
638    }
639
640    /// Read an `i32` (LE).
641    #[inline(always)]
642    pub fn read_i32(&self) -> i32 {
643        i32::from_le_bytes([self.data[0], self.data[1], self.data[2], self.data[3]])
644    }
645
646    /// Read an `i16` (LE).
647    #[inline(always)]
648    pub fn read_i16(&self) -> i16 {
649        i16::from_le_bytes([self.data[0], self.data[1]])
650    }
651
652    /// Read an `i8`.
653    #[inline(always)]
654    pub fn read_i8(&self) -> i8 {
655        self.data[0] as i8
656    }
657
658    /// Read a `u128` (LE).
659    #[inline(always)]
660    pub fn read_u128(&self) -> u128 {
661        u128::from_le_bytes([
662            self.data[0],  self.data[1],  self.data[2],  self.data[3],
663            self.data[4],  self.data[5],  self.data[6],  self.data[7],
664            self.data[8],  self.data[9],  self.data[10], self.data[11],
665            self.data[12], self.data[13], self.data[14], self.data[15],
666        ])
667    }
668
669    /// Read an `i128` (LE).
670    #[inline(always)]
671    pub fn read_i128(&self) -> i128 {
672        i128::from_le_bytes([
673            self.data[0],  self.data[1],  self.data[2],  self.data[3],
674            self.data[4],  self.data[5],  self.data[6],  self.data[7],
675            self.data[8],  self.data[9],  self.data[10], self.data[11],
676            self.data[12], self.data[13], self.data[14], self.data[15],
677        ])
678    }
679
680    /// Copy from a byte slice (e.g., an Address).
681    #[inline(always)]
682    pub fn copy_from(&mut self, src: &[u8]) {
683        self.data[..src.len()].copy_from_slice(src);
684    }
685}