Skip to main content

binius_field/arch/portable/
m128.rs

1// Copyright 2026 The Binius Developers
2
3use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not, Shl, Shr};
4
5use binius_utils::{
6	DeserializeBytes, FixedSizeSerializeBytes, SerializationError, SerializeBytes,
7	bytes::{Buf, BufMut},
8	serialization::{assert_enough_data_for, assert_enough_space_for},
9};
10use bytemuck::{Pod, Zeroable};
11use derive_more::{From, Into};
12use rand::{
13	distr::{Distribution, StandardUniform},
14	prelude::*,
15};
16
17use crate::{
18	BinaryField,
19	divisible::{Divisible, impl_divisible_memcast, impl_divisible_self},
20	packed_fields::primitive::PackedPrimitiveType,
21	underlier::{SmallU, Underlier, impl_divisible_bitmask},
22};
23
24/// 128-bit underlier for the portable build — a transparent wrapper over `u128`.
25///
26/// On x86_64/aarch64 `M128` is a SIMD register and on wasm32 (with `simd128`) a `v128`; here it is
27/// a plain `u128` newtype. Wrapping rather than aliasing `u128` keeps `M128` a distinct type on
28/// every target, so the `M128 <-> u128` conversions never collide with `u128`'s own reflexive
29/// impls and the architecture-gated `Ghash128b` conversions need no cfg gate.
30#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, From, Into)]
31#[repr(transparent)]
32pub struct M128(u128);
33
34impl M128 {
35	#[inline(always)]
36	pub const fn from_u128(value: u128) -> Self {
37		Self(value)
38	}
39}
40
41impl From<u64> for M128 {
42	#[inline(always)]
43	fn from(value: u64) -> Self {
44		Self(value as u128)
45	}
46}
47impl From<u32> for M128 {
48	#[inline(always)]
49	fn from(value: u32) -> Self {
50		Self(value as u128)
51	}
52}
53impl From<u16> for M128 {
54	#[inline(always)]
55	fn from(value: u16) -> Self {
56		Self(value as u128)
57	}
58}
59impl From<u8> for M128 {
60	#[inline(always)]
61	fn from(value: u8) -> Self {
62		Self(value as u128)
63	}
64}
65
66impl<const N: usize> From<SmallU<N>> for M128 {
67	#[inline(always)]
68	fn from(value: SmallU<N>) -> Self {
69		Self(value.val() as u128)
70	}
71}
72
73impl SerializeBytes for M128 {
74	fn serialize(&self, mut write_buf: impl BufMut) -> Result<(), SerializationError> {
75		assert_enough_space_for(&write_buf, std::mem::size_of::<Self>())?;
76		write_buf.put_u128_le(self.0);
77		Ok(())
78	}
79}
80
81impl DeserializeBytes for M128 {
82	fn deserialize(mut read_buf: impl Buf) -> Result<Self, SerializationError>
83	where
84		Self: Sized,
85	{
86		assert_enough_data_for(&read_buf, std::mem::size_of::<Self>())?;
87		Ok(Self(read_buf.get_u128_le()))
88	}
89}
90
91impl FixedSizeSerializeBytes for M128 {
92	const BYTE_SIZE: usize = 16;
93}
94
95unsafe impl Zeroable for M128 {}
96
97unsafe impl Pod for M128 {}
98
99impl_divisible_memcast!(M128, u128, u64, u32, u16, u8);
100impl_divisible_bitmask!(M128, 1, 2, 4);
101impl_divisible_self!(M128);
102
103impl BitAnd for M128 {
104	type Output = Self;
105
106	#[inline(always)]
107	fn bitand(self, rhs: Self) -> Self::Output {
108		Self(self.0 & rhs.0)
109	}
110}
111
112impl BitAndAssign for M128 {
113	#[inline(always)]
114	fn bitand_assign(&mut self, rhs: Self) {
115		self.0 &= rhs.0;
116	}
117}
118
119impl BitOr for M128 {
120	type Output = Self;
121
122	#[inline(always)]
123	fn bitor(self, rhs: Self) -> Self::Output {
124		Self(self.0 | rhs.0)
125	}
126}
127
128impl BitOrAssign for M128 {
129	#[inline(always)]
130	fn bitor_assign(&mut self, rhs: Self) {
131		self.0 |= rhs.0;
132	}
133}
134
135impl BitXor for M128 {
136	type Output = Self;
137
138	#[inline(always)]
139	fn bitxor(self, rhs: Self) -> Self::Output {
140		Self(self.0 ^ rhs.0)
141	}
142}
143
144impl BitXorAssign for M128 {
145	#[inline(always)]
146	fn bitxor_assign(&mut self, rhs: Self) {
147		self.0 ^= rhs.0;
148	}
149}
150
151impl Not for M128 {
152	type Output = Self;
153
154	#[inline(always)]
155	fn not(self) -> Self::Output {
156		Self(!self.0)
157	}
158}
159
160impl Shl<usize> for M128 {
161	type Output = Self;
162
163	#[inline(always)]
164	fn shl(self, rhs: usize) -> Self::Output {
165		Self(self.0 << rhs)
166	}
167}
168
169impl Shr<usize> for M128 {
170	type Output = Self;
171
172	#[inline(always)]
173	fn shr(self, rhs: usize) -> Self::Output {
174		Self(self.0 >> rhs)
175	}
176}
177
178impl Distribution<M128> for StandardUniform {
179	#[inline]
180	fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> M128 {
181		M128(rng.random())
182	}
183}
184
185impl std::fmt::Display for M128 {
186	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187		write!(f, "{:032X}", self.0)
188	}
189}
190
191impl std::fmt::Debug for M128 {
192	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193		write!(f, "M128({self})")
194	}
195}
196
197impl std::fmt::LowerHex for M128 {
198	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199		std::fmt::LowerHex::fmt(&self.0, f)
200	}
201}
202
203impl Underlier for M128 {
204	const LOG_BITS: usize = 7;
205	const ZERO: Self = Self(0);
206	const ONE: Self = Self(1);
207	const ONES: Self = Self(u128::MAX);
208
209	#[inline(always)]
210	fn interleave(self, other: Self, log_block_len: usize) -> (Self, Self) {
211		let (a, b) = self.0.interleave(other.0, log_block_len);
212		(Self(a), Self(b))
213	}
214}
215
216impl<Scalar: BinaryField> From<u128> for PackedPrimitiveType<M128, Scalar> {
217	#[inline]
218	fn from(value: u128) -> Self {
219		Self::from(M128::from(value))
220	}
221}
222
223impl<Scalar: BinaryField> From<PackedPrimitiveType<M128, Scalar>> for u128 {
224	#[inline]
225	fn from(value: PackedPrimitiveType<M128, Scalar>) -> Self {
226		value.to_underlier().into()
227	}
228}