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