1- use crate :: traits :: {Into , TryInto };
2- use crate :: option :: OptionTrait ;
1+ // ! Definitions and utilities for the `bytes31` type.
2+ // !
3+ // ! The `bytes31` type is a compact, indexable 31-byte type.
4+ // !
5+ // ! # Examples
6+ // !
7+ // ! Creating a `bytes31` from a `felt252`:
8+ // ! ```
9+ // ! let value: bytes31 = 0xaabb.try_into().unwrap();
10+ // ! ```
11+ // !
12+ // ! Accessing a byte by index:
13+ // ! ```
14+ // ! assert!(value[0] == 0xbb);
15+ // ! ```
16+
317#[allow(unused_imports)]
418use crate :: integer :: {u128_safe_divmod, u128_to_felt252 };
19+ #[allow(unused_imports)]
20+ use crate :: option :: OptionTrait ;
521use crate :: RangeCheck ;
22+ use crate :: traits :: {Into , TryInto };
623
724pub (crate ) const BYTES_IN_BYTES31 : usize = 31 ;
825const BYTES_IN_U128 : usize = 16 ;
926pub (crate ) const POW_2_128 : felt252 = 0x100000000000000000000000000000000 ;
1027pub (crate ) const POW_2_8 : u128 = 0x100 ;
1128
29+ /// Represents a 31-byte fixed-size byte type.
1230#[derive(Copy , Drop )]
1331pub extern type bytes31 ;
1432
1533pub (crate ) extern fn bytes31_const <const value : felt252 >() -> bytes31 nopanic ;
1634extern fn bytes31_try_from_felt252 (value : felt252 ) -> Option <bytes31 > implicits (RangeCheck ) nopanic ;
1735extern fn bytes31_to_felt252 (value : bytes31 ) -> felt252 nopanic ;
1836
37+ /// A trait for accessing a specific byte of a `bytes31` type.
1938#[generate_trait]
2039pub impl Bytes31Impl of Bytes31Trait {
21- /// Gets the byte at the given index (LSB's index is 0), assuming that
22- /// `index < BYTES_IN_BYTES31`. If the assumption is not met, the behavior is undefined.
40+ /// Returns the byte at the given index (LSB's index is 0).
41+ ///
42+ /// Assumes that `index < BYTES_IN_BYTES31`. If the assumption is not met, the behavior is
43+ /// undefined.
44+ ///
45+ /// # Examples
46+ ///
47+ /// ```
48+ /// let bytes: bytes31 = 1_u8.into();
49+ /// assert!(bytes.at(0) == 1);
50+ /// ```
2351 fn at (self : @ bytes31 , index : usize ) -> u8 {
24- let u256 { low , high } = (* self ). into ();
25- let res_u128 = if index < BYTES_IN_U128 {
26- (low / one_shift_left_bytes_u128 (index )) % POW_2_8
27- } else {
28- (high / one_shift_left_bytes_u128 (index - BYTES_IN_U128 )) % POW_2_8
29- };
30- res_u128 . try_into (). unwrap ()
52+ u8_at_u256 ((* self ). into (), index )
3153 }
3254}
3355
@@ -70,34 +92,40 @@ pub(crate) impl U8IntoBytes31 of Into<u8, bytes31> {
7092 crate :: integer :: upcast (self )
7193 }
7294}
95+
7396impl U16IntoBytes31 of Into <u16 , bytes31 > {
7497 fn into (self : u16 ) -> bytes31 {
7598 crate :: integer :: upcast (self )
7699 }
77100}
101+
78102impl U32IntoBytes31 of Into <u32 , bytes31 > {
79103 fn into (self : u32 ) -> bytes31 {
80104 crate :: integer :: upcast (self )
81105 }
82106}
107+
83108impl U64IntoBytes31 of Into <u64 , bytes31 > {
84109 fn into (self : u64 ) -> bytes31 {
85110 crate :: integer :: upcast (self )
86111 }
87112}
113+
88114pub (crate ) impl U128IntoBytes31 of Into <u128 , bytes31 > {
89115 fn into (self : u128 ) -> bytes31 {
90116 crate :: integer :: upcast (self )
91117 }
92118}
93119
94- /// Splits a bytes31 into two bytes31s at the given index (LSB's index is 0).
95- /// The bytes31s are represented using felt252s to improve performance.
120+ /// Splits a `bytes31` into two `bytes31`s at the given index (LSB's index is 0).
121+ /// The input `bytes31` and the output `bytes31`s are represented using `felt252`s to improve
122+ /// performance.
123+ ///
96124/// Note: this function assumes that:
97- /// 1. `word` is validly convertible to a bytes31 which has no more than `len` bytes of data.
98- /// 2. index <= len.
99- /// 3. len <= BYTES_IN_BYTES31.
100- /// If these assumptions are not met, it can corrupt the ByteArray . Thus, this should be a
125+ /// 1. `word` is validly convertible to a ` bytes31`` which has no more than `len` bytes of data.
126+ /// 2. ` index <= len` .
127+ /// 3. ` len <= BYTES_IN_BYTES31` .
128+ /// If these assumptions are not met, it can corrupt the `byte31`s . Thus, this should be a
101129/// private function. We could add masking/assertions but it would be more expansive.
102130pub (crate ) fn split_bytes31 (word : felt252 , len : usize , index : usize ) -> (felt252 , felt252 ) {
103131 if index == 0 {
@@ -114,32 +142,27 @@ pub(crate) fn split_bytes31(word: felt252, len: usize, index: usize) -> (felt252
114142 }
115143
116144 if len <= BYTES_IN_U128 {
117- let (quotient , remainder ) = u128_safe_divmod (
118- low , one_shift_left_bytes_u128 (index ). try_into (). unwrap (),
119- );
120- return (remainder . into (), quotient . into ());
145+ let result = split_u128 (low , index );
146+ return (result . low. into (), result . high. into ());
121147 }
122148
123149 // len > BYTES_IN_U128
124150 if index < BYTES_IN_U128 {
125- let (low_quotient , low_remainder ) = u128_safe_divmod (
126- low , one_shift_left_bytes_u128 (index ). try_into (). unwrap (),
127- );
151+ let low_result = split_u128 (low , index );
128152 let right = high . into () * one_shift_left_bytes_u128 (BYTES_IN_U128 - index ). into ()
129- + low_quotient . into ();
130- return (low_remainder . into (), right );
153+ + low_result . high . into ();
154+ return (low_result . low . into (), right );
131155 }
132156
133157 // len > BYTES_IN_U128 && index > BYTES_IN_U128
134- let (high_quotient , high_remainder ) = u128_safe_divmod (
135- high , one_shift_left_bytes_u128 (index - BYTES_IN_U128 ). try_into (). unwrap (),
136- );
137- let left = high_remainder . into () * POW_2_128 + low . into ();
138- return (left , high_quotient . into ());
158+
159+ let high_result = split_u128 (high , index - BYTES_IN_U128 );
160+ let left = high_result . low. into () * POW_2_128 + low . into ();
161+ return (left , high_result . high. into ());
139162}
140163
141164
142- /// Returns 1 << (8 * ` n_bytes`) as felt252, assuming that `n_bytes < BYTES_IN_BYTES31`.
165+ /// Returns ` 1 << (8 * n_bytes)` as ` felt252` , assuming that `n_bytes < BYTES_IN_BYTES31`.
143166///
144167/// Note: if `n_bytes >= BYTES_IN_BYTES31`, the behavior is undefined. If one wants to
145168/// assert that in the callsite, it's sufficient to assert that `n_bytes != BYTES_IN_BYTES31`
@@ -152,10 +175,33 @@ pub(crate) fn one_shift_left_bytes_felt252(n_bytes: usize) -> felt252 {
152175 }
153176}
154177
155- /// Returns 1 << (8 * ` n_bytes`) as u128, where `n_bytes` must be < BYTES_IN_U128.
178+ /// Returns ` 1 << (8 * n_bytes)` as ` u128` , where `n_bytes` must be < ` BYTES_IN_U128` .
156179///
157180/// Panics if `n_bytes >= BYTES_IN_U128`.
158181pub (crate ) fn one_shift_left_bytes_u128 (n_bytes : usize ) -> u128 {
182+ one_shift_left_bytes_u128_nz (n_bytes ). into ()
183+ }
184+
185+ /// Splits a u128 into two words as a `u256` - the `low` is the first `n_bytes` the `high` is the
186+ /// rest.
187+ pub (crate ) fn split_u128 (value : u128 , n_bytes : usize ) -> u256 {
188+ let (high , low ) = DivRem :: div_rem (value , one_shift_left_bytes_u128_nz (n_bytes ));
189+ u256 { low , high }
190+ }
191+
192+ /// Returns the `u8` at `index` if you look at `value` as an array of 32 `u8`s.
193+ pub (crate ) fn u8_at_u256 (value : u256 , index : usize ) -> u8 {
194+ get_lsb (
195+ if index < BYTES_IN_U128 {
196+ split_u128 (value . low, index ). high
197+ } else {
198+ split_u128 (value . high, index - BYTES_IN_U128 ). high
199+ },
200+ )
201+ }
202+
203+ /// Same as `one_shift_left_bytes_u128` but returns `NonZero` value.
204+ fn one_shift_left_bytes_u128_nz (n_bytes : usize ) -> NonZero <u128 > {
159205 match n_bytes {
160206 0 => 0x1 ,
161207 1 => 0x100 ,
@@ -184,3 +230,20 @@ impl Bytes31PartialEq of PartialEq<bytes31> {
184230 lhs_as_felt252 == rhs_as_felt252
185231 }
186232}
233+
234+ mod helpers {
235+ use core :: internal :: bounded_int :: {DivRemHelper , BoundedInt , div_rem};
236+
237+ impl DivRemU128By256 of DivRemHelper <u128 , BoundedInt <256 , 256 >> {
238+ type DivT = BoundedInt <0 , 0xffffffffffffffffffffffffffffff >;
239+ type RemT = BoundedInt <0 , 0xff >;
240+ }
241+
242+ /// Returns the least significant byte of the given u128.
243+ pub fn get_lsb (value : u128 ) -> u8 {
244+ let (_ , res ) = div_rem :: <_ , BoundedInt <256 , 256 >>(value , 256 );
245+ core :: integer :: upcast (res )
246+ }
247+ }
248+
249+ pub (crate ) use helpers :: get_lsb;
0 commit comments