Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
18 changes: 15 additions & 3 deletions src/limb/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,28 @@ impl Encoding for Limb {
impl Limb {
/// Decode limb from a big endian byte slice.
///
/// Panics if the slice is not the same size as [`Limb::Repr`].
/// Panics if the slice is larger than [`Limb::Repr`].
pub(crate) fn from_be_slice(bytes: &[u8]) -> Self {
Self::from_be_bytes(bytes.try_into().expect("slice not limb-sized"))
let mut repr = Self::ZERO.to_be_bytes();
let repr_len = repr.len();
if bytes.len() > repr_len {
panic!("The given slice is larger than the limb size");
}
repr[(repr_len - bytes.len())..].copy_from_slice(bytes);
Self::from_be_bytes(repr)
}

/// Decode limb from a little endian byte slice.
///
/// Panics if the slice is not the same size as [`Limb::Repr`].
pub(crate) fn from_le_slice(bytes: &[u8]) -> Self {
Self::from_le_bytes(bytes.try_into().expect("slice not limb-sized"))
let mut repr = Self::ZERO.to_le_bytes();
let repr_len = repr.len();
if bytes.len() > repr_len {
panic!("The given slice is larger than the limb size");
}
repr[..bytes.len()].copy_from_slice(bytes);
Self::from_le_bytes(repr)
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ macro_rules! const_assert_ne {
#[macro_export]
macro_rules! nlimbs {
($bits:expr) => {
$bits / $crate::Limb::BITS as usize
(($bits + $crate::Limb::BITS - 1) / $crate::Limb::BITS) as usize
};
}

Expand Down
59 changes: 23 additions & 36 deletions src/uint/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ pub struct BoxedUint {
}

impl BoxedUint {
fn limbs_for_precision(at_least_bits_precision: u32) -> usize {
((at_least_bits_precision + Limb::BITS - 1) / Limb::BITS) as usize
}

/// Get the value `0` represented as succinctly as possible.
pub fn zero() -> Self {
Self {
Expand All @@ -59,15 +63,9 @@ impl BoxedUint {

/// Get the value `0` with the given number of bits of precision.
///
/// Panics if the precision is not a multiple of [`Limb::BITS`].
pub fn zero_with_precision(bits_precision: u32) -> Self {
assert_eq!(
bits_precision % Limb::BITS,
0,
"precision is not a multiple of limb size"
);

vec![Limb::ZERO; (bits_precision / Limb::BITS) as usize].into()
/// `at_least_bits_precision` is rounded up to a multiple of [`Limb::BITS`].
pub fn zero_with_precision(at_least_bits_precision: u32) -> Self {
vec![Limb::ZERO; Self::limbs_for_precision(at_least_bits_precision)].into()
}

/// Get the value `1`, represented as succinctly as possible.
Expand All @@ -79,11 +77,9 @@ impl BoxedUint {

/// Get the value `1` with the given number of bits of precision.
///
/// Panics if the precision is not at least [`Limb::BITS`] or if it is not
/// a multiple thereof.
pub fn one_with_precision(bits_precision: u32) -> Self {
assert!(bits_precision >= Limb::BITS, "precision too small");
let mut ret = Self::zero_with_precision(bits_precision);
/// `at_least_bits_precision` is rounded up to a multiple of [`Limb::BITS`].
pub fn one_with_precision(at_least_bits_precision: u32) -> Self {
let mut ret = Self::zero_with_precision(at_least_bits_precision);
ret.limbs[0] = Limb::ONE;
ret
}
Expand Down Expand Up @@ -125,17 +121,12 @@ impl BoxedUint {
!self.is_odd()
}

/// Get the maximum value for a given number of bits of precision.
/// Get the maximum value for a `BoxedUint` created with `at_least_bits_precision`
/// precision bits requested.
///
/// Panics if the precision is not a multiple of [`Limb::BITS`].
pub fn max(bits_precision: u32) -> Self {
assert_eq!(
bits_precision % Limb::BITS,
0,
"precision is not a multiple of limb size"
);

vec![Limb::MAX; (bits_precision / Limb::BITS) as usize].into()
/// That is, returns the value `2^self.bits_precision() - 1`.
pub fn max(at_least_bits_precision: u32) -> Self {
vec![Limb::MAX; Self::limbs_for_precision(at_least_bits_precision)].into()
}

/// Create a [`BoxedUint`] from an array of [`Word`]s (i.e. word-sized unsigned
Expand Down Expand Up @@ -235,25 +226,21 @@ impl BoxedUint {

/// Widen this type's precision to the given number of bits.
///
/// Panics if `bits_precision` is not a multiple of `Limb::BITS` or smaller than the current
/// precision.
pub fn widen(&self, bits_precision: u32) -> BoxedUint {
assert!(bits_precision % Limb::BITS == 0);
assert!(bits_precision >= self.bits_precision());
/// Panics if `at_least_bits_precision` is smaller than the current precision.
pub fn widen(&self, at_least_bits_precision: u32) -> BoxedUint {
assert!(at_least_bits_precision >= self.bits_precision());

let mut ret = BoxedUint::zero_with_precision(bits_precision);
let mut ret = BoxedUint::zero_with_precision(at_least_bits_precision);
ret.limbs[..self.nlimbs()].copy_from_slice(&self.limbs);
ret
}

/// Shortens this type's precision to the given number of bits.
///
/// Panics if `bits_precision` is not a multiple of `Limb::BITS` or smaller than the current
/// precision.
pub fn shorten(&self, bits_precision: u32) -> BoxedUint {
assert!(bits_precision % Limb::BITS == 0);
assert!(bits_precision <= self.bits_precision());
let mut ret = BoxedUint::zero_with_precision(bits_precision);
/// Panics if `at_least_bits_precision` is larger than the current precision.
pub fn shorten(&self, at_least_bits_precision: u32) -> BoxedUint {
assert!(at_least_bits_precision <= self.bits_precision());
let mut ret = BoxedUint::zero_with_precision(at_least_bits_precision);
let nlimbs = ret.nlimbs();
ret.limbs.copy_from_slice(&self.limbs[..nlimbs]);
ret
Expand Down
110 changes: 78 additions & 32 deletions src/uint/boxed/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ use core::fmt;
/// Decoding errors for [`BoxedUint`].
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DecodeError {
/// Input is not a valid size.
/// Input size is too small to fit in the given precision.
InputSize,

/// Precision is not a multiple of [`Limb::BYTES`].
/// The deserialized number is larger than the given precision.
Precision,
}

Expand All @@ -31,51 +31,53 @@ impl BoxedUint {
/// Create a new [`BoxedUint`] from the provided big endian bytes.
///
/// The `bits_precision` argument represents the precision of the resulting integer, which is
/// fixed as this type is not arbitrary-precision. It MUST be a multiple of the limb size, i.e.
/// [`Limb::BITS`], or otherwise this function will return [`DecodeError::Precision`].
/// fixed as this type is not arbitrary-precision.
/// The new [`BoxedUint`] will be created with `bits_precision`
/// rounded up to a multiple of [`Limb::BITS`].
///
/// If the length of `bytes` (when interpreted as bits) is larger than `bits_precision`, this
/// function will return [`DecodeError::InputSize`].
/// If the length of `bytes` is larger than `bits_precision` (rounded up to a multiple of 8)
/// this function will return [`DecodeError::InputSize`].
/// If the size of the decoded integer is larger than `bits_precision`,
/// this function will return [`DecodeError::Precision`].
pub fn from_be_slice(bytes: &[u8], bits_precision: u32) -> Result<Self, DecodeError> {
if bytes.is_empty() && bits_precision == 0 {
return Ok(Self::zero());
}

if bits_precision % Limb::BITS != 0 {
return Err(DecodeError::Precision);
}

if bytes.len() % Limb::BYTES != 0 || bytes.len() * 8 > bits_precision as usize {
if bytes.len() > (bits_precision as usize + 7) / 8 {
return Err(DecodeError::InputSize);
}

let mut ret = Self::zero_with_precision(bits_precision);

for (chunk, limb) in bytes.chunks(Limb::BYTES).rev().zip(ret.limbs.iter_mut()) {
for (chunk, limb) in bytes.rchunks(Limb::BYTES).zip(ret.limbs.iter_mut()) {
*limb = Limb::from_be_slice(chunk);
}

if bits_precision < ret.bits() {
return Err(DecodeError::Precision);
}

Ok(ret)
}

/// Create a new [`BoxedUint`] from the provided little endian bytes.
///
/// The `bits_precision` argument represents the precision of the resulting integer, which is
/// fixed as this type is not arbitrary-precision. It MUST be a multiple of the limb size, i.e.
/// [`Limb::BITS`], or otherwise this function will return [`DecodeError::Precision`].
/// fixed as this type is not arbitrary-precision.
/// The new [`BoxedUint`] will be created with `bits_precision`
/// rounded up to a multiple of [`Limb::BITS`].
///
/// If the length of `bytes` (when interpreted as bits) is larger than `bits_precision`, this
/// function will return [`DecodeError::InputSize`].
/// If the length of `bytes` is larger than `bits_precision` (rounded up to a multiple of 8)
/// this function will return [`DecodeError::InputSize`].
/// If the size of the decoded integer is larger than `bits_precision`,
/// this function will return [`DecodeError::Precision`].
pub fn from_le_slice(bytes: &[u8], bits_precision: u32) -> Result<Self, DecodeError> {
if bytes.is_empty() && bits_precision == 0 {
return Ok(Self::zero());
}

if bits_precision % Limb::BITS != 0 {
return Err(DecodeError::Precision);
}

if bytes.len() % Limb::BYTES != 0 || bytes.len() * 8 > bits_precision as usize {
if bytes.len() > (bits_precision as usize + 7) / 8 {
return Err(DecodeError::InputSize);
}

Expand All @@ -85,6 +87,10 @@ impl BoxedUint {
*limb = Limb::from_le_slice(chunk);
}

if bits_precision < ret.bits() {
return Err(DecodeError::Precision);
}

Ok(ret)
}

Expand Down Expand Up @@ -186,19 +192,39 @@ mod tests {
}

#[test]
#[cfg(target_pointer_width = "32")]
fn from_be_slice_not_word_sized() {
let bytes = hex!("00112233445566778899aabbccddee");
let bytes = hex!("112233445566778899aabbccddeeff");
let n = BoxedUint::from_be_slice(&bytes, 127).unwrap();
assert_eq!(
BoxedUint::from_be_slice(&bytes, 128),
Err(DecodeError::InputSize)
n.as_limbs(),
&[
Limb(0xccddeeff),
Limb(0x8899aabb),
Limb(0x44556677),
Limb(0x00112233)
]
);
assert_eq!(n.bits_precision(), 128);
}

#[test]
fn from_be_slice_bad_precision() {
let bytes = hex!("00112233445566778899aabbccddeeff");
#[cfg(target_pointer_width = "64")]
fn from_be_slice_not_word_sized() {
let bytes = hex!("112233445566778899aabbccddeeff");
let n = BoxedUint::from_be_slice(&bytes, 127).unwrap();
assert_eq!(
BoxedUint::from_be_slice(&bytes, 127),
n.as_limbs(),
&[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
);
assert_eq!(n.bits_precision(), 128);
}

#[test]
fn from_be_slice_non_multiple_precision() {
let bytes = hex!("0f112233445566778899aabbccddeeff");
assert_eq!(
BoxedUint::from_be_slice(&bytes, 121),
Err(DecodeError::Precision)
);
}
Expand Down Expand Up @@ -259,19 +285,39 @@ mod tests {
}

#[test]
#[cfg(target_pointer_width = "32")]
fn from_le_slice_not_word_sized() {
let bytes = hex!("ffeeddccbbaa998877665544332211");
let n = BoxedUint::from_le_slice(&bytes, 127).unwrap();
assert_eq!(
BoxedUint::from_be_slice(&bytes, 128),
Err(DecodeError::InputSize)
n.as_limbs(),
&[
Limb(0xccddeeff),
Limb(0x8899aabb),
Limb(0x44556677),
Limb(0x00112233)
]
);
assert_eq!(n.bits_precision(), 128);
}

#[test]
fn from_le_slice_bad_precision() {
let bytes = hex!("ffeeddccbbaa99887766554433221100");
#[cfg(target_pointer_width = "64")]
fn from_le_slice_not_word_sized() {
let bytes = hex!("ffeeddccbbaa998877665544332211");
let n = BoxedUint::from_le_slice(&bytes, 127).unwrap();
assert_eq!(
n.as_limbs(),
&[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
);
assert_eq!(n.bits_precision(), 128);
}

#[test]
fn from_le_slice_non_multiple_precision() {
let bytes = hex!("ffeeddccbbaa998877665544332211f0");
assert_eq!(
BoxedUint::from_le_slice(&bytes, 127),
BoxedUint::from_le_slice(&bytes, 121),
Err(DecodeError::Precision)
);
}
Expand Down
Loading