1use hopper_runtime::error::ProgramError;
40
41pub struct StateSnapshot<const SIZE: usize> {
47 data: [u8; SIZE],
48 len: usize,
49 truncated: bool,
51}
52
53impl<const SIZE: usize> StateSnapshot<SIZE> {
54 #[inline]
59 pub fn capture(data: &[u8]) -> Self {
60 let truncated = data.len() > SIZE;
61 let len = if truncated { SIZE } else { data.len() };
62 let mut snapshot = Self {
63 data: [0u8; SIZE],
64 len,
65 truncated,
66 };
67 let mut i = 0;
68 while i < len {
69 snapshot.data[i] = data[i];
70 i += 1;
71 }
72 snapshot
73 }
74
75 #[inline(always)]
77 pub fn len(&self) -> usize {
78 self.len
79 }
80
81 #[inline(always)]
83 pub fn is_empty(&self) -> bool {
84 self.len == 0
85 }
86
87 #[inline(always)]
89 pub fn was_truncated(&self) -> bool {
90 self.truncated
91 }
92
93 #[inline(always)]
95 pub fn data(&self) -> &[u8] {
96 &self.data[..self.len]
97 }
98
99 #[inline]
103 pub fn diff<'a>(&'a self, current: &'a [u8]) -> StateDiff<'a> {
104 let compare_len = if current.len() < self.len {
105 current.len()
106 } else {
107 self.len
108 };
109
110 StateDiff {
111 old: &self.data[..compare_len],
112 new: ¤t[..compare_len],
113 old_full_len: self.len,
114 new_full_len: current.len(),
115 }
116 }
117
118 #[inline]
120 pub fn has_changes(&self, current: &[u8]) -> bool {
121 if current.len() != self.len {
122 return true;
123 }
124 let mut i = 0;
125 while i < self.len {
126 if self.data[i] != current[i] {
127 return true;
128 }
129 i += 1;
130 }
131 false
132 }
133
134 #[inline]
136 pub fn range_changed(&self, current: &[u8], offset: usize, len: usize) -> bool {
137 let end = offset + len;
138 if end > self.len || end > current.len() {
139 return true; }
141 let mut i = offset;
142 while i < end {
143 if self.data[i] != current[i] {
144 return true;
145 }
146 i += 1;
147 }
148 false
149 }
150
151 #[inline]
155 pub fn restore_into(&self, target: &mut [u8]) -> Result<(), ProgramError> {
156 if target.len() < self.len {
157 return Err(ProgramError::AccountDataTooSmall);
158 }
159 let mut i = 0;
160 while i < self.len {
161 target[i] = self.data[i];
162 i += 1;
163 }
164 Ok(())
165 }
166}
167
168pub struct StateDiff<'a> {
172 old: &'a [u8],
173 new: &'a [u8],
174 old_full_len: usize,
175 new_full_len: usize,
176}
177
178impl<'a> StateDiff<'a> {
179 #[inline]
181 pub fn has_changes(&self) -> bool {
182 if self.old.len() != self.new.len() {
183 return true;
184 }
185 let mut i = 0;
186 while i < self.old.len() {
187 if self.old[i] != self.new[i] {
188 return true;
189 }
190 i += 1;
191 }
192 self.old_full_len != self.new_full_len
193 }
194
195 #[inline(always)]
197 pub fn was_resized(&self) -> bool {
198 self.old_full_len != self.new_full_len
199 }
200
201 #[inline(always)]
203 pub fn old_len(&self) -> usize {
204 self.old_full_len
205 }
206
207 #[inline(always)]
209 pub fn new_len(&self) -> usize {
210 self.new_full_len
211 }
212
213 #[inline]
215 pub fn field_changed(&self, offset: usize, size: usize) -> bool {
216 let end = offset + size;
217 if end > self.old.len() || end > self.new.len() {
218 return true;
219 }
220 let mut i = offset;
221 while i < end {
222 if self.old[i] != self.new[i] {
223 return true;
224 }
225 i += 1;
226 }
227 false
228 }
229
230 #[inline]
232 pub fn changed_byte_count(&self) -> usize {
233 let compare_len = if self.old.len() < self.new.len() {
234 self.old.len()
235 } else {
236 self.new.len()
237 };
238 let mut count = 0;
239 let mut i = 0;
240 while i < compare_len {
241 if self.old[i] != self.new[i] {
242 count += 1;
243 }
244 i += 1;
245 }
246 if self.old_full_len > self.new_full_len {
248 count += self.old_full_len - self.new_full_len;
249 } else {
250 count += self.new_full_len - self.old_full_len;
251 }
252 count
253 }
254
255 #[inline]
259 pub fn changed_regions<const MAX_REGIONS: usize>(&self) -> ChangedRegions<MAX_REGIONS> {
260 let compare_len = if self.old.len() < self.new.len() {
261 self.old.len()
262 } else {
263 self.new.len()
264 };
265
266 let mut regions = ChangedRegions {
267 entries: [ChangedRegion {
268 offset: 0,
269 length: 0,
270 }; MAX_REGIONS],
271 count: 0,
272 };
273
274 let mut i = 0;
275 while i < compare_len && regions.count < MAX_REGIONS {
276 if self.old[i] != self.new[i] {
277 let start = i;
278 while i < compare_len && self.old[i] != self.new[i] {
279 i += 1;
280 }
281 regions.entries[regions.count] = ChangedRegion {
282 offset: start,
283 length: i - start,
284 };
285 regions.count += 1;
286 } else {
287 i += 1;
288 }
289 }
290
291 regions
292 }
293}
294
295#[derive(Clone, Copy)]
299pub struct ChangedRegion {
300 pub offset: usize,
302 pub length: usize,
304}
305
306pub struct ChangedRegions<const N: usize> {
308 entries: [ChangedRegion; N],
309 count: usize,
310}
311
312impl<const N: usize> ChangedRegions<N> {
313 #[inline(always)]
315 pub fn len(&self) -> usize {
316 self.count
317 }
318
319 #[inline(always)]
321 pub fn is_empty(&self) -> bool {
322 self.count == 0
323 }
324
325 #[inline(always)]
327 pub fn get(&self, index: usize) -> Option<&ChangedRegion> {
328 if index < self.count {
329 Some(&self.entries[index])
330 } else {
331 None
332 }
333 }
334
335 #[inline]
337 pub fn iter(&self) -> ChangedRegionIter<'_> {
338 ChangedRegionIter {
339 entries: &self.entries[..self.count],
340 pos: 0,
341 }
342 }
343}
344
345pub struct ChangedRegionIter<'a> {
347 entries: &'a [ChangedRegion],
348 pos: usize,
349}
350
351impl<'a> Iterator for ChangedRegionIter<'a> {
352 type Item = &'a ChangedRegion;
353
354 #[inline]
355 fn next(&mut self) -> Option<Self::Item> {
356 if self.pos >= self.entries.len() {
357 return None;
358 }
359 let item = &self.entries[self.pos];
360 self.pos += 1;
361 Some(item)
362 }
363}
364
365#[inline]
372pub fn field_diff_mask(old: &[u8], new: &[u8], fields: &[(&str, usize, usize)]) -> u64 {
373 let mut mask: u64 = 0;
374 let mut i = 0;
375 while i < fields.len() && i < 64 {
376 let (_, offset, size) = fields[i];
377 let end = offset + size;
378 if end <= old.len() && end <= new.len() {
379 let mut j = offset;
380 while j < end {
381 if old[j] != new[j] {
382 mask |= 1u64 << i;
383 break;
384 }
385 j += 1;
386 }
387 } else {
388 mask |= 1u64 << i; }
390 i += 1;
391 }
392 mask
393}