11//! Alignment-1 ("pod") primitives for zero-copy deserialization.
22//!
3- //! # Why alignment-1?
4- //!
53//! Solana account data is a raw `&[u8]` with no alignment guarantees.
64//! Standard Rust primitives like `u64` require 8-byte alignment, so you can't
75//! safely cast `&[u8]` to `&u64` without risking undefined behavior.
86//!
97//! These "pod" (plain old data) types wrap byte arrays and provide safe
108//! get/set methods that handle little-endian conversion. They have alignment 1,
119//! so they can be safely referenced from any byte offset.
12- //!
13- //! # Layout
14- //!
15- //! All types use `#[repr(transparent)]` to ensure the struct has the same
16- //! memory layout as its inner byte array.
17-
18- use {
19- solana_address:: Address ,
20- wincode:: { SchemaRead , SchemaWrite } ,
21- } ;
10+ use wincode:: { SchemaRead , SchemaWrite } ;
2211
2312/// Macro to define an alignment-1 little-endian integer wrapper.
24- ///
25- /// Generates:
26- /// - `#[repr(transparent)]` wrapper over `[u8; N]`
27- /// - `Default` (all zeros)
28- /// - `const fn from_primitive`
29- /// - `get/set/as_bytes`
30- /// - `From<prim> for Pod*` and `From<Pod*> for prim`
3113#[ macro_export]
32- macro_rules! impl_pod_int_le {
14+ macro_rules! impl_pod_int {
3315 (
3416 $( #[ $meta: meta] ) *
3517 $name: ident, $prim: ty, $n: expr
@@ -41,170 +23,57 @@ macro_rules! impl_pod_int_le {
4123 pub struct $name( pub [ u8 ; $n] ) ;
4224
4325 impl $name {
44- /// Creates from a native primitive value, stored as little-endian bytes.
45- #[ inline( always) ]
4626 pub const fn from_primitive( v: $prim) -> Self {
4727 Self ( v. to_le_bytes( ) )
4828 }
4929
50- /// Reads the value as a native primitive.
51- #[ inline( always) ]
5230 pub fn get( self ) -> $prim {
5331 <$prim>:: from_le_bytes( self . 0 )
5432 }
5533
56- /// Writes a native primitive value.
57- #[ inline( always) ]
5834 pub fn set( & mut self , v: $prim) {
5935 self . 0 = v. to_le_bytes( ) ;
6036 }
6137
62- /// Returns the raw little-endian bytes.
63- #[ inline( always) ]
6438 pub fn as_bytes( & self ) -> & [ u8 ; $n] {
6539 & self . 0
6640 }
6741
68- /// Returns the raw little-endian bytes as a mutable slice.
69- #[ inline( always) ]
7042 pub fn as_mut_slice( & mut self ) -> & mut [ u8 ] {
7143 & mut self . 0
7244 }
7345 }
7446
7547 impl From <$prim> for $name {
76- #[ inline( always) ]
7748 fn from( v: $prim) -> Self {
7849 Self :: from_primitive( v)
7950 }
8051 }
8152
8253 impl From <$name> for $prim {
83- #[ inline( always) ]
8454 fn from( v: $name) -> Self {
8555 v. get( )
8656 }
8757 }
8858 } ;
8959}
9060
91- impl_pod_int_le ! (
92- /// A `u32` stored as 4 little-endian bytes with alignment 1.
93- PodU32 , u32 , 4
94- ) ;
95-
96- impl_pod_int_le ! (
97- /// A `u64` stored as 8 little-endian bytes with alignment 1.
98- PodU64 , u64 , 8
99- ) ;
61+ impl_pod_int ! ( PodU32 , u32 , 4 ) ;
62+ impl_pod_int ! ( PodU64 , u64 , 8 ) ;
63+ impl_pod_int ! ( PodI64 , i64 , 8 ) ;
10064
101- impl_pod_int_le ! (
102- /// An `i64` stored as 8 little-endian bytes with alignment 1.
103- PodI64 , i64 , 8
104- ) ;
105-
106- /// An `Address` (pubkey) stored as 32 bytes with alignment 1.
65+ /// An `Address` stored as 32 bytes with alignment 1.
10766#[ repr( transparent) ]
10867#[ derive( Clone , Copy , Debug , PartialEq , Eq , Default , SchemaWrite , SchemaRead ) ]
10968#[ wincode( assert_zero_copy) ]
11069pub struct PodAddress ( pub [ u8 ; 32 ] ) ;
11170
11271impl PodAddress {
113- /// Converts to a native `Address`.
114- #[ inline( always) ]
115- pub fn to_address ( self ) -> Address {
116- Address :: new_from_array ( self . 0 )
117- }
118-
119- /// Returns the raw bytes.
120- #[ inline( always) ]
12172 pub fn as_bytes ( & self ) -> & [ u8 ; 32 ] {
12273 & self . 0
12374 }
12475
125- /// Creates from raw bytes.
126- #[ inline( always) ]
12776 pub const fn from_bytes ( bytes : [ u8 ; 32 ] ) -> Self {
12877 Self ( bytes)
12978 }
130-
131- /// Creates from a native `Address`.
132- #[ inline( always) ]
133- pub fn from_address ( addr : Address ) -> Self {
134- Self ( addr. to_bytes ( ) )
135- }
136- }
137-
138- impl From < Address > for PodAddress {
139- #[ inline( always) ]
140- fn from ( addr : Address ) -> Self {
141- Self :: from_address ( addr)
142- }
143- }
144-
145- impl From < PodAddress > for Address {
146- #[ inline( always) ]
147- fn from ( p : PodAddress ) -> Self {
148- p. to_address ( )
149- }
150- }
151-
152- #[ cfg( test) ]
153- mod tests {
154- use super :: * ;
155-
156- #[ test]
157- fn pod_u32_le_layout ( ) {
158- let p = PodU32 :: from_primitive ( 1 ) ;
159- assert_eq ! ( p. 0 , [ 1 , 0 , 0 , 0 ] ) ;
160- assert_eq ! ( p. get( ) , 1 ) ;
161-
162- let x: u32 = p. into ( ) ;
163- assert_eq ! ( x, 1 ) ;
164-
165- let p2: PodU32 = 42u32 . into ( ) ;
166- assert_eq ! ( p2. get( ) , 42 ) ;
167- }
168-
169- #[ test]
170- fn pod_u64_le_layout ( ) {
171- let p = PodU64 :: from_primitive ( 1 ) ;
172- assert_eq ! ( p. 0 , [ 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ) ;
173- assert_eq ! ( p. get( ) , 1 ) ;
174-
175- let x: u64 = p. into ( ) ;
176- assert_eq ! ( x, 1 ) ;
177-
178- let mut p2: PodU64 = 0u64 . into ( ) ;
179- p2. set ( u64:: MAX ) ;
180- assert_eq ! ( p2. get( ) , u64 :: MAX ) ;
181- }
182-
183- #[ test]
184- fn pod_i64_le_layout ( ) {
185- let p = PodI64 :: from_primitive ( -1 ) ;
186- assert_eq ! ( p. get( ) , -1 ) ;
187-
188- let x: i64 = p. into ( ) ;
189- assert_eq ! ( x, -1 ) ;
190-
191- let mut p2: PodI64 = 0i64 . into ( ) ;
192- p2. set ( i64:: MIN ) ;
193- assert_eq ! ( p2. get( ) , i64 :: MIN ) ;
194- }
195-
196- #[ test]
197- fn pod_address_roundtrip ( ) {
198- let bytes = [ 7u8 ; 32 ] ;
199- let p = PodAddress :: from_bytes ( bytes) ;
200- assert_eq ! ( p. as_bytes( ) , & bytes) ;
201-
202- let addr = p. to_address ( ) ;
203- let p2 = PodAddress :: from_address ( addr) ;
204- assert_eq ! ( p2. as_bytes( ) , & bytes) ;
205-
206- let addr2: Address = p2. into ( ) ;
207- let p3: PodAddress = addr2. into ( ) ;
208- assert_eq ! ( p3. as_bytes( ) , & bytes) ;
209- }
21079}
0 commit comments