Skip to content

Commit 437f8c8

Browse files
authored
Merge branch 'main' into fix_inconsistency
2 parents 9afdabd + 2271382 commit 437f8c8

File tree

8 files changed

+253
-159
lines changed

8 files changed

+253
-159
lines changed

src/base_convert.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ impl fmt::Display for BaseConvertError {
2626
#[inline]
2727
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2828
match self {
29-
Self::Overflow => f.write_str("The value is too large to fit the target type"),
29+
Self::Overflow => f.write_str("the value is too large to fit the target type"),
3030
Self::InvalidBase(base) => {
31-
write!(f, "The requested number base {base} is less than two")
31+
write!(f, "the requested number base {base} is less than two")
3232
}
3333
Self::InvalidDigit(digit, base) => {
3434
write!(f, "digit {digit} is out of range for base {base}")

src/bit_arr.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ use alloc::{borrow::Cow, vec::Vec};
1313

1414
/// A newtype wrapper around [`Uint`] that restricts operations to those
1515
/// relevant for bit arrays.
16-
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
16+
#[derive(Clone, Copy, Default, Eq, PartialEq, Hash)]
17+
#[cfg_attr(feature = "alloc", derive(Debug))]
1718
pub struct Bits<const BITS: usize, const LIMBS: usize>(Uint<BITS, LIMBS>);
1819

1920
impl<const BITS: usize, const LIMBS: usize> From<Uint<BITS, LIMBS>> for Bits<BITS, LIMBS> {

src/cmp.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
use crate::Uint;
22
use core::cmp::Ordering;
33

4-
impl<const BITS: usize, const LIMBS: usize> Ord for Uint<BITS, LIMBS> {
4+
impl<const BITS: usize, const LIMBS: usize> PartialOrd for Uint<BITS, LIMBS> {
55
#[inline]
6-
fn cmp(&self, rhs: &Self) -> Ordering {
7-
crate::algorithms::cmp(self.as_limbs(), rhs.as_limbs())
6+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
7+
Some(self.cmp(other))
88
}
99
}
1010

11-
impl<const BITS: usize, const LIMBS: usize> PartialOrd for Uint<BITS, LIMBS> {
11+
impl<const BITS: usize, const LIMBS: usize> Ord for Uint<BITS, LIMBS> {
1212
#[inline]
13-
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
14-
Some(self.cmp(other))
13+
fn cmp(&self, rhs: &Self) -> Ordering {
14+
crate::algorithms::cmp(self.as_limbs(), rhs.as_limbs())
1515
}
1616
}
1717

@@ -20,7 +20,7 @@ impl<const BITS: usize, const LIMBS: usize> Uint<BITS, LIMBS> {
2020
#[inline]
2121
#[must_use]
2222
pub fn is_zero(&self) -> bool {
23-
self == &Self::ZERO
23+
*self == Self::ZERO
2424
}
2525
}
2626

src/fmt.rs

+216
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
#![allow(clippy::missing_inline_in_public_items)] // allow format functions
2+
#![cfg(feature = "alloc")]
3+
4+
use crate::Uint;
5+
use core::{
6+
fmt::{self, Write},
7+
mem::MaybeUninit,
8+
};
9+
10+
mod base {
11+
pub(super) trait Base {
12+
/// Highest power of the base that fits in a `u64`.
13+
const MAX: u64;
14+
/// Number of characters written using `MAX` as the base in
15+
/// `to_base_be`.
16+
///
17+
/// This is `MAX.log(base)`.
18+
const WIDTH: usize;
19+
/// The prefix for the base.
20+
const PREFIX: &'static str;
21+
}
22+
23+
pub(super) struct Binary;
24+
impl Base for Binary {
25+
const MAX: u64 = 1 << 63;
26+
const WIDTH: usize = 63;
27+
const PREFIX: &'static str = "0b";
28+
}
29+
30+
pub(super) struct Octal;
31+
impl Base for Octal {
32+
const MAX: u64 = 1 << 63;
33+
const WIDTH: usize = 21;
34+
const PREFIX: &'static str = "0o";
35+
}
36+
37+
pub(super) struct Decimal;
38+
impl Base for Decimal {
39+
const MAX: u64 = 10_000_000_000_000_000_000;
40+
const WIDTH: usize = 19;
41+
const PREFIX: &'static str = "";
42+
}
43+
44+
pub(super) struct Hexadecimal;
45+
impl Base for Hexadecimal {
46+
const MAX: u64 = 1 << 60;
47+
const WIDTH: usize = 15;
48+
const PREFIX: &'static str = "0x";
49+
}
50+
}
51+
use base::Base;
52+
53+
macro_rules! write_digits {
54+
($self:expr, $f:expr; $base:ty, $base_char:literal) => {
55+
if LIMBS == 0 || $self.is_zero() {
56+
return $f.pad_integral(true, <$base>::PREFIX, "0");
57+
}
58+
// Use `BITS` for all bases since `generic_const_exprs` is not yet stable.
59+
let mut buffer = DisplayBuffer::<BITS>::new();
60+
for (i, spigot) in $self.to_base_be(<$base>::MAX).enumerate() {
61+
write!(
62+
buffer,
63+
concat!("{:0width$", $base_char, "}"),
64+
spigot,
65+
width = if i == 0 { 0 } else { <$base>::WIDTH },
66+
)
67+
.unwrap();
68+
}
69+
return $f.pad_integral(true, <$base>::PREFIX, buffer.as_str());
70+
};
71+
}
72+
73+
impl<const BITS: usize, const LIMBS: usize> fmt::Display for Uint<BITS, LIMBS> {
74+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75+
write_digits!(self, f; base::Decimal, "");
76+
}
77+
}
78+
79+
impl<const BITS: usize, const LIMBS: usize> fmt::Debug for Uint<BITS, LIMBS> {
80+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81+
fmt::Display::fmt(self, f)
82+
}
83+
}
84+
85+
impl<const BITS: usize, const LIMBS: usize> fmt::Binary for Uint<BITS, LIMBS> {
86+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87+
write_digits!(self, f; base::Binary, "b");
88+
}
89+
}
90+
91+
impl<const BITS: usize, const LIMBS: usize> fmt::Octal for Uint<BITS, LIMBS> {
92+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93+
write_digits!(self, f; base::Octal, "o");
94+
}
95+
}
96+
97+
impl<const BITS: usize, const LIMBS: usize> fmt::LowerHex for Uint<BITS, LIMBS> {
98+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99+
write_digits!(self, f; base::Hexadecimal, "x");
100+
}
101+
}
102+
103+
impl<const BITS: usize, const LIMBS: usize> fmt::UpperHex for Uint<BITS, LIMBS> {
104+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105+
write_digits!(self, f; base::Hexadecimal, "X");
106+
}
107+
}
108+
109+
struct DisplayBuffer<const SIZE: usize> {
110+
buf: [MaybeUninit<u8>; SIZE],
111+
len: usize,
112+
}
113+
114+
impl<const SIZE: usize> DisplayBuffer<SIZE> {
115+
#[inline]
116+
const fn new() -> Self {
117+
Self {
118+
buf: unsafe { MaybeUninit::uninit().assume_init() },
119+
len: 0,
120+
}
121+
}
122+
123+
#[inline]
124+
fn as_str(&self) -> &str {
125+
// SAFETY: `buf` is only written to by the `fmt::Write::write_str`
126+
// implementation which writes a valid UTF-8 string to `buf` and
127+
// correctly sets `len`.
128+
unsafe { core::str::from_utf8_unchecked(&self.as_bytes_full()[..self.len]) }
129+
}
130+
131+
#[inline]
132+
const unsafe fn as_bytes_full(&self) -> &[u8] {
133+
unsafe { &*(self.buf.as_slice() as *const [_] as *const [u8]) }
134+
}
135+
}
136+
137+
impl<const SIZE: usize> fmt::Write for DisplayBuffer<SIZE> {
138+
fn write_str(&mut self, s: &str) -> fmt::Result {
139+
if self.len + s.len() > SIZE {
140+
return Err(fmt::Error);
141+
}
142+
unsafe {
143+
let dst = self.buf.as_mut_ptr().add(self.len).cast();
144+
core::ptr::copy_nonoverlapping(s.as_ptr(), dst, s.len());
145+
}
146+
self.len += s.len();
147+
Ok(())
148+
}
149+
}
150+
151+
#[cfg(test)]
152+
mod tests {
153+
use super::*;
154+
use proptest::{prop_assert_eq, proptest};
155+
156+
#[allow(unused_imports)]
157+
use alloc::string::ToString;
158+
159+
#[allow(clippy::unreadable_literal)]
160+
const N: Uint<256, 4> = Uint::from_limbs([
161+
0xa8ec92344438aaf4_u64,
162+
0x9819ebdbd1faaab1_u64,
163+
0x573b1a7064c19c1a_u64,
164+
0xc85ef7d79691fe79_u64,
165+
]);
166+
167+
#[test]
168+
fn test_num() {
169+
assert_eq!(
170+
N.to_string(),
171+
"90630363884335538722706632492458228784305343302099024356772372330524102404852"
172+
);
173+
assert_eq!(
174+
format!("{N:x}"),
175+
"c85ef7d79691fe79573b1a7064c19c1a9819ebdbd1faaab1a8ec92344438aaf4"
176+
);
177+
assert_eq!(
178+
format!("{N:b}"),
179+
"1100100001011110111101111101011110010110100100011111111001111001010101110011101100011010011100000110010011000001100111000001101010011000000110011110101111011011110100011111101010101010101100011010100011101100100100100011010001000100001110001010101011110100"
180+
);
181+
assert_eq!(
182+
format!("{N:o}"),
183+
"14413675753626443771712563543234062301470152300636573364375252543243544443210416125364"
184+
);
185+
}
186+
187+
#[test]
188+
fn test_fmt() {
189+
proptest!(|(value: u128)| {
190+
let n: Uint<128, 2> = Uint::from(value);
191+
192+
prop_assert_eq!(format!("{n:b}"), format!("{value:b}"));
193+
prop_assert_eq!(format!("{n:064b}"), format!("{value:064b}"));
194+
prop_assert_eq!(format!("{n:#b}"), format!("{value:#b}"));
195+
196+
prop_assert_eq!(format!("{n:o}"), format!("{value:o}"));
197+
prop_assert_eq!(format!("{n:064o}"), format!("{value:064o}"));
198+
prop_assert_eq!(format!("{n:#o}"), format!("{value:#o}"));
199+
200+
prop_assert_eq!(format!("{n:}"), format!("{value:}"));
201+
prop_assert_eq!(format!("{n:064}"), format!("{value:064}"));
202+
prop_assert_eq!(format!("{n:#}"), format!("{value:#}"));
203+
prop_assert_eq!(format!("{n:?}"), format!("{value:?}"));
204+
prop_assert_eq!(format!("{n:064}"), format!("{value:064?}"));
205+
prop_assert_eq!(format!("{n:#?}"), format!("{value:#?}"));
206+
207+
prop_assert_eq!(format!("{n:x}"), format!("{value:x}"));
208+
prop_assert_eq!(format!("{n:064x}"), format!("{value:064x}"));
209+
prop_assert_eq!(format!("{n:#x}"), format!("{value:#x}"));
210+
211+
prop_assert_eq!(format!("{n:X}"), format!("{value:X}"));
212+
prop_assert_eq!(format!("{n:064X}"), format!("{value:064X}"));
213+
prop_assert_eq!(format!("{n:#X}"), format!("{value:#X}"));
214+
});
215+
}
216+
}

src/lib.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,7 @@
1919
clippy::cast_sign_loss,
2020
clippy::cast_lossless,
2121
)]
22-
#![cfg_attr(
23-
any(test, feature = "bench"),
24-
allow(clippy::wildcard_imports, clippy::cognitive_complexity)
25-
)]
22+
#![cfg_attr(test, allow(clippy::wildcard_imports, clippy::cognitive_complexity))]
2623
#![cfg_attr(not(feature = "std"), no_std)]
2724
// Unstable features
2825
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
@@ -51,6 +48,7 @@ mod bytes;
5148
mod cmp;
5249
mod const_for;
5350
mod div;
51+
mod fmt;
5452
mod from;
5553
mod gcd;
5654
mod log;

0 commit comments

Comments
 (0)