Skip to content

Commit a761bce

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

11 files changed

Lines changed: 149 additions & 101 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 & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -611,12 +611,7 @@ pub trait Split<Lo, Hi = Lo> {
611611
/// Encoding support.
612612
pub trait Encoding: Sized {
613613
/// Byte array representation.
614-
type Repr: AsRef<[u8]>
615-
+ AsMut<[u8]>
616-
+ Copy
617-
+ Clone
618-
+ Sized
619-
+ for<'a> TryFrom<&'a [u8], Error = core::array::TryFromSliceError>;
614+
type Repr: AsRef<[u8]> + AsMut<[u8]> + Copy + Clone + Sized + for<'a> TryFrom<&'a [u8]>;
620615

621616
/// Decode from big endian bytes.
622617
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: 101 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,11 @@ mod rlp;
1010
use alloc::{string::String, vec::Vec};
1111

1212
use super::Uint;
13-
use crate::{DecodeError, Limb, Word};
13+
use crate::{DecodeError, Encoding, Limb, Word};
1414

1515
#[cfg(feature = "alloc")]
1616
use crate::{ConstChoice, NonZero, Reciprocal, UintRef, WideWord};
1717

18-
#[cfg(feature = "hybrid-array")]
19-
use crate::Encoding;
20-
2118
#[cfg(feature = "alloc")]
2219
const RADIX_ENCODING_LIMBS_LARGE: usize = 16;
2320
#[cfg(feature = "alloc")]
@@ -204,58 +201,123 @@ impl<const LIMBS: usize> Uint<LIMBS> {
204201
let mut buf = *self;
205202
radix_encode_limbs_mut_to_string(radix, buf.as_mut_uint_ref())
206203
}
207-
}
208204

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");
205+
/// Serialize as big endian bytes.
206+
pub const fn to_be_bytes(&self) -> EncodedUint<LIMBS> {
207+
EncodedUint::as_be_bytes(self)
215208
}
216209

217-
let mut ret = [0u8; BYTES];
218-
let mut i = 0;
210+
/// Serialize as little endian bytes.
211+
pub const fn to_le_bytes(&self) -> EncodedUint<LIMBS> {
212+
EncodedUint::as_le_bytes(self)
213+
}
214+
}
219215

220-
while i < LIMBS {
221-
let limb_bytes = uint.limbs[LIMBS - i - 1].0.to_be_bytes();
222-
let mut j = 0;
216+
/// [`Uint`] encoded as bytes.
217+
#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)]
218+
pub struct EncodedUint<const LIMBS: usize>(Uint<LIMBS>);
223219

224-
while j < Limb::BYTES {
225-
ret[i * Limb::BYTES + j] = limb_bytes[j];
226-
j += 1;
220+
impl<const LIMBS: usize> EncodedUint<LIMBS> {
221+
const fn as_le_bytes(value: &Uint<LIMBS>) -> Self {
222+
let mut buffer = *value;
223+
let mut i = 0;
224+
225+
while i < LIMBS {
226+
let src_bytes = &value.limbs[i].0.to_le_bytes();
227+
228+
// We could cast the whole `buffer.limbs` to bytes at once,
229+
// but IndexMut does not work in const context.
230+
let dst_bytes: &mut [u8] =
231+
bytemuck::must_cast_slice_mut(core::slice::from_mut(&mut buffer.limbs[i].0));
232+
233+
// `copy_from_slice` can be used here when MSRV moves past 1.87
234+
let mut j = 0;
235+
while j < Limb::BYTES {
236+
dst_bytes[j] = src_bytes[j];
237+
j += 1;
238+
}
239+
240+
i += 1;
227241
}
242+
Self(buffer)
243+
}
244+
245+
const fn as_be_bytes(value: &Uint<LIMBS>) -> Self {
246+
let mut buffer = *value;
247+
let mut i = 0;
248+
while i < LIMBS {
249+
let src_bytes = &value.limbs[i].0.to_be_bytes();
250+
251+
// We could cast the whole `buffer.limbs` to bytes at once,
252+
// but IndexMut does not work in const context.
253+
let dst_bytes: &mut [u8] = bytemuck::must_cast_slice_mut(core::slice::from_mut(
254+
&mut buffer.limbs[LIMBS - 1 - i].0,
255+
));
256+
257+
// `copy_from_slice` can be used here when MSRV moves past 1.87
258+
let mut j = 0;
259+
while j < Limb::BYTES {
260+
dst_bytes[j] = src_bytes[j];
261+
j += 1;
262+
}
228263

229-
i += 1;
264+
i += 1;
265+
}
266+
Self(buffer)
230267
}
268+
}
231269

232-
ret
270+
impl<const LIMBS: usize> AsRef<[u8]> for EncodedUint<LIMBS> {
271+
fn as_ref(&self) -> &[u8] {
272+
bytemuck::must_cast_slice(&self.0.limbs)
273+
}
233274
}
234275

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");
276+
impl<const LIMBS: usize> AsMut<[u8]> for EncodedUint<LIMBS> {
277+
fn as_mut(&mut self) -> &mut [u8] {
278+
bytemuck::must_cast_slice_mut(&mut self.0.limbs)
241279
}
280+
}
242281

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

246-
while i < LIMBS {
247-
let limb_bytes = uint.limbs[i].0.to_le_bytes();
248-
let mut j = 0;
286+
impl<'a, const LIMBS: usize> TryFrom<&'a [u8]> for EncodedUint<LIMBS> {
287+
type Error = TryFromSliceError;
249288

250-
while j < Limb::BYTES {
251-
ret[i * Limb::BYTES + j] = limb_bytes[j];
252-
j += 1;
289+
fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
290+
if bytes.len() != Uint::<LIMBS>::BYTES {
291+
return Err(TryFromSliceError);
253292
}
293+
let mut result = Self(Uint::<LIMBS>::ZERO);
294+
result.as_mut().copy_from_slice(bytes);
295+
Ok(result)
296+
}
297+
}
298+
299+
impl<const LIMBS: usize> Encoding for Uint<LIMBS> {
300+
type Repr = EncodedUint<LIMBS>;
301+
302+
#[inline]
303+
fn from_be_bytes(bytes: Self::Repr) -> Self {
304+
Self::from_be_slice(bytes.as_ref())
305+
}
254306

255-
i += 1;
307+
#[inline]
308+
fn from_le_bytes(bytes: Self::Repr) -> Self {
309+
Self::from_le_slice(bytes.as_ref())
256310
}
257311

258-
ret
312+
#[inline]
313+
fn to_be_bytes(&self) -> Self::Repr {
314+
self.to_be_bytes()
315+
}
316+
317+
#[inline]
318+
fn to_le_bytes(&self) -> Self::Repr {
319+
self.to_le_bytes()
320+
}
259321
}
260322

261323
/// Decode a single nibble of upper or lower hex
@@ -1057,7 +1119,7 @@ mod tests {
10571119
let n = UintEx::from_be_hex("0011223344556677");
10581120

10591121
let bytes = n.to_be_bytes();
1060-
assert_eq!(bytes, hex!("0011223344556677"));
1122+
assert_eq!(bytes.as_ref(), hex!("0011223344556677"));
10611123

10621124
#[cfg(feature = "der")]
10631125
assert_eq!(super::der::count_der_be_bytes(&n.limbs), 7);
@@ -1069,7 +1131,7 @@ mod tests {
10691131
let n = UintEx::from_be_hex("00112233445566778899aabbccddeeff");
10701132

10711133
let bytes = n.to_be_bytes();
1072-
assert_eq!(bytes, hex!("00112233445566778899aabbccddeeff"));
1134+
assert_eq!(bytes.as_ref(), hex!("00112233445566778899aabbccddeeff"));
10731135

10741136
#[cfg(feature = "der")]
10751137
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)