@@ -840,8 +840,7 @@ pub impl ByteSpanImpl of ByteSpanTrait {
840840 }
841841
842842 /// Gets the element(s) at the given index.
843- /// Accepts ranges (returns Option<ByteSpan>), and (to-be-implemented) single indices (returns
844- /// Option<u8>).
843+ /// Accepts ranges (returns Option<ByteSpan>), and single indices (returns Option<u8>).
845844 #[feature(" corelib-get-trait" )]
846845 fn get <I , impl TGet : crate :: ops :: Get <ByteSpan , I >, + Drop <I >>(
847846 self : @ ByteSpan , index : I ,
@@ -911,6 +910,24 @@ impl ByteSpanGetRangeInclusive of crate::ops::Get<ByteSpan, crate::ops::RangeInc
911910 }
912911}
913912
913+ impl ByteSpanGetUsize of crate :: ops :: Get <ByteSpan , usize > {
914+ type Output = u8 ;
915+
916+ /// Returns the byte at the given index.
917+ /// If out of bounds: returns `None`.
918+ fn get (self : @ ByteSpan , index : usize ) -> Option <u8 > {
919+ helpers :: byte_at (self , index )
920+ }
921+ }
922+
923+ impl ByteSpanIndex of core :: ops :: index :: IndexView <ByteSpan , usize > {
924+ type Target = u8 ;
925+
926+ fn index (self : @ ByteSpan , index : usize ) -> u8 {
927+ ByteSpanTrait :: get (self , index ). expect (' Index out of bounds' )
928+ }
929+ }
930+
914931/// Trait for types that can be converted into a `ByteSpan`.
915932#[unstable(feature: " byte-span" )]
916933pub trait ToByteSpanTrait <C > {
@@ -955,18 +972,21 @@ fn shift_right(word: felt252, word_len: usize, n_bytes: usize) -> felt252 {
955972
956973mod helpers {
957974 use core :: num :: traits :: Bounded ;
958- use crate :: bytes_31 :: BYTES_IN_BYTES31 ;
975+ use crate :: bytes_31 :: { BYTES_IN_BYTES31 , Bytes31Trait , u8_at_ u256 } ;
959976 #[feature(" bounded-int-utils" )]
960977 use crate :: internal :: bounded_int :: {
961- self, AddHelper , BoundedInt , ConstrainHelper , MulHelper , SubHelper , UnitInt , downcast ,
962- upcast,
978+ self, AddHelper , BoundedInt , ConstrainHelper , DivRemHelper , MulHelper , SubHelper , UnitInt ,
979+ downcast, upcast,
963980 };
964981 use super :: {BYTES_IN_BYTES31_MINUS_ONE , ByteSpan , Bytes31Index };
965982
966983 type BytesInBytes31Typed = UnitInt <{ BYTES_IN_BYTES31 . into() }>;
967984
968985 const U32_MAX_TIMES_B31 : felt252 = Bounded :: <u32 >:: MAX . into () * BYTES_IN_BYTES31 . into ();
969986 const BYTES_IN_BYTES31_UNIT_INT : BytesInBytes31Typed = downcast (BYTES_IN_BYTES31 ). unwrap ();
987+ const NZ_BYTES_IN_BYTES31 : NonZero <BytesInBytes31Typed > = 31 ;
988+ const BYTES_IN_BYTES31_MINUS_ONE_TYPED : UnitInt <{ BYTES_IN_BYTES31_MINUS_ONE . into() }> = 30 ;
989+ const ONE_TYPED : UnitInt <1 > = 1 ;
970990
971991 impl U32ByB31 of MulHelper <u32 , BytesInBytes31Typed > {
972992 type Result = BoundedInt <0 , U32_MAX_TIMES_B31 >;
@@ -984,6 +1004,50 @@ mod helpers {
9841004 >;
9851005 }
9861006
1007+ // For byte_at: usize + BoundedInt<0,30>
1008+ impl UsizeAddBytes31Index of AddHelper <usize , Bytes31Index > {
1009+ type Result =
1010+ BoundedInt <0 , { Bounded :: <usize >:: MAX . into () + BYTES_IN_BYTES31_MINUS_ONE . into () }>;
1011+ }
1012+
1013+ // For byte_at: div_rem of (usize + BoundedInt<0,30>) by 31
1014+ const USIZE_PLUS_30_DIV_31 : felt252 = (Bounded :: <usize >:: MAX / 31 + 1 ). into ();
1015+ impl UsizePlusBytes31IndexDivRemB31 of DivRemHelper <
1016+ UsizeAddBytes31Index :: Result , BytesInBytes31Typed ,
1017+ > {
1018+ type DivT = BoundedInt <0 , USIZE_PLUS_30_DIV_31 >;
1019+ type RemT = Bytes31Index ;
1020+ }
1021+
1022+ // For byte_at: 30 - BoundedInt<0,30>
1023+ impl B30SubBytes31Index of SubHelper <
1024+ UnitInt <{ BYTES_IN_BYTES31_MINUS_ONE . into() }>, Bytes31Index ,
1025+ > {
1026+ type Result = Bytes31Index ;
1027+ }
1028+
1029+ // For byte_at: BoundedInt<0,30> - 1
1030+ impl Bytes31IndexSub1 of SubHelper <Bytes31Index , UnitInt <1 >> {
1031+ type Result = BoundedInt <- 1 , { BYTES_IN_BYTES31_MINUS_ONE . into() - 1 }>;
1032+ }
1033+
1034+ // For byte_at: (BoundedInt<0,30> - 1) - BoundedInt<0,30>
1035+ impl Bytes31IndexMinus1SubBytes31Index of SubHelper <Bytes31IndexSub1 :: Result , Bytes31Index > {
1036+ type Result =
1037+ BoundedInt <
1038+ { - BYTES_IN_BYTES31_MINUS_ONE . into() - 1 },
1039+ { BYTES_IN_BYTES31_MINUS_ONE . into() - 1 },
1040+ >;
1041+ }
1042+
1043+ // For byte_at: split BoundedInt<-31, 29> at 0.
1044+ impl ConstrainRemainderIndexAt0 of bounded_int :: ConstrainHelper <
1045+ Bytes31IndexMinus1SubBytes31Index :: Result , 0 ,
1046+ > {
1047+ type LowT = BoundedInt <{ - BYTES_IN_BYTES31_MINUS_ONE . into() - 1 }, - 1 >;
1048+ type HighT = BoundedInt <0 , { BYTES_IN_BYTES31_MINUS_ONE . into() - 1 }>;
1049+ }
1050+
9871051 /// Calculates the length of a `ByteSpan` in bytes.
9881052 pub fn calc_bytespan_len (span : ByteSpan ) -> usize {
9891053 let data_bytes = bounded_int :: mul (span . data. len (), BYTES_IN_BYTES31_UNIT_INT );
@@ -1073,5 +1137,39 @@ mod helpers {
10731137 pub fn length_minus_one (len : BoundedInt <1 , 31 >) -> Bytes31Index {
10741138 bounded_int :: sub (len , 1 )
10751139 }
1140+ /// Returns the byte at the given index in the ByteSpan.
1141+ /// If out of bounds: returns `None`.
1142+ pub fn byte_at (self : @ ByteSpan , index : usize ) -> Option <u8 > {
1143+ let absolute_index = bounded_int :: add (index , * self . first_char_start_offset);
1144+ let (word_index_bounded , msb_index ) = bounded_int :: div_rem (
1145+ absolute_index , NZ_BYTES_IN_BYTES31 ,
1146+ );
1147+
1148+ let word_index = upcast (word_index_bounded );
1149+ match self . data. get (word_index ) {
1150+ Some (word ) => {
1151+ // Convert from MSB to LSB indexing.
1152+ let lsb_index = bounded_int :: sub (BYTES_IN_BYTES31_MINUS_ONE_TYPED , msb_index );
1153+ Some (word . at (upcast (lsb_index )))
1154+ },
1155+ None => {
1156+ // Word index must equal data.len() for remainder word.
1157+ if word_index != self . data. len () {
1158+ return None ;
1159+ }
1160+
1161+ // Compute LSB index: remainder_len - 1 - msb_index.
1162+ let lsb_index_bounded = bounded_int :: sub (
1163+ bounded_int :: sub (* self . remainder_len, ONE_TYPED ), msb_index ,
1164+ );
1165+
1166+ // Check if in bounds and extract non-negative index.
1167+ let Err (lsb_index ) = bounded_int :: constrain :: <_ , 0 >(lsb_index_bounded ) else {
1168+ return None ; // Out of bounds: index >= remainder_len.
1169+ };
1170+ Some (u8_at_u256 ((* self . remainder_word). into (), upcast (lsb_index )))
1171+ },
1172+ }
1173+ }
10761174}
10771175pub (crate ) use helpers :: len_parts;
0 commit comments