1
1
use crate :: Uint ;
2
- use alloc:: vec:: Vec ;
3
2
use core:: fmt;
4
3
5
4
/// Error for [`from_base_le`][Uint::from_base_le] and
@@ -45,6 +44,7 @@ impl<const BITS: usize, const LIMBS: usize> Uint<BITS, LIMBS> {
45
44
/// # Panics
46
45
///
47
46
/// Panics if the base is less than 2.
47
+ #[ inline]
48
48
pub fn to_base_le ( & self , base : u64 ) -> impl Iterator < Item = u64 > {
49
49
assert ! ( base > 1 ) ;
50
50
SpigotLittle {
@@ -63,14 +63,51 @@ impl<const BITS: usize, const LIMBS: usize> Uint<BITS, LIMBS> {
63
63
/// # Panics
64
64
///
65
65
/// Panics if the base is less than 2.
66
+ #[ inline]
67
+ #[ cfg( feature = "alloc" ) ] // OPT: Find an allocation free method. Maybe extract from the top?
66
68
pub fn to_base_be ( & self , base : u64 ) -> impl Iterator < Item = u64 > {
69
+ struct OwnedVecIterator {
70
+ vec : alloc:: vec:: Vec < u64 > ,
71
+ }
72
+
73
+ impl Iterator for OwnedVecIterator {
74
+ type Item = u64 ;
75
+
76
+ #[ inline]
77
+ fn next ( & mut self ) -> Option < Self :: Item > {
78
+ self . vec . pop ( )
79
+ }
80
+ }
81
+
67
82
assert ! ( base > 1 ) ;
68
- // OPT: Find an allocation free method. Maybe extract from the top?
69
83
OwnedVecIterator {
70
84
vec : self . to_base_le ( base) . collect ( ) ,
71
85
}
72
86
}
73
87
88
+ /// Adds a digit in base `base` to the number. This is used internally by
89
+ /// [`Uint::from_base_le`] and [`Uint::from_base_be`].
90
+ #[ inline]
91
+ fn add_digit ( & mut self , digit : u64 , base : u64 ) -> Result < ( ) , BaseConvertError > {
92
+ if digit >= base {
93
+ return Err ( BaseConvertError :: InvalidDigit ( digit, base) ) ;
94
+ }
95
+ // Multiply by base.
96
+ // OPT: keep track of non-zero limbs and mul the minimum.
97
+ let mut carry: u128 = u128:: from ( digit) ;
98
+ #[ allow( clippy:: cast_possible_truncation) ]
99
+ for limb in & mut self . limbs {
100
+ carry += u128:: from ( * limb) * u128:: from ( base) ;
101
+ * limb = carry as u64 ;
102
+ carry >>= 64 ;
103
+ }
104
+ if carry > 0 || ( LIMBS != 0 && self . limbs [ LIMBS - 1 ] > Self :: MASK ) {
105
+ return Err ( BaseConvertError :: Overflow ) ;
106
+ }
107
+
108
+ Ok ( ( ) )
109
+ }
110
+
74
111
/// Constructs the [`Uint`] from digits in the base `base` in little-endian.
75
112
///
76
113
/// # Errors
@@ -79,14 +116,44 @@ impl<const BITS: usize, const LIMBS: usize> Uint<BITS, LIMBS> {
79
116
/// * [`BaseConvertError::InvalidDigit`] if a digit is out of range.
80
117
/// * [`BaseConvertError::Overflow`] if the number is too large to
81
118
/// fit.
82
- pub fn from_base_le < I : IntoIterator < Item = u64 > > (
119
+ #[ inline]
120
+ pub fn from_base_le < I > ( base : u64 , digits : I ) -> Result < Self , BaseConvertError >
121
+ where
122
+ I : IntoIterator < Item = u64 > ,
123
+ {
124
+ if base < 2 {
125
+ return Err ( BaseConvertError :: InvalidBase ( base) ) ;
126
+ }
127
+
128
+ let mut tail = digits. into_iter ( ) ;
129
+ match tail. next ( ) {
130
+ Some ( digit) => Self :: from_base_le_recurse ( digit, base, & mut tail) ,
131
+ None => Ok ( Self :: ZERO ) ,
132
+ }
133
+ }
134
+
135
+ /// This is the recursive part of [`Uint::from_base_le`].
136
+ ///
137
+ /// We drain the iterator via the recursive calls, and then perform the
138
+ /// same construction loop as [`Uint::from_base_be`] while exiting the
139
+ /// recursive callstack.
140
+ #[ inline]
141
+ fn from_base_le_recurse < I : Iterator < Item = u64 > > (
142
+ digit : u64 ,
83
143
base : u64 ,
84
- digits : I ,
144
+ tail : & mut I ,
85
145
) -> Result < Self , BaseConvertError > {
86
- // TODO: Do not allocate.
87
- let mut digits: Vec < _ > = digits. into_iter ( ) . collect ( ) ;
88
- digits. reverse ( ) ;
89
- Self :: from_base_be ( base, digits)
146
+ if digit > base {
147
+ return Err ( BaseConvertError :: InvalidDigit ( digit, base) ) ;
148
+ }
149
+
150
+ let mut acc = match tail. next ( ) {
151
+ Some ( digit) => Self :: from_base_le_recurse :: < I > ( digit, base, tail) ?,
152
+ None => Self :: ZERO ,
153
+ } ;
154
+
155
+ acc. add_digit ( digit, base) ?;
156
+ Ok ( acc)
90
157
}
91
158
92
159
/// Constructs the [`Uint`] from digits in the base `base` in big-endian.
@@ -108,23 +175,10 @@ impl<const BITS: usize, const LIMBS: usize> Uint<BITS, LIMBS> {
108
175
if base < 2 {
109
176
return Err ( BaseConvertError :: InvalidBase ( base) ) ;
110
177
}
178
+
111
179
let mut result = Self :: ZERO ;
112
180
for digit in digits {
113
- if digit >= base {
114
- return Err ( BaseConvertError :: InvalidDigit ( digit, base) ) ;
115
- }
116
- // Multiply by base.
117
- // OPT: keep track of non-zero limbs and mul the minimum.
118
- let mut carry: u128 = u128:: from ( digit) ;
119
- #[ allow( clippy:: cast_possible_truncation) ]
120
- for limb in & mut result. limbs {
121
- carry += u128:: from ( * limb) * u128:: from ( base) ;
122
- * limb = carry as u64 ;
123
- carry >>= 64 ;
124
- }
125
- if carry > 0 || ( LIMBS != 0 && result. limbs [ LIMBS - 1 ] > Self :: MASK ) {
126
- return Err ( BaseConvertError :: Overflow ) ;
127
- }
181
+ result. add_digit ( digit, base) ?;
128
182
}
129
183
130
184
Ok ( result)
@@ -139,6 +193,7 @@ struct SpigotLittle<const LIMBS: usize> {
139
193
impl < const LIMBS : usize > Iterator for SpigotLittle < LIMBS > {
140
194
type Item = u64 ;
141
195
196
+ #[ inline]
142
197
#[ allow( clippy:: cast_possible_truncation) ] // Doesn't truncate
143
198
fn next ( & mut self ) -> Option < Self :: Item > {
144
199
// Knuth Algorithm S.
@@ -147,8 +202,7 @@ impl<const LIMBS: usize> Iterator for SpigotLittle<LIMBS> {
147
202
// OPT: If we keep track of leading zero limbs we can half iterations.
148
203
for limb in self . limbs . iter_mut ( ) . rev ( ) {
149
204
zero |= * limb;
150
- remainder <<= 64 ;
151
- remainder |= u128:: from ( * limb) ;
205
+ remainder = ( remainder << 64 ) | u128:: from ( * limb) ;
152
206
* limb = ( remainder / u128:: from ( self . base ) ) as u64 ;
153
207
remainder %= u128:: from ( self . base ) ;
154
208
}
@@ -160,18 +214,6 @@ impl<const LIMBS: usize> Iterator for SpigotLittle<LIMBS> {
160
214
}
161
215
}
162
216
163
- struct OwnedVecIterator {
164
- vec : Vec < u64 > ,
165
- }
166
-
167
- impl Iterator for OwnedVecIterator {
168
- type Item = u64 ;
169
-
170
- fn next ( & mut self ) -> Option < Self :: Item > {
171
- self . vec . pop ( )
172
- }
173
- }
174
-
175
217
#[ cfg( test) ]
176
218
#[ allow( clippy:: unreadable_literal) ]
177
219
#[ allow( clippy:: zero_prefixed_literal) ]
0 commit comments