Skip to content

Commit 7d174f0

Browse files
authored
Round up bits_precision when creating BoxedUint (#365)
1 parent 6083555 commit 7d174f0

File tree

6 files changed

+151
-87
lines changed

6 files changed

+151
-87
lines changed

src/limb/encoding.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,30 @@ impl Encoding for Limb {
3434
impl Limb {
3535
/// Decode limb from a big endian byte slice.
3636
///
37-
/// Panics if the slice is not the same size as [`Limb::Repr`].
37+
/// Panics if the slice is larger than [`Limb::Repr`].
3838
pub(crate) fn from_be_slice(bytes: &[u8]) -> Self {
39-
Self::from_be_bytes(bytes.try_into().expect("slice not limb-sized"))
39+
let mut repr = Self::ZERO.to_be_bytes();
40+
let repr_len = repr.len();
41+
assert!(
42+
bytes.len() <= repr_len,
43+
"The given slice is larger than the limb size"
44+
);
45+
repr[(repr_len - bytes.len())..].copy_from_slice(bytes);
46+
Self::from_be_bytes(repr)
4047
}
4148

4249
/// Decode limb from a little endian byte slice.
4350
///
4451
/// Panics if the slice is not the same size as [`Limb::Repr`].
4552
pub(crate) fn from_le_slice(bytes: &[u8]) -> Self {
46-
Self::from_le_bytes(bytes.try_into().expect("slice not limb-sized"))
53+
let mut repr = Self::ZERO.to_le_bytes();
54+
let repr_len = repr.len();
55+
assert!(
56+
bytes.len() <= repr_len,
57+
"The given slice is larger than the limb size"
58+
);
59+
repr[..bytes.len()].copy_from_slice(bytes);
60+
Self::from_le_bytes(repr)
4761
}
4862
}
4963

src/macros.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ macro_rules! const_assert_ne {
5959
#[macro_export]
6060
macro_rules! nlimbs {
6161
($bits:expr) => {
62-
$bits / $crate::Limb::BITS as usize
62+
(($bits + $crate::Limb::BITS - 1) / $crate::Limb::BITS) as usize
6363
};
6464
}
6565

src/uint/boxed.rs

Lines changed: 23 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ pub struct BoxedUint {
5050
}
5151

5252
impl BoxedUint {
53+
fn limbs_for_precision(at_least_bits_precision: u32) -> usize {
54+
((at_least_bits_precision + Limb::BITS - 1) / Limb::BITS) as usize
55+
}
56+
5357
/// Get the value `0` represented as succinctly as possible.
5458
pub fn zero() -> Self {
5559
Self {
@@ -59,15 +63,9 @@ impl BoxedUint {
5963

6064
/// Get the value `0` with the given number of bits of precision.
6165
///
62-
/// Panics if the precision is not a multiple of [`Limb::BITS`].
63-
pub fn zero_with_precision(bits_precision: u32) -> Self {
64-
assert_eq!(
65-
bits_precision % Limb::BITS,
66-
0,
67-
"precision is not a multiple of limb size"
68-
);
69-
70-
vec![Limb::ZERO; (bits_precision / Limb::BITS) as usize].into()
66+
/// `at_least_bits_precision` is rounded up to a multiple of [`Limb::BITS`].
67+
pub fn zero_with_precision(at_least_bits_precision: u32) -> Self {
68+
vec![Limb::ZERO; Self::limbs_for_precision(at_least_bits_precision)].into()
7169
}
7270

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

8078
/// Get the value `1` with the given number of bits of precision.
8179
///
82-
/// Panics if the precision is not at least [`Limb::BITS`] or if it is not
83-
/// a multiple thereof.
84-
pub fn one_with_precision(bits_precision: u32) -> Self {
85-
assert!(bits_precision >= Limb::BITS, "precision too small");
86-
let mut ret = Self::zero_with_precision(bits_precision);
80+
/// `at_least_bits_precision` is rounded up to a multiple of [`Limb::BITS`].
81+
pub fn one_with_precision(at_least_bits_precision: u32) -> Self {
82+
let mut ret = Self::zero_with_precision(at_least_bits_precision);
8783
ret.limbs[0] = Limb::ONE;
8884
ret
8985
}
@@ -125,17 +121,12 @@ impl BoxedUint {
125121
!self.is_odd()
126122
}
127123

128-
/// Get the maximum value for a given number of bits of precision.
124+
/// Get the maximum value for a `BoxedUint` created with `at_least_bits_precision`
125+
/// precision bits requested.
129126
///
130-
/// Panics if the precision is not a multiple of [`Limb::BITS`].
131-
pub fn max(bits_precision: u32) -> Self {
132-
assert_eq!(
133-
bits_precision % Limb::BITS,
134-
0,
135-
"precision is not a multiple of limb size"
136-
);
137-
138-
vec![Limb::MAX; (bits_precision / Limb::BITS) as usize].into()
127+
/// That is, returns the value `2^self.bits_precision() - 1`.
128+
pub fn max(at_least_bits_precision: u32) -> Self {
129+
vec![Limb::MAX; Self::limbs_for_precision(at_least_bits_precision)].into()
139130
}
140131

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

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

244-
let mut ret = BoxedUint::zero_with_precision(bits_precision);
233+
let mut ret = BoxedUint::zero_with_precision(at_least_bits_precision);
245234
ret.limbs[..self.nlimbs()].copy_from_slice(&self.limbs);
246235
ret
247236
}
248237

249238
/// Shortens this type's precision to the given number of bits.
250239
///
251-
/// Panics if `bits_precision` is not a multiple of `Limb::BITS` or smaller than the current
252-
/// precision.
253-
pub fn shorten(&self, bits_precision: u32) -> BoxedUint {
254-
assert!(bits_precision % Limb::BITS == 0);
255-
assert!(bits_precision <= self.bits_precision());
256-
let mut ret = BoxedUint::zero_with_precision(bits_precision);
240+
/// Panics if `at_least_bits_precision` is larger than the current precision.
241+
pub fn shorten(&self, at_least_bits_precision: u32) -> BoxedUint {
242+
assert!(at_least_bits_precision <= self.bits_precision());
243+
let mut ret = BoxedUint::zero_with_precision(at_least_bits_precision);
257244
let nlimbs = ret.nlimbs();
258245
ret.limbs.copy_from_slice(&self.limbs[..nlimbs]);
259246
ret

src/uint/boxed/encoding.rs

Lines changed: 83 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,21 @@ use core::fmt;
88
/// Decoding errors for [`BoxedUint`].
99
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1010
pub enum DecodeError {
11-
/// Input is not a valid size.
11+
/// Input size is too small to fit in the given precision.
1212
InputSize,
1313

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

1818
impl fmt::Display for DecodeError {
1919
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2020
match self {
21-
Self::InputSize => write!(f, "input is not a valid size"),
22-
Self::Precision => write!(f, "precision is not a multiple of the word size"),
21+
Self::InputSize => write!(f, "input size is too small to fit in the given precision"),
22+
Self::Precision => write!(
23+
f,
24+
"the deserialized number is larger than the given precision"
25+
),
2326
}
2427
}
2528
}
@@ -31,51 +34,53 @@ impl BoxedUint {
3134
/// Create a new [`BoxedUint`] from the provided big endian bytes.
3235
///
3336
/// The `bits_precision` argument represents the precision of the resulting integer, which is
34-
/// fixed as this type is not arbitrary-precision. It MUST be a multiple of the limb size, i.e.
35-
/// [`Limb::BITS`], or otherwise this function will return [`DecodeError::Precision`].
37+
/// fixed as this type is not arbitrary-precision.
38+
/// The new [`BoxedUint`] will be created with `bits_precision`
39+
/// rounded up to a multiple of [`Limb::BITS`].
3640
///
37-
/// If the length of `bytes` (when interpreted as bits) is larger than `bits_precision`, this
38-
/// function will return [`DecodeError::InputSize`].
41+
/// If the length of `bytes` is larger than `bits_precision` (rounded up to a multiple of 8)
42+
/// this function will return [`DecodeError::InputSize`].
43+
/// If the size of the decoded integer is larger than `bits_precision`,
44+
/// this function will return [`DecodeError::Precision`].
3945
pub fn from_be_slice(bytes: &[u8], bits_precision: u32) -> Result<Self, DecodeError> {
4046
if bytes.is_empty() && bits_precision == 0 {
4147
return Ok(Self::zero());
4248
}
4349

44-
if bits_precision % Limb::BITS != 0 {
45-
return Err(DecodeError::Precision);
46-
}
47-
48-
if bytes.len() % Limb::BYTES != 0 || bytes.len() * 8 > bits_precision as usize {
50+
if bytes.len() > (bits_precision as usize + 7) / 8 {
4951
return Err(DecodeError::InputSize);
5052
}
5153

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

54-
for (chunk, limb) in bytes.chunks(Limb::BYTES).rev().zip(ret.limbs.iter_mut()) {
56+
for (chunk, limb) in bytes.rchunks(Limb::BYTES).zip(ret.limbs.iter_mut()) {
5557
*limb = Limb::from_be_slice(chunk);
5658
}
5759

60+
if bits_precision < ret.bits() {
61+
return Err(DecodeError::Precision);
62+
}
63+
5864
Ok(ret)
5965
}
6066

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

74-
if bits_precision % Limb::BITS != 0 {
75-
return Err(DecodeError::Precision);
76-
}
77-
78-
if bytes.len() % Limb::BYTES != 0 || bytes.len() * 8 > bits_precision as usize {
83+
if bytes.len() > (bits_precision as usize + 7) / 8 {
7984
return Err(DecodeError::InputSize);
8085
}
8186

@@ -85,6 +90,10 @@ impl BoxedUint {
8590
*limb = Limb::from_le_slice(chunk);
8691
}
8792

93+
if bits_precision < ret.bits() {
94+
return Err(DecodeError::Precision);
95+
}
96+
8897
Ok(ret)
8998
}
9099

@@ -186,19 +195,39 @@ mod tests {
186195
}
187196

188197
#[test]
198+
#[cfg(target_pointer_width = "32")]
189199
fn from_be_slice_not_word_sized() {
190-
let bytes = hex!("00112233445566778899aabbccddee");
200+
let bytes = hex!("112233445566778899aabbccddeeff");
201+
let n = BoxedUint::from_be_slice(&bytes, 127).unwrap();
191202
assert_eq!(
192-
BoxedUint::from_be_slice(&bytes, 128),
193-
Err(DecodeError::InputSize)
203+
n.as_limbs(),
204+
&[
205+
Limb(0xccddeeff),
206+
Limb(0x8899aabb),
207+
Limb(0x44556677),
208+
Limb(0x00112233)
209+
]
194210
);
211+
assert_eq!(n.bits_precision(), 128);
195212
}
196213

197214
#[test]
198-
fn from_be_slice_bad_precision() {
199-
let bytes = hex!("00112233445566778899aabbccddeeff");
215+
#[cfg(target_pointer_width = "64")]
216+
fn from_be_slice_not_word_sized() {
217+
let bytes = hex!("112233445566778899aabbccddeeff");
218+
let n = BoxedUint::from_be_slice(&bytes, 127).unwrap();
200219
assert_eq!(
201-
BoxedUint::from_be_slice(&bytes, 127),
220+
n.as_limbs(),
221+
&[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
222+
);
223+
assert_eq!(n.bits_precision(), 128);
224+
}
225+
226+
#[test]
227+
fn from_be_slice_non_multiple_precision() {
228+
let bytes = hex!("0f112233445566778899aabbccddeeff");
229+
assert_eq!(
230+
BoxedUint::from_be_slice(&bytes, 121),
202231
Err(DecodeError::Precision)
203232
);
204233
}
@@ -259,19 +288,39 @@ mod tests {
259288
}
260289

261290
#[test]
291+
#[cfg(target_pointer_width = "32")]
262292
fn from_le_slice_not_word_sized() {
263293
let bytes = hex!("ffeeddccbbaa998877665544332211");
294+
let n = BoxedUint::from_le_slice(&bytes, 127).unwrap();
264295
assert_eq!(
265-
BoxedUint::from_be_slice(&bytes, 128),
266-
Err(DecodeError::InputSize)
296+
n.as_limbs(),
297+
&[
298+
Limb(0xccddeeff),
299+
Limb(0x8899aabb),
300+
Limb(0x44556677),
301+
Limb(0x00112233)
302+
]
267303
);
304+
assert_eq!(n.bits_precision(), 128);
268305
}
269306

270307
#[test]
271-
fn from_le_slice_bad_precision() {
272-
let bytes = hex!("ffeeddccbbaa99887766554433221100");
308+
#[cfg(target_pointer_width = "64")]
309+
fn from_le_slice_not_word_sized() {
310+
let bytes = hex!("ffeeddccbbaa998877665544332211");
311+
let n = BoxedUint::from_le_slice(&bytes, 127).unwrap();
312+
assert_eq!(
313+
n.as_limbs(),
314+
&[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
315+
);
316+
assert_eq!(n.bits_precision(), 128);
317+
}
318+
319+
#[test]
320+
fn from_le_slice_non_multiple_precision() {
321+
let bytes = hex!("ffeeddccbbaa998877665544332211f0");
273322
assert_eq!(
274-
BoxedUint::from_le_slice(&bytes, 127),
323+
BoxedUint::from_le_slice(&bytes, 121),
275324
Err(DecodeError::Precision)
276325
);
277326
}

0 commit comments

Comments
 (0)