solana_serialize_utils/
cursor.rs

1use {
2    solana_instruction::error::InstructionError,
3    solana_pubkey::{Pubkey, PUBKEY_BYTES},
4    std::{
5        io::{BufRead as _, Cursor, Read},
6        ptr,
7    },
8};
9
10pub fn read_u8<T: AsRef<[u8]>>(cursor: &mut Cursor<T>) -> Result<u8, InstructionError> {
11    let mut buf = [0; 1];
12    cursor
13        .read_exact(&mut buf)
14        .map_err(|_| InstructionError::InvalidAccountData)?;
15
16    Ok(buf[0])
17}
18
19pub fn read_u32<T: AsRef<[u8]>>(cursor: &mut Cursor<T>) -> Result<u32, InstructionError> {
20    let mut buf = [0; 4];
21    cursor
22        .read_exact(&mut buf)
23        .map_err(|_| InstructionError::InvalidAccountData)?;
24
25    Ok(u32::from_le_bytes(buf))
26}
27
28pub fn read_u64<T: AsRef<[u8]>>(cursor: &mut Cursor<T>) -> Result<u64, InstructionError> {
29    let mut buf = [0; 8];
30    cursor
31        .read_exact(&mut buf)
32        .map_err(|_| InstructionError::InvalidAccountData)?;
33
34    Ok(u64::from_le_bytes(buf))
35}
36
37pub fn read_option_u64<T: AsRef<[u8]>>(
38    cursor: &mut Cursor<T>,
39) -> Result<Option<u64>, InstructionError> {
40    let variant = read_u8(cursor)?;
41    match variant {
42        0 => Ok(None),
43        1 => read_u64(cursor).map(Some),
44        _ => Err(InstructionError::InvalidAccountData),
45    }
46}
47
48pub fn read_i64<T: AsRef<[u8]>>(cursor: &mut Cursor<T>) -> Result<i64, InstructionError> {
49    let mut buf = [0; 8];
50    cursor
51        .read_exact(&mut buf)
52        .map_err(|_| InstructionError::InvalidAccountData)?;
53
54    Ok(i64::from_le_bytes(buf))
55}
56
57pub fn read_pubkey_into(
58    cursor: &mut Cursor<&[u8]>,
59    pubkey: *mut Pubkey,
60) -> Result<(), InstructionError> {
61    match cursor.fill_buf() {
62        Ok(buf) if buf.len() >= PUBKEY_BYTES => {
63            // Safety: `buf` is guaranteed to be at least `PUBKEY_BYTES` bytes
64            // long. Pubkey a #[repr(transparent)] wrapper around a byte array,
65            // so this is a byte to byte copy and it's safe.
66            unsafe {
67                ptr::copy_nonoverlapping(buf.as_ptr(), pubkey as *mut u8, PUBKEY_BYTES);
68            }
69
70            cursor.consume(PUBKEY_BYTES);
71        }
72        _ => return Err(InstructionError::InvalidAccountData),
73    }
74
75    Ok(())
76}
77
78pub fn read_pubkey<T: AsRef<[u8]>>(cursor: &mut Cursor<T>) -> Result<Pubkey, InstructionError> {
79    let mut buf = [0; 32];
80    cursor
81        .read_exact(&mut buf)
82        .map_err(|_| InstructionError::InvalidAccountData)?;
83
84    Ok(Pubkey::from(buf))
85}
86
87pub fn read_bool<T: AsRef<[u8]>>(cursor: &mut Cursor<T>) -> Result<bool, InstructionError> {
88    let byte = read_u8(cursor)?;
89    match byte {
90        0 => Ok(false),
91        1 => Ok(true),
92        _ => Err(InstructionError::InvalidAccountData),
93    }
94}
95
96#[cfg(test)]
97mod test {
98    use {super::*, rand::Rng, std::fmt::Debug};
99
100    #[test]
101    fn test_read_u8() {
102        for _ in 0..100 {
103            let test_value = rand::random::<u8>();
104            test_read(read_u8, test_value);
105        }
106    }
107
108    #[test]
109    fn test_read_u32() {
110        for _ in 0..100 {
111            let test_value = rand::random::<u32>();
112            test_read(read_u32, test_value);
113        }
114    }
115
116    #[test]
117    fn test_read_u64() {
118        for _ in 0..100 {
119            let test_value = rand::random::<u64>();
120            test_read(read_u64, test_value);
121        }
122    }
123
124    #[test]
125    fn test_read_option_u64() {
126        for _ in 0..100 {
127            let test_value = rand::random::<Option<u64>>();
128            test_read(read_option_u64, test_value);
129        }
130    }
131
132    #[test]
133    fn test_read_i64() {
134        for _ in 0..100 {
135            let test_value = rand::random::<i64>();
136            test_read(read_i64, test_value);
137        }
138    }
139
140    #[test]
141    fn test_read_pubkey() {
142        for _ in 0..100 {
143            let mut buf = [0; 32];
144            rand::thread_rng().fill(&mut buf);
145            let test_value = Pubkey::from(buf);
146            test_read(read_pubkey, test_value);
147        }
148    }
149
150    #[test]
151    fn test_read_bool() {
152        test_read(read_bool, false);
153        test_read(read_bool, true);
154    }
155
156    fn test_read<T: Debug + PartialEq + serde::Serialize + borsh::BorshSerialize>(
157        reader: fn(&mut Cursor<Vec<u8>>) -> Result<T, InstructionError>,
158        test_value: T,
159    ) {
160        let bincode_bytes = bincode::serialize(&test_value).unwrap();
161        let mut cursor = Cursor::new(bincode_bytes);
162        let bincode_read = reader(&mut cursor).unwrap();
163
164        let borsh_bytes = borsh::to_vec(&test_value).unwrap();
165        let mut cursor = Cursor::new(borsh_bytes);
166        let borsh_read = reader(&mut cursor).unwrap();
167
168        assert_eq!(test_value, bincode_read);
169        assert_eq!(test_value, borsh_read);
170    }
171}