bitoku_sdk_agent_native/
state.rs1use borsh::{BorshDeserialize, BorshSerialize};
2use solana_program::{
3 msg,
4 program_error::ProgramError,
5 program_pack::{Pack, Sealed},
6 pubkey::Pubkey,
7};
8
9use crate::instruction::{unpack_request, Request};
10
11#[repr(C)]
12#[derive(Clone, BorshSerialize, BorshDeserialize, Debug)]
13pub struct BookKeeper {
14 pub status: [u8; 32],
15 pub next_id: u8,
16}
17
18#[repr(C)]
19#[derive(Clone, BorshSerialize, BorshDeserialize, Debug)]
20pub struct RequestData {
21 pub client_id: u8,
22 pub requester: Pubkey,
23 pub request: Request,
24}
25
26impl Sealed for BookKeeper {}
27
28impl Pack for BookKeeper {
29 const LEN: usize = 33;
30
31 fn unpack_from_slice(src: &[u8]) -> Result<Self, ProgramError> {
32 if src.len() < 33 {
33 return Err(ProgramError::InvalidAccountData);
34 }
35
36 let status: [u8; 32] = src[..32].try_into().unwrap();
37 let next_id = u8::from_le_bytes(src[32..33].try_into().unwrap())
38 .try_into()
39 .unwrap();
40
41 Ok(Self { status, next_id })
42 }
43
44 fn pack_into_slice(&self, dst: &mut [u8]) {
45 let status = self.status;
46 let next_id = self.next_id.to_le_bytes();
47
48 for i in 0..32 {
49 dst[i] = status[i];
50 }
51
52 for i in 32..33 {
53 dst[i] = next_id[0];
54 }
55 }
56}
57
58impl Sealed for RequestData {}
59
60impl Pack for RequestData {
61 const LEN: usize = 163;
62
63 fn unpack_from_slice(src: &[u8]) -> Result<Self, ProgramError> {
64 if src.len() < 163 {
65 return Err(ProgramError::InvalidAccountData);
66 }
67
68 let client_id = u8::from_le_bytes(src[..1].try_into().unwrap())
69 .try_into()
70 .unwrap();
71 let requester = Pubkey::new(&src[1..33]);
72 let request_bytes = &src[33..];
73
74 let request = unpack_request(request_bytes)?;
75
76 Ok(Self {
77 client_id,
78 requester,
79 request,
80 })
81 }
82
83 fn pack_into_slice(&self, dst: &mut [u8]) {
84 let client_id = self.client_id.to_le_bytes();
85 let requester = self.requester.to_bytes();
86
87 dst[0] = client_id[0];
88
89 for i in 1..33 {
90 dst[i] = requester[i - 1]
91 }
92
93 match &self.request {
94 Request::CreateBucket { name } => {
95 dst[33] = 0;
96 for i in 34..162 {
97 dst[i] = name[i - 34];
98 }
99 }
100 Request::CreateFile { name } => {
101 dst[33] = 1;
102 for i in 34..162 {
103 dst[i] = name[i - 34];
104 }
105 }
106 Request::WriteFile { name, file_id } => {
107 dst[33] = 2;
108 for i in 34..162 {
109 dst[i] = name[i - 34];
110 }
111 dst[162] = *file_id;
112 }
113 Request::CloseFile { name, file_id } => {
114 dst[33] = 3;
115 for i in 34..162 {
116 dst[i] = name[i - 34];
117 }
118 dst[162] = *file_id;
119 }
120 Request::DeleteFile { name, file_id } => {
121 dst[33] = 4;
122 for i in 34..162 {
123 dst[i] = name[i - 34];
124 }
125 dst[162] = *file_id;
126 }
127 Request::SetPosition { name, file_id } => {
128 dst[33] = 5;
129 for i in 34..162 {
130 dst[i] = name[i - 34];
131 }
132 dst[162] = *file_id;
133 }
134 Request::OpenFile { name, file_id } => {
135 dst[33] = 6;
136 for i in 34..162 {
137 dst[i] = name[i - 34];
138 }
139 dst[162] = *file_id;
140 }
141 Request::ReadFile { name, file_id } => {
142 dst[33] = 7;
143 for i in 34..162 {
144 dst[i] = name[i - 34];
145 }
146 dst[162] = *file_id;
147 }
148 }
149 }
150}
151
152pub fn addel(src: &mut [u8; 32], element: u8) {
153 let byte_index = element / 8;
154 let bit_offset = element % 8;
155 src[byte_index as usize] |= 1 << bit_offset;
156}
157
158pub fn isel(src: [u8; 32], element: u8) -> bool {
159 let byte_index = element / 8;
160 let bit_offset = element % 8;
161 let value = (src[byte_index as usize] >> bit_offset) & 1;
162
163 if value == 1 {
164 return true;
165 }
166 false
167}
168
169pub fn delel(src: &mut [u8; 32], element: u8) {
170 let byte_index = element / 8;
171 let bit_offset = element % 8;
172 src[byte_index as usize] &= !(1 << bit_offset);
173}
174
175pub fn validate_name(name: &[u8]) -> bool {
176 if name.len() > 128 as usize {
177 return false;
178 }
179
180 let non_zero_bytes: Vec<u8> = name.iter().take_while(|&b| *b != 0).copied().collect();
181
182 for b in non_zero_bytes {
183 if !(b >= b'a' && b <= b'z')
184 && !(b >= b'A' && b <= b'Z')
185 && !(b >= b'0' && b <= b'9')
186 && !(b == b'.' || b == b'/' || b == b'_' || b == b'+' || b == b'-')
187 {
188 return false;
189 }
190 }
191
192 true
193}
194
195#[cfg(test)]
196mod test {
197
198 use super::*;
199 use super::{Request, RequestData};
200 #[test]
201 fn test_pack() {
202 let mut name: [u8; 128] = [0; 128];
203 let bytes = "test".as_bytes();
204 name[..bytes.len()].copy_from_slice(bytes);
205
206 let requester = Pubkey::new_unique();
207 print!("name {:?}", String::from_utf8(name.to_vec()).unwrap());
208 let src = RequestData {
209 client_id: 85,
210 requester: requester,
211 request: Request::CloseFile { name, file_id: 69 },
212 };
213 let mut dst = [0u8; 163];
214 println!("{:?}", src);
215
216 let res = RequestData::pack(src, &mut dst);
217 print!("packed {:?}", res.unwrap());
218 }
219 #[test]
220 fn test_name_validation() {
221 let name = "test".as_bytes();
222
223 let bool = validate_name(&name);
224
225 assert!(bool);
226 }
227}