Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@
#[macro_use]
extern crate alloc;

pub use uint::encoding::{EncodedUint, TryFromSliceError};

#[cfg(feature = "rand_core")]
pub use rand_core;
#[cfg(feature = "rlp")]
Expand Down
2 changes: 1 addition & 1 deletion src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,7 @@ pub trait Encoding: Sized {
+ Copy
+ Clone
+ Sized
+ for<'a> TryFrom<&'a [u8], Error = core::array::TryFromSliceError>;
+ for<'a> TryFrom<&'a [u8], Error: core::error::Error>;

/// Decode from big endian bytes.
fn from_be_bytes(bytes: Self::Repr) -> Self;
Expand Down
15 changes: 9 additions & 6 deletions src/uint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@ pub use extra_sizes::*;
pub(crate) use ref_type::UintRef;

use crate::{
Bounded, ConstChoice, ConstCtOption, ConstOne, ConstZero, Constants, Encoding, FixedInteger,
Int, Integer, Limb, NonZero, Odd, One, Unsigned, Word, Zero, modular::MontyForm,
Bounded, ConstChoice, ConstCtOption, ConstOne, ConstZero, Constants, FixedInteger, Int,
Integer, Limb, NonZero, Odd, One, Unsigned, Word, Zero, modular::MontyForm,
};

#[cfg(feature = "serde")]
use crate::Encoding;

#[macro_use]
mod macros;

Expand Down Expand Up @@ -413,10 +416,10 @@ where
where
D: Deserializer<'de>,
{
let mut buffer = Self::ZERO.to_le_bytes();
let mut buffer = Encoding::to_le_bytes(&Self::ZERO);
serdect::array::deserialize_hex_or_bin(buffer.as_mut(), deserializer)?;

Ok(Self::from_le_bytes(buffer))
Ok(Encoding::from_le_bytes(buffer))
}
}

Expand All @@ -429,7 +432,7 @@ where
where
S: Serializer,
{
serdect::array::serialize_hex_lower_or_bin(&Encoding::to_le_bytes(self), serializer)
serdect::slice::serialize_hex_lower_or_bin(&Encoding::to_le_bytes(self), serializer)
}
}

Expand Down Expand Up @@ -655,7 +658,7 @@ mod tests {
let be_bytes = a.to_be_bytes();
let le_bytes = a.to_le_bytes();
for i in 0..16 {
assert_eq!(le_bytes[i], be_bytes[15 - i]);
assert_eq!(le_bytes.as_ref()[i], be_bytes.as_ref()[15 - i]);
}

let a_from_be = U128::from_be_bytes(be_bytes);
Expand Down
10 changes: 8 additions & 2 deletions src/uint/div.rs
Original file line number Diff line number Diff line change
Expand Up @@ -873,10 +873,16 @@ mod tests {
);
let rem = U256::rem_wide(lo_hi, &modulus);
// Lower half is zero
assert_eq!(rem.to_be_bytes()[0..16], U128::ZERO.to_be_bytes());
assert_eq!(
&rem.to_be_bytes().as_ref()[0..16],
U128::ZERO.to_be_bytes().as_ref()
);
// Upper half
let expected = U128::from_be_hex("203F80FE03F80FE03F80FE03F80FE041");
assert_eq!(rem.to_be_bytes()[16..], expected.to_be_bytes());
assert_eq!(
&rem.to_be_bytes().as_ref()[16..],
expected.to_be_bytes().as_ref()
);

let remv = U256::rem_wide_vartime(lo_hi, &modulus);
assert_eq!(rem, remv);
Expand Down
175 changes: 137 additions & 38 deletions src/uint/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,17 @@ mod der;
#[cfg(feature = "rlp")]
mod rlp;

use core::{fmt, ops::Deref};

#[cfg(feature = "alloc")]
use alloc::{string::String, vec::Vec};

use super::Uint;
use crate::{DecodeError, Limb, Word};
use crate::{DecodeError, Encoding, Limb, Word};

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

#[cfg(feature = "hybrid-array")]
use crate::Encoding;

#[cfg(feature = "alloc")]
const RADIX_ENCODING_LIMBS_LARGE: usize = 16;
#[cfg(feature = "alloc")]
Expand Down Expand Up @@ -204,58 +203,158 @@ impl<const LIMBS: usize> Uint<LIMBS> {
let mut buf = *self;
radix_encode_limbs_mut_to_string(radix, buf.as_mut_uint_ref())
}

/// Serialize as big endian bytes.
pub const fn to_be_bytes(&self) -> EncodedUint<LIMBS> {
EncodedUint::new_be(self)
}

/// Serialize as little endian bytes.
pub const fn to_le_bytes(&self) -> EncodedUint<LIMBS> {
EncodedUint::new_le(self)
}
}

/// [`Uint`] encoded as bytes.
// Until const generic expressions are stable, we cannot statically declare a `u8` array
// of the size `LIMBS * Limb::BYTES`.
// So instead we use the array of words, and treat it as an array of bytes.
// It's a little hacky, but it works, because the array is guaranteed to be contiguous.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct EncodedUint<const LIMBS: usize>([Word; LIMBS]);

#[allow(unsafe_code)]
const fn cast_slice(limbs: &[Word]) -> &[u8] {
let new_len = size_of_val(limbs);
unsafe { core::slice::from_raw_parts(limbs.as_ptr() as *mut u8, new_len) }
}

/// Encode a [`Uint`] to a big endian byte array of the given size.
pub(crate) const fn uint_to_be_bytes<const LIMBS: usize, const BYTES: usize>(
uint: &Uint<LIMBS>,
) -> [u8; BYTES] {
if BYTES != LIMBS * Limb::BYTES {
panic!("BYTES != LIMBS * Limb::BYTES");
#[allow(unsafe_code)]
const fn cast_slice_mut(limbs: &mut [Word]) -> &mut [u8] {
let new_len = size_of_val(limbs);
unsafe { core::slice::from_raw_parts_mut(limbs.as_mut_ptr() as *mut u8, new_len) }
}

impl<const LIMBS: usize> EncodedUint<LIMBS> {
const fn new_le(value: &Uint<LIMBS>) -> Self {
let mut buffer = [0; LIMBS];
let mut i = 0;

while i < LIMBS {
let src_bytes = &value.limbs[i].0.to_le_bytes();

// We could cast the whole `buffer` to bytes at once,
// but IndexMut does not work in const context.
let dst_bytes: &mut [u8] = cast_slice_mut(core::slice::from_mut(&mut buffer[i]));

// `copy_from_slice` can be used here when MSRV moves past 1.87
let mut j = 0;
while j < Limb::BYTES {
dst_bytes[j] = src_bytes[j];
j += 1;
}

i += 1;
}
Self(buffer)
}

let mut ret = [0u8; BYTES];
let mut i = 0;
const fn new_be(value: &Uint<LIMBS>) -> Self {
let mut buffer = [0; LIMBS];
let mut i = 0;
while i < LIMBS {
let src_bytes = &value.limbs[i].0.to_be_bytes();

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

while j < Limb::BYTES {
ret[i * Limb::BYTES + j] = limb_bytes[j];
j += 1;
// `copy_from_slice` can be used here when MSRV moves past 1.87
let mut j = 0;
while j < Limb::BYTES {
dst_bytes[j] = src_bytes[j];
j += 1;
}

i += 1;
}
Self(buffer)
}
}

i += 1;
impl<const LIMBS: usize> Default for EncodedUint<LIMBS> {
fn default() -> Self {
Self([0; LIMBS])
}
}

ret
impl<const LIMBS: usize> AsRef<[u8]> for EncodedUint<LIMBS> {
fn as_ref(&self) -> &[u8] {
cast_slice(&self.0)
}
}

/// Encode a [`Uint`] to a little endian byte array of the given size.
pub(crate) const fn uint_to_le_bytes<const LIMBS: usize, const BYTES: usize>(
uint: &Uint<LIMBS>,
) -> [u8; BYTES] {
if BYTES != LIMBS * Limb::BYTES {
panic!("BYTES != LIMBS * Limb::BYTES");
impl<const LIMBS: usize> AsMut<[u8]> for EncodedUint<LIMBS> {
fn as_mut(&mut self) -> &mut [u8] {
cast_slice_mut(&mut self.0)
}
}

let mut ret = [0u8; BYTES];
let mut i = 0;
impl<const LIMBS: usize> Deref for EncodedUint<LIMBS> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}

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

while j < Limb::BYTES {
ret[i * Limb::BYTES + j] = limb_bytes[j];
j += 1;
impl fmt::Display for TryFromSliceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "TryFromSliceError")
}
}

impl core::error::Error for TryFromSliceError {}

impl<'a, const LIMBS: usize> TryFrom<&'a [u8]> for EncodedUint<LIMBS> {
type Error = TryFromSliceError;

fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
if bytes.len() != Uint::<LIMBS>::BYTES {
return Err(TryFromSliceError);
}
let mut result = Self::default();
result.as_mut().copy_from_slice(bytes);
Ok(result)
}
}

impl<const LIMBS: usize> Encoding for Uint<LIMBS> {
type Repr = EncodedUint<LIMBS>;

#[inline]
fn from_be_bytes(bytes: Self::Repr) -> Self {
Self::from_be_slice(bytes.as_ref())
}

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

ret
#[inline]
fn to_be_bytes(&self) -> Self::Repr {
self.to_be_bytes()
}

#[inline]
fn to_le_bytes(&self) -> Self::Repr {
self.to_le_bytes()
}
}

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

let bytes = n.to_be_bytes();
assert_eq!(bytes, hex!("0011223344556677"));
assert_eq!(bytes.as_ref(), hex!("0011223344556677"));

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

let bytes = n.to_be_bytes();
assert_eq!(bytes, hex!("00112233445566778899aabbccddeeff"));
assert_eq!(bytes.as_ref(), hex!("00112233445566778899aabbccddeeff"));

#[cfg(feature = "der")]
assert_eq!(super::der::count_der_be_bytes(&n.limbs), 15);
Expand Down
36 changes: 0 additions & 36 deletions src/uint/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,42 +7,6 @@ macro_rules! impl_uint_aliases {
#[doc = $doc]
#[doc="unsigned big integer."]
pub type $name = Uint<{ nlimbs!($bits) }>;

impl $name {
/// Serialize as big endian bytes.
pub const fn to_be_bytes(&self) -> [u8; $bits / 8] {
encoding::uint_to_be_bytes::<{ nlimbs!($bits) }, { $bits / 8 }>(self)
}

/// Serialize as little endian bytes.
pub const fn to_le_bytes(&self) -> [u8; $bits / 8] {
encoding::uint_to_le_bytes::<{ nlimbs!($bits) }, { $bits / 8 }>(self)
}
}

impl Encoding for $name {
type Repr = [u8; $bits / 8];

#[inline]
fn from_be_bytes(bytes: Self::Repr) -> Self {
Self::from_be_slice(&bytes)
}

#[inline]
fn from_le_bytes(bytes: Self::Repr) -> Self {
Self::from_le_slice(&bytes)
}

#[inline]
fn to_be_bytes(&self) -> Self::Repr {
encoding::uint_to_be_bytes(self)
}

#[inline]
fn to_le_bytes(&self) -> Self::Repr {
encoding::uint_to_le_bytes(self)
}
}
)+
};
}
Expand Down
Loading