Skip to content

Commit ecb405d

Browse files
committed
Implement Encoding for all Uints
1 parent 5784b13 commit ecb405d

8 files changed

Lines changed: 169 additions & 95 deletions

File tree

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/traits.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,7 @@ pub trait Encoding: Sized {
644644
+ Copy
645645
+ Clone
646646
+ Sized
647-
+ for<'a> TryFrom<&'a [u8], Error = core::array::TryFromSliceError>;
647+
+ for<'a> TryFrom<&'a [u8], Error: core::error::Error>;
648648

649649
/// Decode from big endian bytes.
650650
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

@@ -655,7 +658,7 @@ mod tests {
655658
let be_bytes = a.to_be_bytes();
656659
let le_bytes = a.to_le_bytes();
657660
for i in 0..16 {
658-
assert_eq!(le_bytes[i], be_bytes[15 - i]);
661+
assert_eq!(le_bytes.as_ref()[i], be_bytes.as_ref()[15 - i]);
659662
}
660663

661664
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
@@ -873,10 +873,16 @@ mod tests {
873873
);
874874
let rem = U256::rem_wide(lo_hi, &modulus);
875875
// Lower half is zero
876-
assert_eq!(rem.to_be_bytes()[0..16], U128::ZERO.to_be_bytes());
876+
assert_eq!(
877+
&rem.to_be_bytes().as_ref()[0..16],
878+
U128::ZERO.to_be_bytes().as_ref()
879+
);
877880
// Upper half
878881
let expected = U128::from_be_hex("203F80FE03F80FE03F80FE03F80FE041");
879-
assert_eq!(rem.to_be_bytes()[16..], expected.to_be_bytes());
882+
assert_eq!(
883+
&rem.to_be_bytes().as_ref()[16..],
884+
expected.to_be_bytes().as_ref()
885+
);
880886

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

src/uint/encoding.rs

Lines changed: 137 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, ops::Deref};
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,158 @@ 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+
}
216+
}
217+
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+
#[allow(unsafe_code)]
227+
const fn cast_slice(limbs: &[Word]) -> &[u8] {
228+
let new_len = size_of_val(limbs);
229+
unsafe { core::slice::from_raw_parts(limbs.as_ptr() as *mut u8, new_len) }
207230
}
208231

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");
232+
#[allow(unsafe_code)]
233+
const fn cast_slice_mut(limbs: &mut [Word]) -> &mut [u8] {
234+
let new_len = size_of_val(limbs);
235+
unsafe { core::slice::from_raw_parts_mut(limbs.as_mut_ptr() as *mut u8, new_len) }
236+
}
237+
238+
impl<const LIMBS: usize> EncodedUint<LIMBS> {
239+
const fn new_le(value: &Uint<LIMBS>) -> Self {
240+
let mut buffer = [0; LIMBS];
241+
let mut i = 0;
242+
243+
while i < LIMBS {
244+
let src_bytes = &value.limbs[i].0.to_le_bytes();
245+
246+
// We could cast the whole `buffer` to bytes at once,
247+
// but IndexMut does not work in const context.
248+
let dst_bytes: &mut [u8] = cast_slice_mut(core::slice::from_mut(&mut buffer[i]));
249+
250+
// `copy_from_slice` can be used here when MSRV moves past 1.87
251+
let mut j = 0;
252+
while j < Limb::BYTES {
253+
dst_bytes[j] = src_bytes[j];
254+
j += 1;
255+
}
256+
257+
i += 1;
258+
}
259+
Self(buffer)
215260
}
216261

217-
let mut ret = [0u8; BYTES];
218-
let mut i = 0;
262+
const fn new_be(value: &Uint<LIMBS>) -> Self {
263+
let mut buffer = [0; LIMBS];
264+
let mut i = 0;
265+
while i < LIMBS {
266+
let src_bytes = &value.limbs[i].0.to_be_bytes();
219267

220-
while i < LIMBS {
221-
let limb_bytes = uint.limbs[LIMBS - i - 1].0.to_be_bytes();
222-
let mut j = 0;
268+
// We could cast the whole `buffer` to bytes at once,
269+
// but IndexMut does not work in const context.
270+
let dst_bytes: &mut [u8] =
271+
cast_slice_mut(core::slice::from_mut(&mut buffer[LIMBS - 1 - i]));
223272

224-
while j < Limb::BYTES {
225-
ret[i * Limb::BYTES + j] = limb_bytes[j];
226-
j += 1;
273+
// `copy_from_slice` can be used here when MSRV moves past 1.87
274+
let mut j = 0;
275+
while j < Limb::BYTES {
276+
dst_bytes[j] = src_bytes[j];
277+
j += 1;
278+
}
279+
280+
i += 1;
227281
}
282+
Self(buffer)
283+
}
284+
}
228285

229-
i += 1;
286+
impl<const LIMBS: usize> Default for EncodedUint<LIMBS> {
287+
fn default() -> Self {
288+
Self([0; LIMBS])
230289
}
290+
}
231291

232-
ret
292+
impl<const LIMBS: usize> AsRef<[u8]> for EncodedUint<LIMBS> {
293+
fn as_ref(&self) -> &[u8] {
294+
cast_slice(&self.0)
295+
}
233296
}
234297

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");
298+
impl<const LIMBS: usize> AsMut<[u8]> for EncodedUint<LIMBS> {
299+
fn as_mut(&mut self) -> &mut [u8] {
300+
cast_slice_mut(&mut self.0)
241301
}
302+
}
242303

243-
let mut ret = [0u8; BYTES];
244-
let mut i = 0;
304+
impl<const LIMBS: usize> Deref for EncodedUint<LIMBS> {
305+
type Target = [u8];
306+
fn deref(&self) -> &Self::Target {
307+
self.as_ref()
308+
}
309+
}
245310

246-
while i < LIMBS {
247-
let limb_bytes = uint.limbs[i].0.to_le_bytes();
248-
let mut j = 0;
311+
/// Returned if an object cannot be instantiated from the given byte slice.
312+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
313+
pub struct TryFromSliceError;
249314

250-
while j < Limb::BYTES {
251-
ret[i * Limb::BYTES + j] = limb_bytes[j];
252-
j += 1;
315+
impl fmt::Display for TryFromSliceError {
316+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
317+
write!(f, "TryFromSliceError")
318+
}
319+
}
320+
321+
impl core::error::Error for TryFromSliceError {}
322+
323+
impl<'a, const LIMBS: usize> TryFrom<&'a [u8]> for EncodedUint<LIMBS> {
324+
type Error = TryFromSliceError;
325+
326+
fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
327+
if bytes.len() != Uint::<LIMBS>::BYTES {
328+
return Err(TryFromSliceError);
253329
}
330+
let mut result = Self::default();
331+
result.as_mut().copy_from_slice(bytes);
332+
Ok(result)
333+
}
334+
}
335+
336+
impl<const LIMBS: usize> Encoding for Uint<LIMBS> {
337+
type Repr = EncodedUint<LIMBS>;
338+
339+
#[inline]
340+
fn from_be_bytes(bytes: Self::Repr) -> Self {
341+
Self::from_be_slice(bytes.as_ref())
342+
}
254343

255-
i += 1;
344+
#[inline]
345+
fn from_le_bytes(bytes: Self::Repr) -> Self {
346+
Self::from_le_slice(bytes.as_ref())
256347
}
257348

258-
ret
349+
#[inline]
350+
fn to_be_bytes(&self) -> Self::Repr {
351+
self.to_be_bytes()
352+
}
353+
354+
#[inline]
355+
fn to_le_bytes(&self) -> Self::Repr {
356+
self.to_le_bytes()
357+
}
259358
}
260359

261360
/// Decode a single nibble of upper or lower hex
@@ -1057,7 +1156,7 @@ mod tests {
10571156
let n = UintEx::from_be_hex("0011223344556677");
10581157

10591158
let bytes = n.to_be_bytes();
1060-
assert_eq!(bytes, hex!("0011223344556677"));
1159+
assert_eq!(bytes.as_ref(), hex!("0011223344556677"));
10611160

10621161
#[cfg(feature = "der")]
10631162
assert_eq!(super::der::count_der_be_bytes(&n.limbs), 7);
@@ -1069,7 +1168,7 @@ mod tests {
10691168
let n = UintEx::from_be_hex("00112233445566778899aabbccddeeff");
10701169

10711170
let bytes = n.to_be_bytes();
1072-
assert_eq!(bytes, hex!("00112233445566778899aabbccddeeff"));
1171+
assert_eq!(bytes.as_ref(), hex!("00112233445566778899aabbccddeeff"));
10731172

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