1use core::marker::PhantomData;
38
39use crate::borrow::{Ref, RefMut};
40use crate::error::ProgramError;
41use crate::segment_lease::SegmentLease;
42
43pub trait TailCodec: Sized {
51 const MAX_ENCODED_LEN: usize;
56
57 fn encode(&self, out: &mut [u8]) -> Result<usize, ProgramError>;
61
62 fn decode(input: &[u8]) -> Result<(Self, usize), ProgramError>;
65}
66
67pub trait TailElement: TailCodec + Copy + Default + PartialEq {}
73
74impl<T> TailElement for T where T: TailCodec + Copy + Default + PartialEq {}
75
76#[derive(Clone, Copy, Eq, PartialEq)]
82pub struct TailBytes<'a> {
83 bytes: &'a [u8],
84}
85
86impl<'a> TailBytes<'a> {
87 #[inline(always)]
89 pub const fn new(bytes: &'a [u8]) -> Self {
90 Self { bytes }
91 }
92
93 #[inline(always)]
95 pub const fn as_bytes(&self) -> &'a [u8] {
96 self.bytes
97 }
98
99 #[inline(always)]
101 pub const fn len(&self) -> usize {
102 self.bytes.len()
103 }
104
105 #[inline(always)]
107 pub const fn is_empty(&self) -> bool {
108 self.bytes.is_empty()
109 }
110}
111
112#[derive(Clone, Copy, Eq, PartialEq)]
118pub struct TailStr<'a> {
119 bytes: &'a [u8],
120}
121
122impl<'a> TailStr<'a> {
123 #[inline(always)]
125 pub const fn new(bytes: &'a [u8]) -> Self {
126 Self { bytes }
127 }
128
129 #[inline(always)]
131 pub const fn from_str(value: &'a str) -> Self {
132 Self {
133 bytes: value.as_bytes(),
134 }
135 }
136
137 #[inline(always)]
139 pub const fn as_bytes(&self) -> &'a [u8] {
140 self.bytes
141 }
142
143 #[inline]
145 pub fn as_str(&self) -> Result<&'a str, ProgramError> {
146 core::str::from_utf8(self.bytes).map_err(|_| ProgramError::InvalidAccountData)
147 }
148
149 #[inline(always)]
151 pub const fn len(&self) -> usize {
152 self.bytes.len()
153 }
154
155 #[inline(always)]
157 pub const fn is_empty(&self) -> bool {
158 self.bytes.is_empty()
159 }
160}
161
162impl TailCodec for u8 {
165 const MAX_ENCODED_LEN: usize = 1;
166 #[inline]
167 fn encode(&self, out: &mut [u8]) -> Result<usize, ProgramError> {
168 if out.is_empty() {
169 return Err(ProgramError::AccountDataTooSmall);
170 }
171 out[0] = *self;
172 Ok(1)
173 }
174 #[inline]
175 fn decode(input: &[u8]) -> Result<(Self, usize), ProgramError> {
176 input
177 .first()
178 .copied()
179 .map(|b| (b, 1))
180 .ok_or(ProgramError::InvalidAccountData)
181 }
182}
183
184macro_rules! tail_codec_int {
185 ( $( $ty:ty : $n:expr ),+ $(,)? ) => {
186 $(
187 impl TailCodec for $ty {
188 const MAX_ENCODED_LEN: usize = $n;
189 #[inline]
190 fn encode(&self, out: &mut [u8]) -> Result<usize, ProgramError> {
191 if out.len() < $n {
192 return Err(ProgramError::AccountDataTooSmall);
193 }
194 out[..$n].copy_from_slice(&self.to_le_bytes());
195 Ok($n)
196 }
197 #[inline]
198 fn decode(input: &[u8]) -> Result<(Self, usize), ProgramError> {
199 if input.len() < $n {
200 return Err(ProgramError::InvalidAccountData);
201 }
202 let mut bytes = [0u8; $n];
203 bytes.copy_from_slice(&input[..$n]);
204 Ok((Self::from_le_bytes(bytes), $n))
205 }
206 }
207 )+
208 };
209}
210
211tail_codec_int! {
212 u16: 2, u32: 4, u64: 8, u128: 16,
213 i16: 2, i32: 4, i64: 8, i128: 16,
214}
215
216impl TailCodec for bool {
218 const MAX_ENCODED_LEN: usize = 1;
219 #[inline]
220 fn encode(&self, out: &mut [u8]) -> Result<usize, ProgramError> {
221 if out.is_empty() {
222 return Err(ProgramError::AccountDataTooSmall);
223 }
224 out[0] = if *self { 1 } else { 0 };
225 Ok(1)
226 }
227 #[inline]
228 fn decode(input: &[u8]) -> Result<(Self, usize), ProgramError> {
229 match input.first().copied() {
230 Some(0) => Ok((false, 1)),
231 Some(1) => Ok((true, 1)),
232 _ => Err(ProgramError::InvalidAccountData),
233 }
234 }
235}
236
237impl<const N: usize> TailCodec for [u8; N] {
239 const MAX_ENCODED_LEN: usize = N;
240 #[inline]
241 fn encode(&self, out: &mut [u8]) -> Result<usize, ProgramError> {
242 if out.len() < N {
243 return Err(ProgramError::AccountDataTooSmall);
244 }
245 out[..N].copy_from_slice(self);
246 Ok(N)
247 }
248 #[inline]
249 fn decode(input: &[u8]) -> Result<(Self, usize), ProgramError> {
250 if input.len() < N {
251 return Err(ProgramError::InvalidAccountData);
252 }
253 let mut out = [0u8; N];
254 out.copy_from_slice(&input[..N]);
255 Ok((out, N))
256 }
257}
258
259impl<T: TailCodec> TailCodec for Option<T> {
261 const MAX_ENCODED_LEN: usize = 1 + T::MAX_ENCODED_LEN;
262 #[inline]
263 fn encode(&self, out: &mut [u8]) -> Result<usize, ProgramError> {
264 if out.is_empty() {
265 return Err(ProgramError::AccountDataTooSmall);
266 }
267 match self {
268 None => {
269 out[0] = 0;
270 Ok(1)
271 }
272 Some(inner) => {
273 out[0] = 1;
274 let written = inner.encode(&mut out[1..])?;
275 Ok(1 + written)
276 }
277 }
278 }
279 #[inline]
280 fn decode(input: &[u8]) -> Result<(Self, usize), ProgramError> {
281 match input.first().copied() {
282 Some(0) => Ok((None, 1)),
283 Some(1) => {
284 let (inner, n) = T::decode(&input[1..])?;
285 Ok((Some(inner), 1 + n))
286 }
287 _ => Err(ProgramError::InvalidAccountData),
288 }
289 }
290}
291
292#[derive(Clone, Copy, Eq, PartialEq)]
300pub struct BoundedString<const N: usize> {
301 len: u16,
302 bytes: [u8; N],
303}
304
305impl<const N: usize> BoundedString<N> {
306 #[inline]
308 pub const fn empty() -> Self {
309 Self {
310 len: 0,
311 bytes: [0u8; N],
312 }
313 }
314
315 #[allow(clippy::should_implement_trait)]
321 #[inline]
322 pub fn from_str(value: &str) -> Result<Self, ProgramError> {
323 Self::from_bytes(value.as_bytes())
324 }
325
326 #[inline]
328 pub fn from_bytes(value: &[u8]) -> Result<Self, ProgramError> {
329 if value.len() > N || value.len() > u16::MAX as usize {
330 return Err(ProgramError::InvalidInstructionData);
331 }
332 let mut out = Self::empty();
333 out.bytes[..value.len()].copy_from_slice(value);
334 out.len = value.len() as u16;
335 Ok(out)
336 }
337
338 #[inline]
340 pub fn set_str(&mut self, value: &str) -> Result<(), ProgramError> {
341 self.set_bytes(value.as_bytes())
342 }
343
344 #[inline]
346 pub fn set_bytes(&mut self, value: &[u8]) -> Result<(), ProgramError> {
347 if value.len() > N || value.len() > u16::MAX as usize {
348 return Err(ProgramError::InvalidInstructionData);
349 }
350 self.bytes = [0u8; N];
351 self.bytes[..value.len()].copy_from_slice(value);
352 self.len = value.len() as u16;
353 Ok(())
354 }
355
356 #[inline]
358 pub fn clear(&mut self) {
359 self.bytes = [0u8; N];
360 self.len = 0;
361 }
362
363 #[inline(always)]
365 pub fn as_bytes(&self) -> &[u8] {
366 &self.bytes[..self.len as usize]
367 }
368
369 #[inline]
371 pub fn as_str(&self) -> Result<&str, ProgramError> {
372 core::str::from_utf8(self.as_bytes()).map_err(|_| ProgramError::InvalidAccountData)
373 }
374
375 #[inline(always)]
377 pub const fn len(&self) -> usize {
378 self.len as usize
379 }
380
381 #[inline(always)]
383 pub const fn capacity(&self) -> usize {
384 N
385 }
386
387 #[inline(always)]
389 pub const fn remaining_capacity(&self) -> usize {
390 N - self.len as usize
391 }
392
393 #[inline(always)]
395 pub const fn is_full(&self) -> bool {
396 self.len as usize == N
397 }
398
399 #[inline(always)]
401 pub const fn is_empty(&self) -> bool {
402 self.len == 0
403 }
404}
405
406impl<const N: usize> Default for BoundedString<N> {
407 #[inline]
408 fn default() -> Self {
409 Self::empty()
410 }
411}
412
413impl<const N: usize> TailCodec for BoundedString<N> {
414 const MAX_ENCODED_LEN: usize = 2 + N;
415
416 #[inline]
417 fn encode(&self, out: &mut [u8]) -> Result<usize, ProgramError> {
418 let len = self.len as usize;
419 if len > N || out.len() < 2 + len {
420 return Err(ProgramError::AccountDataTooSmall);
421 }
422 out[..2].copy_from_slice(&self.len.to_le_bytes());
423 out[2..2 + len].copy_from_slice(&self.bytes[..len]);
424 Ok(2 + len)
425 }
426
427 #[inline]
428 fn decode(input: &[u8]) -> Result<(Self, usize), ProgramError> {
429 if input.len() < 2 {
430 return Err(ProgramError::InvalidAccountData);
431 }
432 let len = u16::from_le_bytes([input[0], input[1]]) as usize;
433 if len > N || input.len() < 2 + len {
434 return Err(ProgramError::InvalidAccountData);
435 }
436 let mut out = Self::empty();
437 out.len = len as u16;
438 out.bytes[..len].copy_from_slice(&input[2..2 + len]);
439 Ok((out, 2 + len))
440 }
441}
442
443#[derive(Clone, Copy, Eq, PartialEq)]
449pub struct BoundedVec<T, const N: usize>
450where
451 T: TailCodec + Copy + Default,
452{
453 len: u16,
454 items: [T; N],
455}
456
457impl<T, const N: usize> BoundedVec<T, N>
458where
459 T: TailCodec + Copy + Default,
460{
461 #[inline]
463 pub fn empty() -> Self {
464 Self {
465 len: 0,
466 items: [T::default(); N],
467 }
468 }
469
470 #[inline]
472 pub fn from_slice(values: &[T]) -> Result<Self, ProgramError> {
473 if values.len() > N || values.len() > u16::MAX as usize {
474 return Err(ProgramError::InvalidInstructionData);
475 }
476 let mut out = Self::empty();
477 out.items[..values.len()].copy_from_slice(values);
478 out.len = values.len() as u16;
479 Ok(out)
480 }
481
482 #[inline]
484 pub fn push(&mut self, item: T) -> Result<(), ProgramError> {
485 let len = self.len as usize;
486 if len >= N || len >= u16::MAX as usize {
487 return Err(ProgramError::AccountDataTooSmall);
488 }
489 self.items[len] = item;
490 self.len += 1;
491 Ok(())
492 }
493
494 #[inline]
496 pub fn pop(&mut self) -> Option<T> {
497 let len = self.len as usize;
498 if len == 0 {
499 return None;
500 }
501 let new_len = len - 1;
502 let item = self.items[new_len];
503 self.items[new_len] = T::default();
504 self.len = new_len as u16;
505 Some(item)
506 }
507
508 #[inline]
510 pub fn clear(&mut self) {
511 let len = self.len as usize;
512 let mut i = 0;
513 while i < len {
514 self.items[i] = T::default();
515 i += 1;
516 }
517 self.len = 0;
518 }
519
520 #[inline(always)]
522 pub fn as_slice(&self) -> &[T] {
523 &self.items[..self.len as usize]
524 }
525
526 #[inline(always)]
528 pub fn as_mut_slice(&mut self) -> &mut [T] {
529 &mut self.items[..self.len as usize]
530 }
531
532 #[inline(always)]
534 pub const fn len(&self) -> usize {
535 self.len as usize
536 }
537
538 #[inline(always)]
540 pub const fn capacity(&self) -> usize {
541 N
542 }
543
544 #[inline(always)]
546 pub const fn remaining_capacity(&self) -> usize {
547 N - self.len as usize
548 }
549
550 #[inline(always)]
552 pub const fn is_full(&self) -> bool {
553 self.len as usize == N
554 }
555
556 #[inline(always)]
558 pub const fn is_empty(&self) -> bool {
559 self.len == 0
560 }
561}
562
563impl<T, const N: usize> BoundedVec<T, N>
564where
565 T: TailCodec + Copy + Default + PartialEq,
566{
567 #[inline]
569 pub fn contains(&self, item: &T) -> bool {
570 self.as_slice().iter().any(|candidate| candidate == item)
571 }
572
573 #[inline]
578 pub fn push_unique(&mut self, item: T) -> Result<bool, ProgramError> {
579 if self.contains(&item) {
580 return Ok(false);
581 }
582 self.push(item)?;
583 Ok(true)
584 }
585
586 #[inline]
590 pub fn remove_first(&mut self, item: &T) -> bool {
591 let len = self.len as usize;
592 let mut found = None;
593 let mut i = 0;
594 while i < len {
595 if &self.items[i] == item {
596 found = Some(i);
597 break;
598 }
599 i += 1;
600 }
601 let Some(index) = found else {
602 return false;
603 };
604 let mut j = index;
605 while j + 1 < len {
606 self.items[j] = self.items[j + 1];
607 j += 1;
608 }
609 self.items[len - 1] = T::default();
610 self.len = (len - 1) as u16;
611 true
612 }
613}
614
615pub type HopperString<const N: usize> = BoundedString<N>;
617
618pub type HopperVec<T, const N: usize> = BoundedVec<T, N>;
620
621impl<T, const N: usize> Default for BoundedVec<T, N>
622where
623 T: TailCodec + Copy + Default,
624{
625 #[inline]
626 fn default() -> Self {
627 Self::empty()
628 }
629}
630
631impl<T, const N: usize> TailCodec for BoundedVec<T, N>
632where
633 T: TailCodec + Copy + Default,
634{
635 const MAX_ENCODED_LEN: usize = 2 + (N * T::MAX_ENCODED_LEN);
636
637 #[inline]
638 fn encode(&self, out: &mut [u8]) -> Result<usize, ProgramError> {
639 let len = self.len as usize;
640 if len > N || out.len() < 2 {
641 return Err(ProgramError::AccountDataTooSmall);
642 }
643 out[..2].copy_from_slice(&self.len.to_le_bytes());
644 let mut cursor = 2;
645 for item in self.as_slice() {
646 let written = item.encode(&mut out[cursor..])?;
647 cursor = cursor
648 .checked_add(written)
649 .ok_or(ProgramError::AccountDataTooSmall)?;
650 }
651 Ok(cursor)
652 }
653
654 #[inline]
655 fn decode(input: &[u8]) -> Result<(Self, usize), ProgramError> {
656 if input.len() < 2 {
657 return Err(ProgramError::InvalidAccountData);
658 }
659 let len = u16::from_le_bytes([input[0], input[1]]) as usize;
660 if len > N {
661 return Err(ProgramError::InvalidAccountData);
662 }
663 let mut out = Self::empty();
664 let mut cursor = 2;
665 let mut i = 0;
666 while i < len {
667 let (item, consumed) = T::decode(&input[cursor..])?;
668 out.items[i] = item;
669 cursor = cursor
670 .checked_add(consumed)
671 .ok_or(ProgramError::InvalidAccountData)?;
672 i += 1;
673 }
674 out.len = len as u16;
675 Ok((out, cursor))
676 }
677}
678
679impl TailCodec for crate::address::Address {
680 const MAX_ENCODED_LEN: usize = 32;
681
682 #[inline]
683 fn encode(&self, out: &mut [u8]) -> Result<usize, ProgramError> {
684 if out.len() < 32 {
685 return Err(ProgramError::AccountDataTooSmall);
686 }
687 out[..32].copy_from_slice(self.as_array());
688 Ok(32)
689 }
690
691 #[inline]
692 fn decode(input: &[u8]) -> Result<(Self, usize), ProgramError> {
693 if input.len() < 32 {
694 return Err(ProgramError::InvalidAccountData);
695 }
696 let mut bytes = [0u8; 32];
697 bytes.copy_from_slice(&input[..32]);
698 Ok((crate::address::Address::new(bytes), 32))
699 }
700}
701
702pub const SEQ_LEN_PREFIX: usize = 4;
726
727pub trait SeqElement: TailElement {
738 const STRIDE: usize;
741}
742
743macro_rules! seq_element_fixed {
744 ( $( $ty:ty ),+ $(,)? ) => {
745 $(
746 impl SeqElement for $ty {
747 const STRIDE: usize = <$ty as TailCodec>::MAX_ENCODED_LEN;
748 }
749 )+
750 };
751}
752
753seq_element_fixed!(u8, u16, u32, u64, u128, i16, i32, i64, i128, bool);
756
757impl SeqElement for crate::address::Address {
759 const STRIDE: usize = 32;
760}
761
762#[inline(always)]
773pub const fn seq_capacity_for<T: SeqElement>(region_len: usize) -> usize {
774 if region_len < SEQ_LEN_PREFIX {
775 return 0;
776 }
777 (region_len - SEQ_LEN_PREFIX) / T::STRIDE
778}
779
780#[inline(always)]
783pub const fn seq_region_bytes_for<T: SeqElement>(n: usize) -> usize {
784 SEQ_LEN_PREFIX + n * T::STRIDE
785}
786
787#[derive(Clone, Copy)]
795pub struct TailSeq<'a, T: SeqElement> {
796 region: &'a [u8],
798 _marker: PhantomData<T>,
799}
800
801impl<'a, T: SeqElement> TailSeq<'a, T> {
802 #[inline]
806 pub fn from_region(region: &'a [u8]) -> Result<Self, ProgramError> {
807 if region.len() < SEQ_LEN_PREFIX {
808 return Err(ProgramError::AccountDataTooSmall);
809 }
810 let this = Self {
811 region,
812 _marker: PhantomData,
813 };
814 if this.len() as usize > this.capacity() {
815 return Err(ProgramError::InvalidAccountData);
816 }
817 Ok(this)
818 }
819
820 #[inline]
822 pub fn len(&self) -> u32 {
823 let mut bytes = [0u8; 4];
824 bytes.copy_from_slice(&self.region[..SEQ_LEN_PREFIX]);
825 u32::from_le_bytes(bytes)
826 }
827
828 #[inline]
830 pub fn is_empty(&self) -> bool {
831 self.len() == 0
832 }
833
834 #[inline]
836 pub fn capacity(&self) -> usize {
837 seq_capacity_for::<T>(self.region.len())
838 }
839
840 #[inline]
842 pub fn get(&self, index: usize) -> Result<T, ProgramError> {
843 if index >= self.len() as usize {
844 return Err(ProgramError::InvalidArgument);
845 }
846 let start = SEQ_LEN_PREFIX + index * T::STRIDE;
847 let end = start + T::STRIDE;
848 let bytes = self
849 .region
850 .get(start..end)
851 .ok_or(ProgramError::InvalidAccountData)?;
852 let (value, _consumed) = T::decode(bytes)?;
853 Ok(value)
854 }
855
856 #[inline]
860 pub fn iter(&self) -> TailSeqIter<'a, T> {
861 TailSeqIter {
862 region: self.region,
863 len: self.len() as usize,
864 pos: 0,
865 _marker: PhantomData,
866 }
867 }
868}
869
870pub struct TailSeqIter<'a, T: SeqElement> {
872 region: &'a [u8],
873 len: usize,
874 pos: usize,
875 _marker: PhantomData<T>,
876}
877
878impl<T: SeqElement> Iterator for TailSeqIter<'_, T> {
879 type Item = Result<T, ProgramError>;
880
881 #[inline]
882 fn next(&mut self) -> Option<Self::Item> {
883 if self.pos >= self.len {
884 return None;
885 }
886 let start = SEQ_LEN_PREFIX + self.pos * T::STRIDE;
887 let end = start + T::STRIDE;
888 self.pos += 1;
889 let item = match self.region.get(start..end) {
890 Some(bytes) => T::decode(bytes).map(|(value, _)| value),
891 None => Err(ProgramError::InvalidAccountData),
892 };
893 Some(item)
894 }
895
896 #[inline]
897 fn size_hint(&self) -> (usize, Option<usize>) {
898 let remaining = self.len - self.pos;
899 (remaining, Some(remaining))
900 }
901}
902
903pub struct TailSeqMut<'a, T: SeqElement> {
914 region: &'a mut [u8],
916 _marker: PhantomData<T>,
917}
918
919impl<'a, T: SeqElement> TailSeqMut<'a, T> {
920 #[inline]
924 pub fn from_region(region: &'a mut [u8]) -> Result<Self, ProgramError> {
925 if region.len() < SEQ_LEN_PREFIX {
926 return Err(ProgramError::AccountDataTooSmall);
927 }
928 let this = Self {
929 region,
930 _marker: PhantomData,
931 };
932 if this.len() as usize > this.capacity() {
933 return Err(ProgramError::InvalidAccountData);
934 }
935 Ok(this)
936 }
937
938 #[inline]
940 pub fn len(&self) -> u32 {
941 let mut bytes = [0u8; 4];
942 bytes.copy_from_slice(&self.region[..SEQ_LEN_PREFIX]);
943 u32::from_le_bytes(bytes)
944 }
945
946 #[inline]
948 pub fn is_empty(&self) -> bool {
949 self.len() == 0
950 }
951
952 #[inline]
954 pub fn capacity(&self) -> usize {
955 seq_capacity_for::<T>(self.region.len())
956 }
957
958 #[inline]
960 pub fn is_full(&self) -> bool {
961 self.len() as usize >= self.capacity()
962 }
963
964 #[inline]
966 pub fn remaining_capacity(&self) -> usize {
967 self.capacity().saturating_sub(self.len() as usize)
968 }
969
970 #[inline]
972 pub fn get(&self, index: usize) -> Result<T, ProgramError> {
973 if index >= self.len() as usize {
974 return Err(ProgramError::InvalidArgument);
975 }
976 let start = SEQ_LEN_PREFIX + index * T::STRIDE;
977 let end = start + T::STRIDE;
978 let bytes = self
979 .region
980 .get(start..end)
981 .ok_or(ProgramError::InvalidAccountData)?;
982 let (value, _consumed) = T::decode(bytes)?;
983 Ok(value)
984 }
985
986 #[inline]
989 pub fn set(&mut self, index: usize, value: T) -> Result<(), ProgramError> {
990 if index >= self.len() as usize {
991 return Err(ProgramError::InvalidArgument);
992 }
993 let start = SEQ_LEN_PREFIX + index * T::STRIDE;
994 let end = start + T::STRIDE;
995 let slot = self
996 .region
997 .get_mut(start..end)
998 .ok_or(ProgramError::AccountDataTooSmall)?;
999 let _ = value.encode(slot)?;
1000 Ok(())
1001 }
1002
1003 #[inline]
1007 pub fn push(&mut self, value: T) -> Result<(), ProgramError> {
1008 let len = self.len() as usize;
1009 if len >= self.capacity() {
1010 return Err(ProgramError::AccountDataTooSmall);
1011 }
1012 let start = SEQ_LEN_PREFIX + len * T::STRIDE;
1013 let end = start + T::STRIDE;
1014 let slot = self
1015 .region
1016 .get_mut(start..end)
1017 .ok_or(ProgramError::AccountDataTooSmall)?;
1018 let _ = value.encode(slot)?;
1019 let new_len = (len as u32) + 1;
1022 self.region[..SEQ_LEN_PREFIX].copy_from_slice(&new_len.to_le_bytes());
1023 Ok(())
1024 }
1025
1026 #[inline]
1029 pub fn swap_remove(&mut self, index: usize) -> Result<T, ProgramError> {
1030 let len = self.len() as usize;
1031 if index >= len {
1032 return Err(ProgramError::InvalidArgument);
1033 }
1034 let removed = self.get(index)?;
1035 let last = len - 1;
1036 let last_start = SEQ_LEN_PREFIX + last * T::STRIDE;
1037 if index != last {
1038 let idx_start = SEQ_LEN_PREFIX + index * T::STRIDE;
1039 self.region
1043 .copy_within(last_start..last_start + T::STRIDE, idx_start);
1044 }
1045 if let Some(tail) = self.region.get_mut(last_start..last_start + T::STRIDE) {
1048 for byte in tail.iter_mut() {
1049 *byte = 0;
1050 }
1051 }
1052 self.region[..SEQ_LEN_PREFIX].copy_from_slice(&(last as u32).to_le_bytes());
1053 Ok(removed)
1054 }
1055
1056 #[inline]
1058 pub fn as_seq(&self) -> TailSeq<'_, T> {
1059 TailSeq {
1060 region: self.region,
1061 _marker: PhantomData,
1062 }
1063 }
1064}
1065
1066pub struct SeqTailRead<'a, T: SeqElement> {
1084 region: Ref<'a, [u8]>,
1085 _lease: SegmentLease<'a>,
1086 _marker: PhantomData<T>,
1087}
1088
1089impl<'a, T: SeqElement> SeqTailRead<'a, T> {
1090 #[doc(hidden)]
1095 #[inline]
1096 pub fn new(region: Ref<'a, [u8]>, lease: SegmentLease<'a>) -> Self {
1097 Self {
1098 region,
1099 _lease: lease,
1100 _marker: PhantomData,
1101 }
1102 }
1103
1104 #[inline]
1106 pub fn seq(&self) -> Result<TailSeq<'_, T>, ProgramError> {
1107 TailSeq::from_region(&self.region)
1108 }
1109}
1110
1111pub struct SeqTailWrite<'a, T: SeqElement> {
1116 region: RefMut<'a, [u8]>,
1117 _lease: SegmentLease<'a>,
1118 _marker: PhantomData<T>,
1119}
1120
1121impl<'a, T: SeqElement> SeqTailWrite<'a, T> {
1122 #[doc(hidden)]
1128 #[inline]
1129 pub fn new(region: RefMut<'a, [u8]>, lease: SegmentLease<'a>) -> Self {
1130 Self {
1131 region,
1132 _lease: lease,
1133 _marker: PhantomData,
1134 }
1135 }
1136
1137 #[inline]
1140 pub fn seq_mut(&mut self) -> Result<TailSeqMut<'_, T>, ProgramError> {
1141 TailSeqMut::from_region(&mut self.region)
1142 }
1143
1144 #[inline]
1146 pub fn seq(&self) -> Result<TailSeq<'_, T>, ProgramError> {
1147 TailSeq::from_region(&self.region)
1148 }
1149}
1150
1151#[inline]
1161pub fn read_tail_len(data: &[u8], body_end: usize) -> Result<u32, ProgramError> {
1162 let end = body_end
1163 .checked_add(4)
1164 .ok_or(ProgramError::AccountDataTooSmall)?;
1165 if data.len() < end {
1166 return Err(ProgramError::AccountDataTooSmall);
1167 }
1168 let mut bytes = [0u8; 4];
1169 bytes.copy_from_slice(&data[body_end..end]);
1170 Ok(u32::from_le_bytes(bytes))
1171}
1172
1173#[inline]
1176pub fn tail_payload(data: &[u8], body_end: usize) -> Result<&[u8], ProgramError> {
1177 let len = read_tail_len(data, body_end)? as usize;
1178 let start = body_end + 4;
1179 let end = start
1180 .checked_add(len)
1181 .ok_or(ProgramError::InvalidAccountData)?;
1182 if data.len() < end {
1183 return Err(ProgramError::InvalidAccountData);
1184 }
1185 Ok(&data[start..end])
1186}
1187
1188#[inline]
1194pub fn tail_capacity(data: &[u8], body_end: usize) -> Result<usize, ProgramError> {
1195 let start = body_end
1196 .checked_add(4)
1197 .ok_or(ProgramError::AccountDataTooSmall)?;
1198 if data.len() < start {
1199 return Err(ProgramError::AccountDataTooSmall);
1200 }
1201 Ok(data.len() - start)
1202}
1203
1204#[inline]
1210pub fn borrow_bounded_str<const N: usize>(input: &[u8]) -> Result<(&str, usize), ProgramError> {
1211 if input.len() < 2 {
1212 return Err(ProgramError::InvalidAccountData);
1213 }
1214 let len = u16::from_le_bytes([input[0], input[1]]) as usize;
1215 if len > N || input.len() < 2 + len {
1216 return Err(ProgramError::InvalidAccountData);
1217 }
1218 let bytes = &input[2..2 + len];
1219 let value = core::str::from_utf8(bytes).map_err(|_| ProgramError::InvalidAccountData)?;
1220 Ok((value, 2 + len))
1221}
1222
1223#[inline]
1230pub fn borrow_address_slice<const N: usize>(
1231 input: &[u8],
1232) -> Result<(&[crate::address::Address], usize), ProgramError> {
1233 if input.len() < 2 {
1234 return Err(ProgramError::InvalidAccountData);
1235 }
1236 let len = u16::from_le_bytes([input[0], input[1]]) as usize;
1237 if len > N {
1238 return Err(ProgramError::InvalidAccountData);
1239 }
1240 let byte_len = len
1241 .checked_mul(32)
1242 .ok_or(ProgramError::InvalidAccountData)?;
1243 let end = 2usize
1244 .checked_add(byte_len)
1245 .ok_or(ProgramError::InvalidAccountData)?;
1246 if input.len() < end {
1247 return Err(ProgramError::InvalidAccountData);
1248 }
1249 let bytes = &input[2..end];
1250 let ptr = bytes.as_ptr() as *const crate::address::Address;
1251 let values = unsafe { core::slice::from_raw_parts(ptr, len) };
1254 Ok((values, end))
1255}
1256
1257#[inline]
1261pub fn read_tail<T: TailCodec>(data: &[u8], body_end: usize) -> Result<T, ProgramError> {
1262 let payload = tail_payload(data, body_end)?;
1263 let (value, consumed) = T::decode(payload)?;
1264 if consumed != payload.len() {
1265 return Err(ProgramError::InvalidAccountData);
1266 }
1267 Ok(value)
1268}
1269
1270#[inline]
1275pub fn write_tail<T: TailCodec>(
1276 data: &mut [u8],
1277 body_end: usize,
1278 tail: &T,
1279) -> Result<usize, ProgramError> {
1280 let prefix_end = body_end
1281 .checked_add(4)
1282 .ok_or(ProgramError::AccountDataTooSmall)?;
1283 if data.len() < prefix_end {
1284 return Err(ProgramError::AccountDataTooSmall);
1285 }
1286 let written = tail.encode(&mut data[prefix_end..])?;
1287 if written > u32::MAX as usize {
1288 return Err(ProgramError::InvalidAccountData);
1289 }
1290 data[body_end..prefix_end].copy_from_slice(&(written as u32).to_le_bytes());
1291 Ok(written)
1292}
1293
1294#[inline]
1300pub fn write_tail_payload(
1301 data: &mut [u8],
1302 body_end: usize,
1303 payload: &[u8],
1304) -> Result<usize, ProgramError> {
1305 let prefix_end = body_end
1306 .checked_add(4)
1307 .ok_or(ProgramError::AccountDataTooSmall)?;
1308 let payload_end = prefix_end
1309 .checked_add(payload.len())
1310 .ok_or(ProgramError::AccountDataTooSmall)?;
1311 if data.len() < payload_end || payload.len() > u32::MAX as usize {
1312 return Err(ProgramError::AccountDataTooSmall);
1313 }
1314 data[prefix_end..payload_end].copy_from_slice(payload);
1315 data[body_end..prefix_end].copy_from_slice(&(payload.len() as u32).to_le_bytes());
1316 Ok(payload.len())
1317}
1318
1319#[cfg(test)]
1320mod tests {
1321 use super::*;
1322
1323 #[test]
1324 fn u32_roundtrip() {
1325 let mut buf = [0u8; 8];
1326 let n = 0xDEAD_BEEFu32.encode(&mut buf).unwrap();
1327 assert_eq!(n, 4);
1328 let (back, consumed) = u32::decode(&buf).unwrap();
1329 assert_eq!(consumed, 4);
1330 assert_eq!(back, 0xDEAD_BEEF);
1331 }
1332
1333 #[test]
1334 fn u64_roundtrip() {
1335 let mut buf = [0u8; 8];
1336 0x0123_4567_89AB_CDEFu64.encode(&mut buf).unwrap();
1337 let (back, _) = u64::decode(&buf).unwrap();
1338 assert_eq!(back, 0x0123_4567_89AB_CDEF);
1339 }
1340
1341 #[test]
1342 fn bool_encode_decode() {
1343 let mut buf = [0u8; 1];
1344 true.encode(&mut buf).unwrap();
1345 assert_eq!(buf[0], 1);
1346 assert_eq!(bool::decode(&buf).unwrap(), (true, 1));
1347 false.encode(&mut buf).unwrap();
1348 assert_eq!(buf[0], 0);
1349 assert_eq!(bool::decode(&buf).unwrap(), (false, 1));
1350 }
1351
1352 #[test]
1353 fn bool_rejects_garbage() {
1354 let buf = [2u8];
1355 assert!(bool::decode(&buf).is_err());
1356 }
1357
1358 #[test]
1359 fn byte_array_roundtrip() {
1360 let src: [u8; 8] = *b"HOPPER!!";
1361 let mut buf = [0u8; 16];
1362 let n = src.encode(&mut buf).unwrap();
1363 assert_eq!(n, 8);
1364 let (back, consumed) = <[u8; 8]>::decode(&buf).unwrap();
1365 assert_eq!(consumed, 8);
1366 assert_eq!(back, src);
1367 }
1368
1369 #[test]
1370 fn option_none_encodes_to_one_byte() {
1371 let mut buf = [0u8; 16];
1372 let n = Option::<u64>::None.encode(&mut buf).unwrap();
1373 assert_eq!(n, 1);
1374 assert_eq!(buf[0], 0);
1375 let (back, c) = <Option<u64>>::decode(&buf).unwrap();
1376 assert_eq!(back, None);
1377 assert_eq!(c, 1);
1378 }
1379
1380 #[test]
1381 fn option_some_includes_inner_payload() {
1382 let mut buf = [0u8; 16];
1383 let n = Option::<u64>::Some(0xAAAA_BBBB_CCCC_DDDD)
1384 .encode(&mut buf)
1385 .unwrap();
1386 assert_eq!(n, 9);
1387 assert_eq!(buf[0], 1);
1388 let (back, c) = <Option<u64>>::decode(&buf).unwrap();
1389 assert_eq!(back, Some(0xAAAA_BBBB_CCCC_DDDD));
1390 assert_eq!(c, 9);
1391 }
1392
1393 #[test]
1394 fn option_rejects_invalid_tag() {
1395 let buf = [7u8, 0, 0, 0, 0, 0, 0, 0, 0];
1396 assert!(<Option<u64>>::decode(&buf).is_err());
1397 }
1398
1399 #[test]
1400 fn tail_length_prefix_roundtrip() {
1401 let mut data = [0u8; 64];
1404 let body_end = 24usize;
1405 let tail_value: u64 = 0x1234_5678_9ABC_DEF0;
1406 let written = write_tail(&mut data, body_end, &tail_value).unwrap();
1407 assert_eq!(written, 8);
1408 let read_len = read_tail_len(&data, body_end).unwrap();
1409 assert_eq!(read_len, 8);
1410 let back: u64 = read_tail::<u64>(&data, body_end).unwrap();
1411 assert_eq!(back, tail_value);
1412 }
1413
1414 #[test]
1415 fn tail_decode_rejects_excess_payload() {
1416 let mut data = [0u8; 32];
1419 let body_end = 16usize;
1423 data[body_end..body_end + 4].copy_from_slice(&8u32.to_le_bytes());
1424 data[body_end + 4..body_end + 8].copy_from_slice(&0x1122_3344u32.to_le_bytes());
1427 data[body_end + 8..body_end + 12].copy_from_slice(&0xFFu32.to_le_bytes());
1428 let result = read_tail::<u32>(&data, body_end);
1430 assert!(result.is_err());
1431 }
1432
1433 #[test]
1434 fn tail_bounds_check_on_short_buffer() {
1435 let data = [0u8; 10];
1436 assert!(read_tail_len(&data, 16).is_err());
1437 assert!(tail_payload(&data, 16).is_err());
1438 }
1439
1440 #[test]
1441 fn max_encoded_len_matches_actual_encode_size() {
1442 let mut buf = [0u8; 32];
1443 assert_eq!(0u32.encode(&mut buf).unwrap(), u32::MAX_ENCODED_LEN);
1444 assert_eq!(0u64.encode(&mut buf).unwrap(), u64::MAX_ENCODED_LEN);
1445 assert_eq!(true.encode(&mut buf).unwrap(), bool::MAX_ENCODED_LEN);
1446 assert_eq!(
1447 [0u8; 7].encode(&mut buf).unwrap(),
1448 <[u8; 7]>::MAX_ENCODED_LEN
1449 );
1450 assert_eq!(Option::<u32>::None.encode(&mut buf).unwrap(), 1);
1451 assert_eq!(
1452 Option::<u32>::Some(0).encode(&mut buf).unwrap(),
1453 <Option<u32>>::MAX_ENCODED_LEN
1454 );
1455 }
1456
1457 #[test]
1458 fn bounded_string_roundtrip() {
1459 let label = BoundedString::<32>::from_str("multisig").unwrap();
1460 let mut buf = [0u8; BoundedString::<32>::MAX_ENCODED_LEN];
1461 let written = label.encode(&mut buf).unwrap();
1462 assert_eq!(written, 10);
1463 let (back, consumed) = BoundedString::<32>::decode(&buf).unwrap();
1464 assert_eq!(consumed, written);
1465 assert_eq!(back.as_str().unwrap(), "multisig");
1466 }
1467
1468 #[test]
1469 fn bounded_string_capacity_helpers() {
1470 let mut label = HopperString::<8>::from_str("ops").unwrap();
1471 assert_eq!(label.remaining_capacity(), 5);
1472 assert!(!label.is_full());
1473 label.set_str("12345678").unwrap();
1474 assert!(label.is_full());
1475 label.clear();
1476 assert!(label.is_empty());
1477 assert_eq!(label.as_bytes(), b"");
1478 }
1479
1480 #[test]
1481 fn bounded_vec_roundtrip() {
1482 let mut vec = BoundedVec::<u64, 4>::empty();
1483 vec.push(7).unwrap();
1484 vec.push(9).unwrap();
1485 let mut buf = [0u8; BoundedVec::<u64, 4>::MAX_ENCODED_LEN];
1486 let written = vec.encode(&mut buf).unwrap();
1487 assert_eq!(written, 18);
1488 let (back, consumed) = BoundedVec::<u64, 4>::decode(&buf).unwrap();
1489 assert_eq!(consumed, written);
1490 assert_eq!(back.as_slice(), &[7, 9]);
1491 }
1492
1493 #[test]
1494 fn bounded_vec_set_helpers_preserve_order() {
1495 let mut vec = HopperVec::<u64, 4>::empty();
1496 assert_eq!(vec.remaining_capacity(), 4);
1497 assert!(vec.push_unique(7).unwrap());
1498 assert!(!vec.push_unique(7).unwrap());
1499 vec.push(9).unwrap();
1500 vec.push(11).unwrap();
1501 assert!(vec.contains(&9));
1502 assert!(vec.remove_first(&9));
1503 assert_eq!(vec.as_slice(), &[7, 11]);
1504 assert_eq!(vec.pop(), Some(11));
1505 assert_eq!(vec.as_slice(), &[7]);
1506 vec.clear();
1507 assert!(vec.is_empty());
1508 }
1509
1510 use crate::address::Address;
1513
1514 #[test]
1515 fn seq_element_stride_matches_encoded_len() {
1516 assert_eq!(<u8 as SeqElement>::STRIDE, 1);
1517 assert_eq!(<u16 as SeqElement>::STRIDE, 2);
1518 assert_eq!(<u32 as SeqElement>::STRIDE, 4);
1519 assert_eq!(<u64 as SeqElement>::STRIDE, 8);
1520 assert_eq!(<bool as SeqElement>::STRIDE, 1);
1521 assert_eq!(<Address as SeqElement>::STRIDE, 32);
1522 }
1523
1524 #[test]
1525 fn seq_capacity_is_region_derived() {
1526 assert_eq!(seq_capacity_for::<u64>(4 + 8 * 3), 3);
1528 assert_eq!(seq_capacity_for::<u64>(4 + 8 * 3 + 5), 3); assert_eq!(seq_capacity_for::<u64>(4), 0);
1530 assert_eq!(seq_capacity_for::<u64>(0), 0); assert_eq!(seq_region_bytes_for::<Address>(10), 4 + 32 * 10);
1532 }
1533
1534 #[test]
1535 fn seq_push_get_iter_roundtrip() {
1536 let mut region = [0u8; 4 + 8 * 4];
1538 let mut seq = TailSeqMut::<u64>::from_region(&mut region).unwrap();
1539 assert_eq!(seq.capacity(), 4);
1540 assert_eq!(seq.len(), 0);
1541 assert!(seq.is_empty());
1542 assert_eq!(seq.remaining_capacity(), 4);
1543
1544 seq.push(10).unwrap();
1545 seq.push(20).unwrap();
1546 seq.push(30).unwrap();
1547 assert_eq!(seq.len(), 3);
1548 assert_eq!(seq.remaining_capacity(), 1);
1549 assert_eq!(seq.get(0).unwrap(), 10);
1550 assert_eq!(seq.get(2).unwrap(), 30);
1551 assert!(seq.get(3).is_err()); seq.set(1, 99).unwrap();
1555 assert_eq!(seq.get(1).unwrap(), 99);
1556 assert!(seq.set(3, 1).is_err()); let read = TailSeq::<u64>::from_region(®ion).unwrap();
1560 assert_eq!(read.len(), 3);
1561 let collected: Result<std::vec::Vec<u64>, _> = read.iter().collect();
1562 assert_eq!(collected.unwrap(), std::vec![10, 99, 30]);
1563 }
1564
1565 #[test]
1566 fn seq_push_at_capacity_is_account_data_too_small() {
1567 let mut region = [0u8; 4 + 8 * 2]; let mut seq = TailSeqMut::<u64>::from_region(&mut region).unwrap();
1569 seq.push(1).unwrap();
1570 seq.push(2).unwrap();
1571 assert!(seq.is_full());
1572 assert_eq!(seq.remaining_capacity(), 0);
1573 assert_eq!(seq.push(3), Err(ProgramError::AccountDataTooSmall));
1574 assert_eq!(seq.len(), 2);
1576 assert_eq!(seq.get(0).unwrap(), 1);
1577 assert_eq!(seq.get(1).unwrap(), 2);
1578 }
1579
1580 #[test]
1581 fn seq_swap_remove_moves_last_into_hole() {
1582 let mut region = [0u8; 4 + 8 * 4];
1583 let mut seq = TailSeqMut::<u64>::from_region(&mut region).unwrap();
1584 for v in [10u64, 20, 30, 40] {
1585 seq.push(v).unwrap();
1586 }
1587 assert_eq!(seq.swap_remove(1).unwrap(), 20);
1589 assert_eq!(seq.len(), 3);
1590 assert_eq!(seq.get(0).unwrap(), 10);
1591 assert_eq!(seq.get(1).unwrap(), 40);
1592 assert_eq!(seq.get(2).unwrap(), 30);
1593 assert_eq!(seq.swap_remove(2).unwrap(), 30);
1595 assert_eq!(seq.len(), 2);
1596 seq.push(50).unwrap();
1598 assert_eq!(seq.get(2).unwrap(), 50);
1599 assert!(seq.swap_remove(9).is_err());
1600 }
1601
1602 #[test]
1603 fn seq_of_addresses_roundtrips() {
1604 let mut region = [0u8; 4 + 32 * 3];
1605 let mut seq = TailSeqMut::<Address>::from_region(&mut region).unwrap();
1606 let a = Address::new([7u8; 32]);
1607 let b = Address::new([9u8; 32]);
1608 seq.push(a).unwrap();
1609 seq.push(b).unwrap();
1610 assert_eq!(seq.get(0).unwrap(), a);
1611 assert_eq!(seq.get(1).unwrap(), b);
1612 assert_eq!(seq.capacity(), 3);
1613 }
1614
1615 #[test]
1616 fn seq_from_region_rejects_corrupt_count_and_tiny_region() {
1617 let mut tiny = [0u8; 2];
1619 assert!(TailSeqMut::<u64>::from_region(&mut tiny).is_err());
1620 let mut region = [0u8; 4 + 8 * 2];
1622 region[..4].copy_from_slice(&9999u32.to_le_bytes());
1623 assert!(TailSeqMut::<u64>::from_region(&mut region).is_err());
1624 assert!(TailSeq::<u64>::from_region(®ion).is_err());
1625 }
1626}
1627
1628#[cfg(kani)]
1629mod kani_proofs {
1630 use super::*;
1631
1632 #[kani::proof]
1633 fn bounded_string_decode_never_exceeds_capacity() {
1634 let len: u16 = kani::any();
1635 let mut input = [0u8; BoundedString::<4>::MAX_ENCODED_LEN];
1636 input[..2].copy_from_slice(&len.to_le_bytes());
1637
1638 let result = BoundedString::<4>::decode(&input);
1639 if len as usize > 4 {
1640 assert!(result.is_err());
1641 } else {
1642 let (decoded, consumed) = result.unwrap();
1643 assert!(decoded.len() <= decoded.capacity());
1644 assert_eq!(consumed, 2 + decoded.len());
1645 }
1646 }
1647
1648 #[kani::proof]
1649 fn bounded_vec_mutators_preserve_capacity() {
1650 let values: [u8; 5] = kani::any();
1651 let mut vec = BoundedVec::<u8, 4>::empty();
1652
1653 let _ = vec.push(values[0]);
1654 let _ = vec.push(values[1]);
1655 let _ = vec.push(values[2]);
1656 let _ = vec.push(values[3]);
1657 let fifth = vec.push(values[4]);
1658
1659 assert!(vec.len() <= vec.capacity());
1660 assert!(fifth.is_err());
1661 let _ = vec.pop();
1662 assert!(vec.len() <= vec.capacity());
1663 vec.clear();
1664 assert_eq!(vec.len(), 0);
1665 }
1666
1667 #[kani::proof]
1668 fn tail_payload_bounds_checks_arbitrary_prefixes() {
1669 let data: [u8; 16] = kani::any();
1670 let body_end: usize = kani::any();
1671 kani::assume(body_end < data.len());
1672
1673 let result = tail_payload(&data, body_end);
1674 if let Ok(payload) = result {
1675 assert!(payload.len() <= data.len());
1676 }
1677 }
1678}