@@ -38,6 +38,7 @@ pub const EXTENSION_DEGREE: usize = 3;
3838#[ derive(
3939 Debug , PartialEq , Eq , Copy , Clone , Hash , Serialize , Deserialize , BFieldCodec , Arbitrary ,
4040) ]
41+ #[ repr( transparent) ]
4142pub struct XFieldElement {
4243 pub coefficients : [ BFieldElement ; EXTENSION_DEGREE ] ,
4344}
@@ -177,6 +178,80 @@ macro_rules! xfe_array {
177178 } ;
178179}
179180
181+ /// Re-interpret a slice of [`XFieldElement`]s as a slice of [`BFieldElement`]s
182+ /// without any memory allocation.
183+ ///
184+ /// This function is semantically similar to [flat-mapping] the coefficients of
185+ /// the `XFieldElement`s (see examples). However, this function does not perform
186+ /// any memory allocation, which makes is particularly useful in
187+ /// high-performance scenarios.
188+ ///
189+ /// # Examples
190+ ///
191+ /// Re-interpretation behaves like flattening, but does not allocate or copy any
192+ /// data.
193+ ///
194+ /// ```
195+ /// # use twenty_first::prelude::*;
196+ /// # use twenty_first::math::x_field_element::as_flat_slice;
197+ /// let xfes = xfe_vec![[17, 18, 19], [42, 42, 44], [97, 98, 99]];
198+ /// let bfes = bfe_vec![17, 18, 19, 42, 42, 44, 97, 98, 99];
199+ /// assert_eq!(&bfes, as_flat_slice(&xfes));
200+ /// ```
201+ ///
202+ /// This can be particularly useful for hashing sequences of [`XFieldElement]`s,
203+ /// where ownership is irrelevant:
204+ ///
205+ /// ```
206+ /// # use twenty_first::prelude::*;
207+ /// # use twenty_first::math::x_field_element::as_flat_slice;
208+ /// let xfes = xfe_vec![42; 17];
209+ /// let xfe_digest = Tip5::hash_varlen(as_flat_slice(&xfes));
210+ ///
211+ /// // alternative requires copying data
212+ /// let bfes = xfes.into_iter().flat_map(|xfe| xfe.coefficients).collect::<Vec<_>>();
213+ /// let bfe_digest = Tip5::hash_varlen(&bfes);
214+ ///
215+ /// assert_eq!(bfe_digest, xfe_digest);
216+ /// ```
217+ ///
218+ /// [hashing]: crate::tip5::Tip5::hash_varlen
219+ /// [Tip5]: crate::tip5::Tip5
220+ /// [flat-mapping]: Iterator::flat_map
221+ pub fn as_flat_slice ( xfe_slice : & [ XFieldElement ] ) -> & [ BFieldElement ] {
222+ let slice_pointer = xfe_slice. as_ptr ( ) as * const BFieldElement ;
223+ let bfe_slice_len = xfe_slice. len ( ) * EXTENSION_DEGREE ;
224+
225+ // SAFETY:
226+ // - The slice_pointer is non-null, and is valid for reads for
227+ // xfe_slice.len() * size_of::<XFieldElement>() ==
228+ // xfe_slice.len() * size_of::<BFieldElement>() * EXTENSION_DEGREE
229+ // many bytes, and is properly aligned because both BFieldElement and
230+ // XFieldElement are #[repr(transparent)]. In particular:
231+ // - The entire memory range of the slice is contained within a single
232+ // allocated object. This is because of
233+ // (a) the origin of `slice_pointer` being a slice, and
234+ // (b) the layout and ABI of XFieldElement is identical to
235+ // [BFieldElement; EXTENSION_DEGREE] because of
236+ // #[repr(transparent)]
237+ // - The slice_pointer is non-null and aligned, again because of
238+ // #[repr(transparent)] on BFieldElement and XFieldElement.
239+ // - The slice_pointer points to xfe_slice.len() * EXTENSION_DEGREE
240+ // consecutive properly initialized values of type BFieldElement,
241+ // again because of #[repr(transparent)] on BFieldElement and
242+ // XFieldElement.
243+ // - The memory referenced by the returned slice cannot be mutated for
244+ // the duration of the lifetime of xfe_slice thanks to rust's
245+ // “mut XOR shared” compile time guarantees.
246+ // - The total size of the produced slice is no larger than isize::MAX
247+ // since it is identical to the total size of the initial size, and
248+ // adding that size to the slice_pointer does not “wrap around” the
249+ // address space because both, the slice_pointer and the total size
250+ // have been obtained through safe code or unsafe code for which the
251+ // safety invariants have been upheld.
252+ unsafe { std:: slice:: from_raw_parts ( slice_pointer, bfe_slice_len) }
253+ }
254+
180255impl From < XFieldElement > for Digest {
181256 /// Interpret the `XFieldElement` as a [`Digest`]. No hashing is performed.
182257 /// This interpretation can be useful for [`Tip5`](crate::prelude::Tip5)
0 commit comments