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
+
3
17
#[allow(unused_imports)]
4
18
use crate :: integer :: {u128_safe_divmod, u128_to_felt252 };
19
+ #[allow(unused_imports)]
20
+ use crate :: option :: OptionTrait ;
5
21
use crate :: RangeCheck ;
22
+ use crate :: traits :: {Into , TryInto };
6
23
7
24
pub (crate ) const BYTES_IN_BYTES31 : usize = 31 ;
8
25
const BYTES_IN_U128 : usize = 16 ;
9
26
pub (crate ) const POW_2_128 : felt252 = 0x100000000000000000000000000000000 ;
10
27
pub (crate ) const POW_2_8 : u128 = 0x100 ;
11
28
29
+ /// Represents a 31-byte fixed-size byte type.
12
30
#[derive(Copy , Drop )]
13
31
pub extern type bytes31 ;
14
32
15
33
pub (crate ) extern fn bytes31_const <const value : felt252 >() -> bytes31 nopanic ;
16
34
extern fn bytes31_try_from_felt252 (value : felt252 ) -> Option <bytes31 > implicits (RangeCheck ) nopanic ;
17
35
extern fn bytes31_to_felt252 (value : bytes31 ) -> felt252 nopanic ;
18
36
37
+ /// A trait for accessing a specific byte of a `bytes31` type.
19
38
#[generate_trait]
20
39
pub 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
+ /// ```
23
51
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 )
31
53
}
32
54
}
33
55
@@ -70,34 +92,40 @@ pub(crate) impl U8IntoBytes31 of Into<u8, bytes31> {
70
92
crate :: integer :: upcast (self )
71
93
}
72
94
}
95
+
73
96
impl U16IntoBytes31 of Into <u16 , bytes31 > {
74
97
fn into (self : u16 ) -> bytes31 {
75
98
crate :: integer :: upcast (self )
76
99
}
77
100
}
101
+
78
102
impl U32IntoBytes31 of Into <u32 , bytes31 > {
79
103
fn into (self : u32 ) -> bytes31 {
80
104
crate :: integer :: upcast (self )
81
105
}
82
106
}
107
+
83
108
impl U64IntoBytes31 of Into <u64 , bytes31 > {
84
109
fn into (self : u64 ) -> bytes31 {
85
110
crate :: integer :: upcast (self )
86
111
}
87
112
}
113
+
88
114
pub (crate ) impl U128IntoBytes31 of Into <u128 , bytes31 > {
89
115
fn into (self : u128 ) -> bytes31 {
90
116
crate :: integer :: upcast (self )
91
117
}
92
118
}
93
119
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
+ ///
96
124
/// 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
101
129
/// private function. We could add masking/assertions but it would be more expansive.
102
130
pub (crate ) fn split_bytes31 (word : felt252 , len : usize , index : usize ) -> (felt252 , felt252 ) {
103
131
if index == 0 {
@@ -114,32 +142,27 @@ pub(crate) fn split_bytes31(word: felt252, len: usize, index: usize) -> (felt252
114
142
}
115
143
116
144
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 ());
121
147
}
122
148
123
149
// len > BYTES_IN_U128
124
150
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 );
128
152
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 );
131
155
}
132
156
133
157
// 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 ());
139
162
}
140
163
141
164
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`.
143
166
///
144
167
/// Note: if `n_bytes >= BYTES_IN_BYTES31`, the behavior is undefined. If one wants to
145
168
/// 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 {
152
175
}
153
176
}
154
177
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` .
156
179
///
157
180
/// Panics if `n_bytes >= BYTES_IN_U128`.
158
181
pub (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 > {
159
205
match n_bytes {
160
206
0 => 0x1 ,
161
207
1 => 0x100 ,
@@ -184,3 +230,20 @@ impl Bytes31PartialEq of PartialEq<bytes31> {
184
230
lhs_as_felt252 == rhs_as_felt252
185
231
}
186
232
}
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