Skip to content

Commit 36ed440

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

11 files changed

Lines changed: 157 additions & 100 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: 109 additions & 38 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,132 @@ 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
}
204+
205+
/// Serialize as big endian bytes.
206+
pub const fn to_be_bytes(&self) -> EncodedUint<LIMBS> {
207+
EncodedUint::new_be(self)
208+
}
209+
210+
/// Serialize as little endian bytes.
211+
pub const fn to_le_bytes(&self) -> EncodedUint<LIMBS> {
212+
EncodedUint::new_le(self)
213+
}
207214
}
208215

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");
216+
/// [`Uint`] encoded as bytes.
217+
// Until const generic expressions are stable, we cannot statically declare a `u8` array
218+
// of the size `LIMBS * Limb::BYTES`.
219+
// So instead we use the array of words, and treat it as an array of bytes.
220+
// It's a little hacky, but it works, because the array is guaranteed to be contiguous.
221+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
222+
pub struct EncodedUint<const LIMBS: usize>([Word; LIMBS]);
223+
224+
impl<const LIMBS: usize> Default for EncodedUint<LIMBS> {
225+
fn default() -> Self {
226+
Self([0; LIMBS])
215227
}
228+
}
216229

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

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

224-
while j < Limb::BYTES {
225-
ret[i * Limb::BYTES + j] = limb_bytes[j];
226-
j += 1;
238+
// We could cast the whole `buffer` to bytes at once,
239+
// but IndexMut does not work in const context.
240+
let dst_bytes: &mut [u8] =
241+
bytemuck::must_cast_slice_mut(core::slice::from_mut(&mut buffer[i]));
242+
243+
// `copy_from_slice` can be used here when MSRV moves past 1.87
244+
let mut j = 0;
245+
while j < Limb::BYTES {
246+
dst_bytes[j] = src_bytes[j];
247+
j += 1;
248+
}
249+
250+
i += 1;
227251
}
252+
Self(buffer)
253+
}
254+
255+
const fn new_be(value: &Uint<LIMBS>) -> Self {
256+
let mut buffer = [0; LIMBS];
257+
let mut i = 0;
258+
while i < LIMBS {
259+
let src_bytes = &value.limbs[i].0.to_be_bytes();
260+
261+
// We could cast the whole `buffer` to bytes at once,
262+
// but IndexMut does not work in const context.
263+
let dst_bytes: &mut [u8] =
264+
bytemuck::must_cast_slice_mut(core::slice::from_mut(&mut buffer[LIMBS - 1 - i]));
265+
266+
// `copy_from_slice` can be used here when MSRV moves past 1.87
267+
let mut j = 0;
268+
while j < Limb::BYTES {
269+
dst_bytes[j] = src_bytes[j];
270+
j += 1;
271+
}
228272

229-
i += 1;
273+
i += 1;
274+
}
275+
Self(buffer)
230276
}
277+
}
231278

232-
ret
279+
impl<const LIMBS: usize> AsRef<[u8]> for EncodedUint<LIMBS> {
280+
fn as_ref(&self) -> &[u8] {
281+
bytemuck::must_cast_slice(&self.0)
282+
}
233283
}
234284

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");
285+
impl<const LIMBS: usize> AsMut<[u8]> for EncodedUint<LIMBS> {
286+
fn as_mut(&mut self) -> &mut [u8] {
287+
bytemuck::must_cast_slice_mut(&mut self.0)
241288
}
289+
}
242290

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

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

250-
while j < Limb::BYTES {
251-
ret[i * Limb::BYTES + j] = limb_bytes[j];
252-
j += 1;
298+
fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
299+
if bytes.len() != Uint::<LIMBS>::BYTES {
300+
return Err(TryFromSliceError);
253301
}
302+
let mut result = Self::default();
303+
result.as_mut().copy_from_slice(bytes);
304+
Ok(result)
305+
}
306+
}
307+
308+
impl<const LIMBS: usize> Encoding for Uint<LIMBS> {
309+
type Repr = EncodedUint<LIMBS>;
310+
311+
#[inline]
312+
fn from_be_bytes(bytes: Self::Repr) -> Self {
313+
Self::from_be_slice(bytes.as_ref())
314+
}
254315

255-
i += 1;
316+
#[inline]
317+
fn from_le_bytes(bytes: Self::Repr) -> Self {
318+
Self::from_le_slice(bytes.as_ref())
256319
}
257320

258-
ret
321+
#[inline]
322+
fn to_be_bytes(&self) -> Self::Repr {
323+
self.to_be_bytes()
324+
}
325+
326+
#[inline]
327+
fn to_le_bytes(&self) -> Self::Repr {
328+
self.to_le_bytes()
329+
}
259330
}
260331

261332
/// Decode a single nibble of upper or lower hex
@@ -1057,7 +1128,7 @@ mod tests {
10571128
let n = UintEx::from_be_hex("0011223344556677");
10581129

10591130
let bytes = n.to_be_bytes();
1060-
assert_eq!(bytes, hex!("0011223344556677"));
1131+
assert_eq!(bytes.as_ref(), hex!("0011223344556677"));
10611132

10621133
#[cfg(feature = "der")]
10631134
assert_eq!(super::der::count_der_be_bytes(&n.limbs), 7);
@@ -1069,7 +1140,7 @@ mod tests {
10691140
let n = UintEx::from_be_hex("00112233445566778899aabbccddeeff");
10701141

10711142
let bytes = n.to_be_bytes();
1072-
assert_eq!(bytes, hex!("00112233445566778899aabbccddeeff"));
1143+
assert_eq!(bytes.as_ref(), hex!("00112233445566778899aabbccddeeff"));
10731144

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