@@ -839,8 +839,7 @@ pub impl ByteSpanImpl of ByteSpanTrait {
839839 }
840840
841841 /// Gets the element(s) at the given index.
842- /// Accepts ranges (returns Option<ByteSpan>), and (to-be-implemented) single indices (returns
843- /// Option<u8>).
842+ /// Accepts ranges (returns Option<ByteSpan>), and single indices (returns Option<u8>).
844843 #[feature(" corelib-get-trait" )]
845844 fn get <I , impl TGet : crate :: ops :: Get <ByteSpan , I >, + Drop <I >>(
846845 self : @ ByteSpan , index : I ,
@@ -909,6 +908,16 @@ impl ByteSpanGetRangeInclusive of crate::ops::Get<ByteSpan, crate::ops::RangeInc
909908 }
910909}
911910
911+ impl ByteSpanGetUsize of crate :: ops :: Get <ByteSpan , usize > {
912+ type Output = u8 ;
913+
914+ /// Returns the byte at the given index.
915+ /// If out of bounds: returns `None`.
916+ fn get (self : @ ByteSpan , index : usize ) -> Option <u8 > {
917+ helpers :: byte_at (self , index )
918+ }
919+ }
920+
912921#[feature(" byte-span" )]
913922impl ByteSpanIndexViewRange of crate :: ops :: IndexView <ByteSpan , crate :: ops :: Range <usize >> {
914923 type Target = ByteSpan ;
@@ -929,6 +938,14 @@ impl ByteSpanIndexViewRangeInclusive of crate::ops::IndexView<
929938 }
930939}
931940
941+ impl ByteSpanIndex of core :: ops :: index :: IndexView <ByteSpan , usize > {
942+ type Target = u8 ;
943+
944+ fn index (self : @ ByteSpan , index : usize ) -> u8 {
945+ ByteSpanTrait :: get (self , index ). expect (' Index out of bounds' )
946+ }
947+ }
948+
932949/// Trait for types that can be converted into a `ByteSpan`.
933950#[unstable(feature: " byte-span" )]
934951pub trait ToByteSpanTrait <C > {
@@ -973,18 +990,21 @@ fn shift_right(word: felt252, word_len: usize, n_bytes: usize) -> felt252 {
973990
974991mod helpers {
975992 use core :: num :: traits :: Bounded ;
976- use crate :: bytes_31 :: BYTES_IN_BYTES31 ;
993+ use crate :: bytes_31 :: { BYTES_IN_BYTES31 , Bytes31Trait , u8_at_ u256 } ;
977994 #[feature(" bounded-int-utils" )]
978995 use crate :: internal :: bounded_int :: {
979- self, AddHelper , BoundedInt , ConstrainHelper , MulHelper , SubHelper , UnitInt , downcast ,
980- upcast,
996+ self, AddHelper , BoundedInt , ConstrainHelper , DivRemHelper , MulHelper , SubHelper , UnitInt ,
997+ downcast, upcast,
981998 };
982999 use super :: {BYTES_IN_BYTES31_MINUS_ONE , ByteSpan , Bytes31Index };
9831000
9841001 type BytesInBytes31Typed = UnitInt <{ BYTES_IN_BYTES31 . into() }>;
9851002
9861003 const U32_MAX_TIMES_B31 : felt252 = Bounded :: <u32 >:: MAX . into () * BYTES_IN_BYTES31 . into ();
9871004 const BYTES_IN_BYTES31_UNIT_INT : BytesInBytes31Typed = downcast (BYTES_IN_BYTES31 ). unwrap ();
1005+ const NZ_BYTES_IN_BYTES31 : NonZero <BytesInBytes31Typed > = 31 ;
1006+ const BYTES_IN_BYTES31_MINUS_ONE_TYPED : UnitInt <{ BYTES_IN_BYTES31_MINUS_ONE . into() }> = 30 ;
1007+ const ONE_TYPED : UnitInt <1 > = 1 ;
9881008
9891009 impl U32ByB31 of MulHelper <u32 , BytesInBytes31Typed > {
9901010 type Result = BoundedInt <0 , U32_MAX_TIMES_B31 >;
@@ -1002,6 +1022,50 @@ mod helpers {
10021022 >;
10031023 }
10041024
1025+ // For byte_at: usize + BoundedInt<0,30>
1026+ impl UsizeAddBytes31Index of AddHelper <usize , Bytes31Index > {
1027+ type Result =
1028+ BoundedInt <0 , { Bounded :: <usize >:: MAX . into () + BYTES_IN_BYTES31_MINUS_ONE . into () }>;
1029+ }
1030+
1031+ // For byte_at: div_rem of (usize + BoundedInt<0,30>) by 31
1032+ const USIZE_PLUS_30_DIV_31 : felt252 = (Bounded :: <usize >:: MAX / 31 + 1 ). into ();
1033+ impl UsizePlusBytes31IndexDivRemB31 of DivRemHelper <
1034+ UsizeAddBytes31Index :: Result , BytesInBytes31Typed ,
1035+ > {
1036+ type DivT = BoundedInt <0 , USIZE_PLUS_30_DIV_31 >;
1037+ type RemT = Bytes31Index ;
1038+ }
1039+
1040+ // For byte_at: 30 - BoundedInt<0,30>
1041+ impl B30SubBytes31Index of SubHelper <
1042+ UnitInt <{ BYTES_IN_BYTES31_MINUS_ONE . into() }>, Bytes31Index ,
1043+ > {
1044+ type Result = Bytes31Index ;
1045+ }
1046+
1047+ // For byte_at: BoundedInt<0,30> - 1
1048+ impl Bytes31IndexSub1 of SubHelper <Bytes31Index , UnitInt <1 >> {
1049+ type Result = BoundedInt <- 1 , { BYTES_IN_BYTES31_MINUS_ONE . into() - 1 }>;
1050+ }
1051+
1052+ // For byte_at: (BoundedInt<0,30> - 1) - BoundedInt<0,30>
1053+ impl Bytes31IndexMinus1SubBytes31Index of SubHelper <Bytes31IndexSub1 :: Result , Bytes31Index > {
1054+ type Result =
1055+ BoundedInt <
1056+ { - BYTES_IN_BYTES31_MINUS_ONE . into() - 1 },
1057+ { BYTES_IN_BYTES31_MINUS_ONE . into() - 1 },
1058+ >;
1059+ }
1060+
1061+ // For byte_at: split BoundedInt<-31, 29> at 0.
1062+ impl ConstrainRemainderIndexAt0 of bounded_int :: ConstrainHelper <
1063+ Bytes31IndexMinus1SubBytes31Index :: Result , 0 ,
1064+ > {
1065+ type LowT = BoundedInt <{ - BYTES_IN_BYTES31_MINUS_ONE . into() - 1 }, - 1 >;
1066+ type HighT = BoundedInt <0 , { BYTES_IN_BYTES31_MINUS_ONE . into() - 1 }>;
1067+ }
1068+
10051069 /// Calculates the length of a `ByteSpan` in bytes.
10061070 pub fn calc_bytespan_len (span : ByteSpan ) -> usize {
10071071 let data_bytes = bounded_int :: mul (span . data. len (), BYTES_IN_BYTES31_UNIT_INT );
@@ -1091,5 +1155,39 @@ mod helpers {
10911155 pub fn length_minus_one (len : BoundedInt <1 , 31 >) -> Bytes31Index {
10921156 bounded_int :: sub (len , 1 )
10931157 }
1158+ /// Returns the byte at the given index in the ByteSpan.
1159+ /// If out of bounds: returns `None`.
1160+ pub fn byte_at (self : @ ByteSpan , index : usize ) -> Option <u8 > {
1161+ let absolute_index = bounded_int :: add (index , * self . first_char_start_offset);
1162+ let (word_index_bounded , msb_index ) = bounded_int :: div_rem (
1163+ absolute_index , NZ_BYTES_IN_BYTES31 ,
1164+ );
1165+
1166+ let word_index = upcast (word_index_bounded );
1167+ match self . data. get (word_index ) {
1168+ Some (word ) => {
1169+ // Convert from MSB to LSB indexing.
1170+ let lsb_index = bounded_int :: sub (BYTES_IN_BYTES31_MINUS_ONE_TYPED , msb_index );
1171+ Some (word . at (upcast (lsb_index )))
1172+ },
1173+ None => {
1174+ // Word index must equal data.len() for remainder word.
1175+ if word_index != self . data. len () {
1176+ return None ;
1177+ }
1178+
1179+ // Compute LSB index: remainder_len - 1 - msb_index.
1180+ let lsb_index_bounded = bounded_int :: sub (
1181+ bounded_int :: sub (* self . remainder_len, ONE_TYPED ), msb_index ,
1182+ );
1183+
1184+ // Check if in bounds and extract non-negative index.
1185+ let Err (lsb_index ) = bounded_int :: constrain :: <_ , 0 >(lsb_index_bounded ) else {
1186+ return None ; // Out of bounds: index >= remainder_len.
1187+ };
1188+ Some (u8_at_u256 ((* self . remainder_word). into (), upcast (lsb_index )))
1189+ },
1190+ }
1191+ }
10941192}
10951193pub (crate ) use helpers :: len_parts;
0 commit comments