binius_field/underlier/
divisible.rs

1// Copyright 2024-2025 Irreducible Inc.
2
3use std::{
4	mem::{align_of, size_of},
5	slice::{self, from_raw_parts, from_raw_parts_mut},
6};
7
8/// Underlier value that can be split into a slice of smaller `U` values.
9/// This trait is unsafe because it allows to reinterpret the memory of a type as a slice of another
10/// type.
11///
12/// # Safety
13/// Implementors must ensure that `&Self` can be safely bit-cast to `&[U; Self::WIDTH]` and
14/// `&mut Self` can be safely bit-cast to `&mut [U; Self::WIDTH]`.
15#[allow(dead_code)]
16pub unsafe trait Divisible<U: UnderlierType>: UnderlierType {
17	const WIDTH: usize = {
18		assert!(size_of::<Self>().is_multiple_of(size_of::<U>()));
19		assert!(align_of::<Self>() >= align_of::<U>());
20		size_of::<Self>() / size_of::<U>()
21	};
22
23	/// This is actually `[U; Self::WIDTH]` but we can't use it as the default value in the trait
24	/// definition without `generic_const_exprs` feature enabled.
25	type Array: IntoIterator<Item = U, IntoIter: Send + Clone>;
26
27	fn split_val(self) -> Self::Array;
28	fn split_ref(&self) -> &[U];
29	fn split_mut(&mut self) -> &mut [U];
30
31	fn split_slice(values: &[Self]) -> &[U] {
32		let ptr = values.as_ptr() as *const U;
33		// Safety: if `&Self` can be reinterpreted as a sequence of `Self::WIDTH` elements of `U`
34		// then `&[Self]` can be reinterpreted as a sequence of `Self::Width * values.len()`
35		// elements of `U`.
36		unsafe { from_raw_parts(ptr, values.len() * Self::WIDTH) }
37	}
38
39	fn split_slice_mut(values: &mut [Self]) -> &mut [U] {
40		let ptr = values.as_mut_ptr() as *mut U;
41		// Safety: if `&mut Self` can be reinterpreted as a sequence of `Self::WIDTH` elements of
42		// `U` then `&mut [Self]` can be reinterpreted as a sequence of `Self::Width *
43		// values.len()` elements of `U`.
44		unsafe { from_raw_parts_mut(ptr, values.len() * Self::WIDTH) }
45	}
46}
47
48unsafe impl<U: UnderlierType> Divisible<U> for U {
49	type Array = [U; 1];
50
51	fn split_val(self) -> Self::Array {
52		[self]
53	}
54
55	fn split_ref(&self) -> &[U] {
56		slice::from_ref(self)
57	}
58
59	fn split_mut(&mut self) -> &mut [U] {
60		slice::from_mut(self)
61	}
62}
63
64/// Divides an underlier type into smaller underliers in memory and iterates over them.
65///
66/// [`DivisIterable`] (say that 10 times, fast) provides iteration over the subdivisions of an
67/// underlier type, guaranteeing that iteration proceeds from the least significant bits to the most
68/// significant bits, regardless of the CPU architecture's endianness.
69///
70/// # Endianness Handling
71///
72/// To ensure consistent LSB-to-MSB iteration order across all platforms:
73/// - On little-endian systems: elements are naturally ordered LSB-to-MSB in memory, so iteration
74///   proceeds forward through the array
75/// - On big-endian systems: elements are ordered MSB-to-LSB in memory, so iteration is reversed to
76///   achieve LSB-to-MSB order
77///
78/// This abstraction allows code to work with subdivided underliers in a platform-independent way
79/// while maintaining the invariant that the first element always represents the least significant
80/// portion of the value.
81pub trait DivisIterable<T> {
82	type Iter<'a>: ExactSizeIterator<Item = &'a T>
83	where
84		Self: 'a,
85		T: 'a;
86
87	/// Returns an iterator over subdivisions of this underlier, ordered from LSB to MSB.
88	fn divide(&self) -> Self::Iter<'_>;
89}
90
91macro_rules! impl_divisible {
92    (@pairs $name:ty,?) => {};
93    (@pairs $bigger:ty, $smaller:ty) => {
94        unsafe impl $crate::underlier::Divisible<$smaller> for $bigger {
95            type Array = [$smaller; {size_of::<Self>() / size_of::<$smaller>()}];
96
97            fn split_val(self) -> Self::Array {
98                bytemuck::must_cast::<_, Self::Array>(self)
99            }
100
101            fn split_ref(&self) -> &[$smaller] {
102                bytemuck::must_cast_ref::<_, [$smaller;{(<$bigger>::BITS as usize / <$smaller>::BITS as usize ) }]>(self)
103            }
104
105            fn split_mut(&mut self) -> &mut [$smaller] {
106                bytemuck::must_cast_mut::<_, [$smaller;{(<$bigger>::BITS as usize / <$smaller>::BITS as usize ) }]>(self)
107            }
108        }
109
110		unsafe impl $crate::underlier::Divisible<$smaller> for $crate::underlier::ScaledUnderlier<$bigger, 2> {
111            type Array = [$smaller; {2 * size_of::<$bigger>() / size_of::<$smaller>()}];
112
113            fn split_val(self) -> Self::Array {
114                bytemuck::must_cast::<_, Self::Array>(self)
115            }
116
117            fn split_ref(&self) -> &[$smaller] {
118                bytemuck::must_cast_ref::<_, [$smaller;{(2 * <$bigger>::BITS as usize / <$smaller>::BITS as usize ) }]>(&self.0)
119            }
120
121            fn split_mut(&mut self) -> &mut [$smaller] {
122                bytemuck::must_cast_mut::<_, [$smaller;{(2 * <$bigger>::BITS as usize / <$smaller>::BITS as usize ) }]>(&mut self.0)
123            }
124        }
125
126		unsafe impl $crate::underlier::Divisible<$smaller> for $crate::underlier::ScaledUnderlier<$crate::underlier::ScaledUnderlier<$bigger, 2>, 2> {
127            type Array = [$smaller; {4 * size_of::<$bigger>() / size_of::<$smaller>()}];
128
129            fn split_val(self) -> Self::Array {
130                bytemuck::must_cast::<_, Self::Array>(self)
131            }
132
133            fn split_ref(&self) -> &[$smaller] {
134                bytemuck::must_cast_ref::<_, [$smaller;{(4 * <$bigger>::BITS as usize / <$smaller>::BITS as usize ) }]>(&self.0)
135            }
136
137            fn split_mut(&mut self) -> &mut [$smaller] {
138                bytemuck::must_cast_mut::<_, [$smaller;{(4 * <$bigger>::BITS as usize / <$smaller>::BITS as usize ) }]>(&mut self.0)
139            }
140        }
141
142		#[cfg(target_endian = "little")]
143		impl $crate::underlier::DivisIterable<$smaller> for $bigger {
144			type Iter<'a> = std::slice::Iter<'a, $smaller>;
145
146			#[inline]
147			fn divide(&self) -> Self::Iter<'_> {
148				const N: usize = size_of::<$bigger>() / size_of::<$smaller>();
149				::bytemuck::must_cast_ref::<Self, [$smaller; N]>(self).iter()
150			}
151		}
152
153		#[cfg(target_endian = "big")]
154		impl $crate::underlier::DivisIterable<$smaller> for $bigger {
155			type Iter<'a> = std::iter::Rev<std::slice::Iter<'a, u8>>;
156
157			#[inline]
158			fn divide(&self) -> Self::Iter<'_> {
159				const N: usize = size_of::<$bigger>() / size_of::<$smaller>();
160				::bytemuck::must_cast_ref::<Self, [$smaller; N]>(self).iter().rev()
161			}
162		}
163    };
164    (@pairs $first:ty, $second:ty, $($tail:ty),*) => {
165        impl_divisible!(@pairs $first, $second);
166        impl_divisible!(@pairs $first, $($tail),*);
167    };
168    ($_:ty) => {};
169    ($head:ty, $($tail:ty),*) => {
170        impl_divisible!(@pairs $head, $($tail),*);
171        impl_divisible!($($tail),*);
172    }
173}
174
175#[allow(unused)]
176pub(crate) use impl_divisible;
177
178use super::UnderlierType;
179
180impl_divisible!(u128, u64, u32, u16, u8);