4242// ! assert!(first_byte == 0x41);
4343// ! ```
4444
45- use crate :: array :: {ArrayTrait , Span , SpanTrait };
46- use crate :: bytes_31 :: split_bytes31;
45+ use crate :: array :: {ArrayTrait , Span , SpanIter , SpanTrait };
4746#[allow(unused_imports)]
4847use crate :: bytes_31 :: {
4948 BYTES_IN_BYTES31 , Bytes31Trait , POW_2_128 , POW_2_8 , U128IntoBytes31 , U8IntoBytes31 ,
50- one_shift_left_bytes_u128 , split_u128 , u8_at_u256 ,
49+ one_shift_left_bytes_u128 , split_bytes31, split_u128 , u8_at_u256 ,
5150};
5251use crate :: clone :: Clone ;
5352use crate :: cmp :: min;
@@ -706,7 +705,7 @@ pub(crate) impl ByteArrayIndexView of crate::traits::IndexView<ByteArray, usize,
706705 }
707706}
708707
709- // TODO: Implement a more efficient version of this iterator .
708+ // TODO(giladchase): Delegate to byte span iterator instead of current at-based implementation .
710709/// An iterator struct over a ByteArray.
711710#[derive(Drop , Clone )]
712711pub struct ByteArrayIter {
@@ -716,6 +715,7 @@ pub struct ByteArrayIter {
716715
717716impl ByteArrayIterator of crate :: iter :: Iterator <ByteArrayIter > {
718717 type Item = u8 ;
718+
719719 fn next (ref self : ByteArrayIter ) -> Option <u8 > {
720720 self . ba. at (self . current_index. next ()? )
721721 }
@@ -973,6 +973,86 @@ impl ByteSpanToByteSpan of ToByteSpanTrait<ByteSpan> {
973973 }
974974}
975975
976+ /// An iterator struct over a ByteSpan.
977+ #[derive(Drop , Clone )]
978+ pub struct ByteSpanIter {
979+ /// Iterator over the full words.
980+ data_iter : SpanIter <bytes31 >,
981+ /// The word currently being iterated over.
982+ current_word : ShortString ,
983+ /// The last, partial word of the ByteSpan, iterated over after all full words are consumed.
984+ remainder : ShortString ,
985+ }
986+
987+ impl ByteSpanIterator of crate :: iter :: Iterator <ByteSpanIter > {
988+ type Item = u8 ;
989+
990+ fn next (ref self : ByteSpanIter ) -> Option <u8 > {
991+ if let Some (byte ) = self . current_word. pop_first () {
992+ return Some (byte );
993+ }
994+
995+ // Current word exhausted, try loading the next into current word from data or remainder.
996+ match self . data_iter. next () {
997+ Some (word ) => {
998+ self . current_word = ShortString { word : (* word ). into (), word_len : 31 };
999+ },
1000+ // No more words in data, try loading the remainder.
1001+ None => {
1002+ if self . remainder. word_len == 0 { // Remainder is empty.
1003+ return None ;
1004+ }
1005+
1006+ self . current_word = self . remainder;
1007+ self . remainder. word_len = 0 ; // Mark remainder as consumed.
1008+ },
1009+ }
1010+
1011+ self . current_word. pop_first ()
1012+ }
1013+ }
1014+
1015+ impl ByteSpanIntoIterator of crate :: iter :: IntoIterator <ByteSpan > {
1016+ type IntoIter = ByteSpanIter ;
1017+
1018+ /// Creates an iterator over the bytes in the `ByteSpan`.
1019+ fn into_iter (self : ByteSpan ) -> Self :: IntoIter {
1020+ let mut data_iter = self . data. into_iter ();
1021+
1022+ // Get first word in data array if exists, otherwise iterate on the remainder word.
1023+ let Some (first_word ) = data_iter . next () else {
1024+ if self . remainder_len == 0 {
1025+ return ByteSpanIter {
1026+ data_iter , current_word : Default :: default (), remainder : Default :: default (),
1027+ };
1028+ }
1029+
1030+ // If remainder length is nonzero then it's strictly larger than the start offset.
1031+ let word_len = helpers :: length_sub_offset (
1032+ upcast (self . remainder_len), self . first_char_start_offset,
1033+ );
1034+
1035+ return ByteSpanIter {
1036+ data_iter ,
1037+ current_word : ShortString { word : self . remainder_word. into (), word_len : word_len },
1038+ remainder : Default :: default (),
1039+ };
1040+ };
1041+
1042+ let word_len = helpers :: length_sub_offset (
1043+ upcast (BYTES_IN_BYTES31 ), self . first_char_start_offset,
1044+ );
1045+ ByteSpanIter {
1046+ data_iter ,
1047+ current_word : ShortString { word : (* first_word ). into (), word_len : word_len },
1048+ remainder : ShortString {
1049+ word : self . remainder_word. into (), word_len : upcast (self . remainder_len),
1050+ },
1051+ }
1052+ }
1053+ }
1054+
1055+
9761056/// Shifts a word right by `n_bytes`.
9771057/// The input `bytes31` and the output `bytes31`s are represented using `felt252`s to improve
9781058/// performance.
@@ -987,6 +1067,36 @@ fn shift_right(word: felt252, word_len: usize, n_bytes: usize) -> felt252 {
9871067 let (_shifted_out , after_shift_right ) = split_bytes31 (word , word_len , n_bytes );
9881068 after_shift_right
9891069}
1070+ /// Representation of a `felt252` holding a string up to size 31, including length.
1071+ #[derive(Drop , Copy )]
1072+ struct ShortString {
1073+ /// The actual data.
1074+ word : u256 ,
1075+ /// The actual length of the short string in bytes.
1076+ word_len : BoundedInt <0 , 31 >,
1077+ }
1078+
1079+ #[generate_trait]
1080+ impl ShortStringImpl of ShortStringTrait {
1081+ /// Removes and returns the first byte from the string if it exists.
1082+ fn pop_first (ref self : ShortString ) -> Option <u8 > {
1083+ let Some (byte_position ) = helpers :: short_string_byte_count_dec (self . word_len) else {
1084+ return None ;
1085+ };
1086+
1087+ // Strings are indexed by lsb, so the first char is at position (byte_count - 1).
1088+ let byte = u8_at_u256 (self . word, upcast (byte_position ));
1089+
1090+ self . word_len = byte_position ;
1091+ Some (byte )
1092+ }
1093+ }
1094+
1095+ impl ShortStringDefault of Default <ShortString > {
1096+ fn default () -> ShortString {
1097+ ShortString { word : 0 , word_len : 0 }
1098+ }
1099+ }
9901100
9911101mod helpers {
9921102 use core :: num :: traits :: Bounded ;
@@ -1049,6 +1159,11 @@ mod helpers {
10491159 type Result = BoundedInt <- 1 , { BYTES_IN_BYTES31_MINUS_ONE . into() - 1 }>;
10501160 }
10511161
1162+ // For decrementing ShortString count (BoundedInt<0, 31>) by 1
1163+ impl ShortStringCountSub1 of SubHelper <BoundedInt <0 , 31 >, UnitInt <1 >> {
1164+ type Result = BoundedInt <- 1 , { BYTES_IN_BYTES31 . into() - 1 }>;
1165+ }
1166+
10521167 // For byte_at: (BoundedInt<0,30> - 1) - BoundedInt<0,30>
10531168 impl Bytes31IndexMinus1SubBytes31Index of SubHelper <Bytes31IndexSub1 :: Result , Bytes31Index > {
10541169 type Result =
@@ -1110,6 +1225,57 @@ mod helpers {
11101225 }
11111226 }
11121227
1228+ pub impl TrimMinShortStringCount of bounded_int :: TrimMinHelper <BoundedInt <0 , 31 >> {
1229+ type Target = BoundedInt <1 , 31 >;
1230+ }
1231+ impl SubShortStringCount of SubHelper <BoundedInt <1 , 31 >, UnitInt <1 >> {
1232+ type Result = BoundedInt <0 , 30 >;
1233+ }
1234+
1235+ /// Decrements the ShortString byte count by one, or returns `None` if the count is zero.
1236+ pub fn short_string_byte_count_dec (count : BoundedInt <0 , 31 >) -> Option <BoundedInt <0 , 31 >> {
1237+ if let crate :: internal :: OptionRev :: Some (trimmed ) = bounded_int :: trim_min (count ) {
1238+ Some (upcast (bounded_int :: sub (trimmed , 1 )))
1239+ } else {
1240+ None
1241+ }
1242+ }
1243+
1244+ impl Bytes31IndexSubBytes31Index of SubHelper <Bytes31Index , Bytes31Index > {
1245+ type Result = BoundedInt <{ - 30 }, 30 >;
1246+ }
1247+
1248+ impl ConstrainBytes31IndexSubPositive of ConstrainHelper <
1249+ Bytes31IndexSubBytes31Index :: Result , 0 ,
1250+ > {
1251+ type LowT = BoundedInt <{ - 30 }, { - 1 }>;
1252+ type HighT = BoundedInt <0 , 30 >;
1253+ }
1254+
1255+ impl B31SubOffset of SubHelper <BoundedInt <0 , 31 >, Bytes31Index > {
1256+ type Result = BoundedInt <- 30 , 31 >;
1257+ }
1258+
1259+ impl ConstrainB31SubOffsetPos of ConstrainHelper <B31SubOffset :: Result , 0 > {
1260+ type LowT = BoundedInt <- 30 , - 1 >;
1261+ type HighT = BoundedInt <0 , 31 >;
1262+ }
1263+
1264+ /// Subtracts `offset` from `length`, assumes `offset < length`.
1265+ pub fn length_sub_offset (length : BoundedInt <0 , 31 >, offset : Bytes31Index ) -> BoundedInt <0 , 31 > {
1266+ let diff = bounded_int :: sub (length , offset );
1267+ bounded_int :: constrain :: <_ , 0 >(diff ). unwrap_err ()
1268+ }
1269+
1270+ /// Decrements the index by one, or returns `None` if the index is zero.
1271+ pub fn byte31_index_dec (index : Bytes31Index ) -> Option <Bytes31Index > {
1272+ if let crate :: internal :: OptionRev :: Some (trimmed ) = bounded_int :: trim_min (index ) {
1273+ Some (upcast (bounded_int :: sub (trimmed , 1 )))
1274+ } else {
1275+ None
1276+ }
1277+ }
1278+
11131279 /// The information about the new pending word length and the split index.
11141280 pub enum AppendWordInfo {
11151281 /// The new pending word length is less than 31, and fits in the current pending word.
@@ -1129,6 +1295,7 @@ mod helpers {
11291295 type Result = Bytes31Index ;
11301296 }
11311297
1298+
11321299 /// Returns the information about the new pending word length and the split index.
11331300 pub fn append_word_info (
11341301 pending_bytes : Bytes31Index , new_word_bytes : BoundedInt <1 , 31 >,
@@ -1146,11 +1313,9 @@ mod helpers {
11461313 pub impl TrimMinBytes31Index of bounded_int :: TrimMinHelper <Bytes31Index > {
11471314 type Target = BoundedInt <1 , 30 >;
11481315 }
1149-
1150- impl LengthToBytes31Index of SubHelper <BoundedInt <1 , 31 >, UnitInt <1 >> {
1151- type Result = Bytes31Index ;
1316+ impl SubBytes31Index of SubHelper <BoundedInt <1 , 30 >, UnitInt <1 >> {
1317+ type Result = BoundedInt <0 , 29 >;
11521318 }
1153-
11541319 /// Takes the length of an input word and returns the length minus one.
11551320 pub fn length_minus_one (len : BoundedInt <1 , 31 >) -> Bytes31Index {
11561321 bounded_int :: sub (len , 1 )
0 commit comments