solana_serialize_utils/
cursor.rs1use {
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 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}