Skip to content

Commit e7a7cde

Browse files
committed
Implement Encoding for all Uints
1 parent 7273f8d commit e7a7cde

11 files changed

Lines changed: 167 additions & 95 deletions

File tree

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ rust-version = "1.85"
1818

1919
[dependencies]
2020
subtle = { version = "2.6", default-features = false }
21+
bytemuck = { version = "1.24", default-features = false, features = ["must_cast", "must_cast_extra"] }
2122

2223
# optional dependencies
2324
der = { version = "0.8.0-rc.9", optional = true, default-features = false }

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@
167167
#[macro_use]
168168
extern crate alloc;
169169

170+
pub use uint::encoding::{EncodedUint, TryFromSliceError};
171+
170172
#[cfg(feature = "rand_core")]
171173
pub use rand_core;
172174
#[cfg(feature = "rlp")]

src/limb.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,14 @@ impl Serialize for Limb {
231231
#[cfg(feature = "zeroize")]
232232
impl zeroize::DefaultIsZeroes for Limb {}
233233

234+
// SAFETY: `Limb` is a newtype of an integer POD type
235+
#[allow(unsafe_code)]
236+
unsafe impl bytemuck::Zeroable for Limb {}
237+
238+
// SAFETY: `Limb` is a newtype of an integer POD type
239+
#[allow(unsafe_code)]
240+
unsafe impl bytemuck::Pod for Limb {}
241+
234242
#[cfg(test)]
235243
mod tests {
236244
#[cfg(feature = "alloc")]

src/traits.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,7 @@ pub trait Encoding: Sized {
616616
+ Copy
617617
+ Clone
618618
+ Sized
619-
+ for<'a> TryFrom<&'a [u8], Error = core::array::TryFromSliceError>;
619+
+ for<'a> TryFrom<&'a [u8], Error: core::error::Error>;
620620

621621
/// Decode from big endian bytes.
622622
fn from_be_bytes(bytes: Self::Repr) -> Self;

src/uint.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@ pub use extra_sizes::*;
1616
pub(crate) use ref_type::UintRef;
1717

1818
use crate::{
19-
Bounded, ConstChoice, ConstCtOption, ConstOne, ConstZero, Constants, Encoding, FixedInteger,
20-
Int, Integer, Limb, NonZero, Odd, One, Unsigned, Word, Zero, modular::MontyForm,
19+
Bounded, ConstChoice, ConstCtOption, ConstOne, ConstZero, Constants, FixedInteger, Int,
20+
Integer, Limb, NonZero, Odd, One, Unsigned, Word, Zero, modular::MontyForm,
2121
};
2222

23+
#[cfg(feature = "serde")]
24+
use crate::Encoding;
25+
2326
#[macro_use]
2427
mod macros;
2528

@@ -413,10 +416,10 @@ where
413416
where
414417
D: Deserializer<'de>,
415418
{
416-
let mut buffer = Self::ZERO.to_le_bytes();
419+
let mut buffer = Encoding::to_le_bytes(&Self::ZERO);
417420
serdect::array::deserialize_hex_or_bin(buffer.as_mut(), deserializer)?;
418421

419-
Ok(Self::from_le_bytes(buffer))
422+
Ok(Encoding::from_le_bytes(buffer))
420423
}
421424
}
422425

@@ -429,7 +432,7 @@ where
429432
where
430433
S: Serializer,
431434
{
432-
serdect::array::serialize_hex_lower_or_bin(&Encoding::to_le_bytes(self), serializer)
435+
serdect::slice::serialize_hex_lower_or_bin(&Encoding::to_le_bytes(self), serializer)
433436
}
434437
}
435438

@@ -603,7 +606,7 @@ mod tests {
603606
let be_bytes = a.to_be_bytes();
604607
let le_bytes = a.to_le_bytes();
605608
for i in 0..16 {
606-
assert_eq!(le_bytes[i], be_bytes[15 - i]);
609+
assert_eq!(le_bytes.as_ref()[i], be_bytes.as_ref()[15 - i]);
607610
}
608611

609612
let a_from_be = U128::from_be_bytes(be_bytes);

src/uint/div.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -872,10 +872,16 @@ mod tests {
872872
);
873873
let rem = U256::rem_wide(lo_hi, &modulus);
874874
// Lower half is zero
875-
assert_eq!(rem.to_be_bytes()[0..16], U128::ZERO.to_be_bytes());
875+
assert_eq!(
876+
&rem.to_be_bytes().as_ref()[0..16],
877+
U128::ZERO.to_be_bytes().as_ref()
878+
);
876879
// Upper half
877880
let expected = U128::from_be_hex("203F80FE03F80FE03F80FE03F80FE041");
878-
assert_eq!(rem.to_be_bytes()[16..], expected.to_be_bytes());
881+
assert_eq!(
882+
&rem.to_be_bytes().as_ref()[16..],
883+
expected.to_be_bytes().as_ref()
884+
);
879885

880886
let remv = U256::rem_wide_vartime(lo_hi, &modulus);
881887
assert_eq!(rem, remv);

src/uint/encoding.rs

Lines changed: 119 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,17 @@ mod der;
66
#[cfg(feature = "rlp")]
77
mod rlp;
88

9+
use core::fmt;
10+
911
#[cfg(feature = "alloc")]
1012
use alloc::{string::String, vec::Vec};
1113

1214
use super::Uint;
13-
use crate::{DecodeError, Limb, Word};
15+
use crate::{DecodeError, Encoding, Limb, Word};
1416

1517
#[cfg(feature = "alloc")]
1618
use crate::{ConstChoice, NonZero, Reciprocal, UintRef, WideWord};
1719

18-
#[cfg(feature = "hybrid-array")]
19-
use crate::Encoding;
20-
2120
#[cfg(feature = "alloc")]
2221
const RADIX_ENCODING_LIMBS_LARGE: usize = 16;
2322
#[cfg(feature = "alloc")]
@@ -204,58 +203,140 @@ impl<const LIMBS: usize> Uint<LIMBS> {
204203
let mut buf = *self;
205204
radix_encode_limbs_mut_to_string(radix, buf.as_mut_uint_ref())
206205
}
206+
207+
/// Serialize as big endian bytes.
208+
pub const fn to_be_bytes(&self) -> EncodedUint<LIMBS> {
209+
EncodedUint::new_be(self)
210+
}
211+
212+
/// Serialize as little endian bytes.
213+
pub const fn to_le_bytes(&self) -> EncodedUint<LIMBS> {
214+
EncodedUint::new_le(self)
215+
}
207216
}
208217

209-
/// Encode a [`Uint`] to a big endian byte array of the given size.
210-
pub(crate) const fn uint_to_be_bytes<const LIMBS: usize, const BYTES: usize>(
211-
uint: &Uint<LIMBS>,
212-
) -> [u8; BYTES] {
213-
if BYTES != LIMBS * Limb::BYTES {
214-
panic!("BYTES != LIMBS * Limb::BYTES");
218+
/// [`Uint`] encoded as bytes.
219+
// Until const generic expressions are stable, we cannot statically declare a `u8` array
220+
// of the size `LIMBS * Limb::BYTES`.
221+
// So instead we use the array of words, and treat it as an array of bytes.
222+
// It's a little hacky, but it works, because the array is guaranteed to be contiguous.
223+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
224+
pub struct EncodedUint<const LIMBS: usize>([Word; LIMBS]);
225+
226+
impl<const LIMBS: usize> Default for EncodedUint<LIMBS> {
227+
fn default() -> Self {
228+
Self([0; LIMBS])
215229
}
230+
}
216231

217-
let mut ret = [0u8; BYTES];
218-
let mut i = 0;
232+
impl<const LIMBS: usize> EncodedUint<LIMBS> {
233+
const fn new_le(value: &Uint<LIMBS>) -> Self {
234+
let mut buffer = [0; LIMBS];
235+
let mut i = 0;
219236

220-
while i < LIMBS {
221-
let limb_bytes = uint.limbs[LIMBS - i - 1].0.to_be_bytes();
222-
let mut j = 0;
237+
while i < LIMBS {
238+
let src_bytes = &value.limbs[i].0.to_le_bytes();
223239

224-
while j < Limb::BYTES {
225-
ret[i * Limb::BYTES + j] = limb_bytes[j];
226-
j += 1;
240+
// We could cast the whole `buffer` to bytes at once,
241+
// but IndexMut does not work in const context.
242+
let dst_bytes: &mut [u8] =
243+
bytemuck::must_cast_slice_mut(core::slice::from_mut(&mut buffer[i]));
244+
245+
// `copy_from_slice` can be used here when MSRV moves past 1.87
246+
let mut j = 0;
247+
while j < Limb::BYTES {
248+
dst_bytes[j] = src_bytes[j];
249+
j += 1;
250+
}
251+
252+
i += 1;
227253
}
254+
Self(buffer)
255+
}
228256

229-
i += 1;
257+
const fn new_be(value: &Uint<LIMBS>) -> Self {
258+
let mut buffer = [0; LIMBS];
259+
let mut i = 0;
260+
while i < LIMBS {
261+
let src_bytes = &value.limbs[i].0.to_be_bytes();
262+
263+
// We could cast the whole `buffer` to bytes at once,
264+
// but IndexMut does not work in const context.
265+
let dst_bytes: &mut [u8] =
266+
bytemuck::must_cast_slice_mut(core::slice::from_mut(&mut buffer[LIMBS - 1 - i]));
267+
268+
// `copy_from_slice` can be used here when MSRV moves past 1.87
269+
let mut j = 0;
270+
while j < Limb::BYTES {
271+
dst_bytes[j] = src_bytes[j];
272+
j += 1;
273+
}
274+
275+
i += 1;
276+
}
277+
Self(buffer)
230278
}
279+
}
231280

232-
ret
281+
impl<const LIMBS: usize> AsRef<[u8]> for EncodedUint<LIMBS> {
282+
fn as_ref(&self) -> &[u8] {
283+
bytemuck::must_cast_slice(&self.0)
284+
}
233285
}
234286

235-
/// Encode a [`Uint`] to a little endian byte array of the given size.
236-
pub(crate) const fn uint_to_le_bytes<const LIMBS: usize, const BYTES: usize>(
237-
uint: &Uint<LIMBS>,
238-
) -> [u8; BYTES] {
239-
if BYTES != LIMBS * Limb::BYTES {
240-
panic!("BYTES != LIMBS * Limb::BYTES");
287+
impl<const LIMBS: usize> AsMut<[u8]> for EncodedUint<LIMBS> {
288+
fn as_mut(&mut self) -> &mut [u8] {
289+
bytemuck::must_cast_slice_mut(&mut self.0)
241290
}
291+
}
242292

243-
let mut ret = [0u8; BYTES];
244-
let mut i = 0;
293+
/// Returned if an object cannot be instantiated from the given byte slice.
294+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
295+
pub struct TryFromSliceError;
245296

246-
while i < LIMBS {
247-
let limb_bytes = uint.limbs[i].0.to_le_bytes();
248-
let mut j = 0;
297+
impl fmt::Display for TryFromSliceError {
298+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
299+
write!(f, "TryFromSliceError")
300+
}
301+
}
249302

250-
while j < Limb::BYTES {
251-
ret[i * Limb::BYTES + j] = limb_bytes[j];
252-
j += 1;
303+
impl core::error::Error for TryFromSliceError {}
304+
305+
impl<'a, const LIMBS: usize> TryFrom<&'a [u8]> for EncodedUint<LIMBS> {
306+
type Error = TryFromSliceError;
307+
308+
fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
309+
if bytes.len() != Uint::<LIMBS>::BYTES {
310+
return Err(TryFromSliceError);
253311
}
312+
let mut result = Self::default();
313+
result.as_mut().copy_from_slice(bytes);
314+
Ok(result)
315+
}
316+
}
317+
318+
impl<const LIMBS: usize> Encoding for Uint<LIMBS> {
319+
type Repr = EncodedUint<LIMBS>;
320+
321+
#[inline]
322+
fn from_be_bytes(bytes: Self::Repr) -> Self {
323+
Self::from_be_slice(bytes.as_ref())
324+
}
254325

255-
i += 1;
326+
#[inline]
327+
fn from_le_bytes(bytes: Self::Repr) -> Self {
328+
Self::from_le_slice(bytes.as_ref())
329+
}
330+
331+
#[inline]
332+
fn to_be_bytes(&self) -> Self::Repr {
333+
self.to_be_bytes()
256334
}
257335

258-
ret
336+
#[inline]
337+
fn to_le_bytes(&self) -> Self::Repr {
338+
self.to_le_bytes()
339+
}
259340
}
260341

261342
/// Decode a single nibble of upper or lower hex
@@ -1057,7 +1138,7 @@ mod tests {
10571138
let n = UintEx::from_be_hex("0011223344556677");
10581139

10591140
let bytes = n.to_be_bytes();
1060-
assert_eq!(bytes, hex!("0011223344556677"));
1141+
assert_eq!(bytes.as_ref(), hex!("0011223344556677"));
10611142

10621143
#[cfg(feature = "der")]
10631144
assert_eq!(super::der::count_der_be_bytes(&n.limbs), 7);
@@ -1069,7 +1150,7 @@ mod tests {
10691150
let n = UintEx::from_be_hex("00112233445566778899aabbccddeeff");
10701151

10711152
let bytes = n.to_be_bytes();
1072-
assert_eq!(bytes, hex!("00112233445566778899aabbccddeeff"));
1153+
assert_eq!(bytes.as_ref(), hex!("00112233445566778899aabbccddeeff"));
10731154

10741155
#[cfg(feature = "der")]
10751156
assert_eq!(super::der::count_der_be_bytes(&n.limbs), 15);

src/uint/macros.rs

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,42 +7,6 @@ macro_rules! impl_uint_aliases {
77
#[doc = $doc]
88
#[doc="unsigned big integer."]
99
pub type $name = Uint<{ nlimbs!($bits) }>;
10-
11-
impl $name {
12-
/// Serialize as big endian bytes.
13-
pub const fn to_be_bytes(&self) -> [u8; $bits / 8] {
14-
encoding::uint_to_be_bytes::<{ nlimbs!($bits) }, { $bits / 8 }>(self)
15-
}
16-
17-
/// Serialize as little endian bytes.
18-
pub const fn to_le_bytes(&self) -> [u8; $bits / 8] {
19-
encoding::uint_to_le_bytes::<{ nlimbs!($bits) }, { $bits / 8 }>(self)
20-
}
21-
}
22-
23-
impl Encoding for $name {
24-
type Repr = [u8; $bits / 8];
25-
26-
#[inline]
27-
fn from_be_bytes(bytes: Self::Repr) -> Self {
28-
Self::from_be_slice(&bytes)
29-
}
30-
31-
#[inline]
32-
fn from_le_bytes(bytes: Self::Repr) -> Self {
33-
Self::from_le_slice(&bytes)
34-
}
35-
36-
#[inline]
37-
fn to_be_bytes(&self) -> Self::Repr {
38-
encoding::uint_to_be_bytes(self)
39-
}
40-
41-
#[inline]
42-
fn to_le_bytes(&self) -> Self::Repr {
43-
encoding::uint_to_le_bytes(self)
44-
}
45-
}
4610
)+
4711
};
4812
}

0 commit comments

Comments
 (0)