11//! Const-friendly decoding operations for [`BoxedUint`].
22
33use super :: BoxedUint ;
4- use crate :: { uint:: encoding, Limb , Word } ;
5- use alloc:: boxed:: Box ;
6- use core:: fmt;
4+ use crate :: { uint:: encoding, DecodeError , Limb , Word } ;
5+ use alloc:: { boxed:: Box , vec:: Vec } ;
76use subtle:: { Choice , CtOption } ;
87
9- /// Decoding errors for [`BoxedUint`].
10- #[ derive( Clone , Copy , Debug , Eq , PartialEq ) ]
11- pub enum DecodeError {
12- /// Input size is too small to fit in the given precision.
13- InputSize ,
14-
15- /// The deserialized number is larger than the given precision.
16- Precision ,
17- }
18-
19- impl fmt:: Display for DecodeError {
20- fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
21- match self {
22- Self :: InputSize => write ! ( f, "input size is too small to fit in the given precision" ) ,
23- Self :: Precision => write ! (
24- f,
25- "the deserialized number is larger than the given precision"
26- ) ,
27- }
28- }
29- }
30-
31- #[ cfg( feature = "std" ) ]
32- impl std:: error:: Error for DecodeError { }
33-
348impl BoxedUint {
359 /// Create a new [`BoxedUint`] from the provided big endian bytes.
3610 ///
@@ -142,7 +116,6 @@ impl BoxedUint {
142116 bytes. len( ) == Limb :: BYTES * nlimbs * 2 ,
143117 "hex string is not the expected size"
144118 ) ;
145-
146119 let mut res = vec ! [ Limb :: ZERO ; nlimbs] ;
147120 let mut buf = [ 0u8 ; Limb :: BYTES ] ;
148121 let mut i = 0 ;
@@ -163,6 +136,76 @@ impl BoxedUint {
163136 }
164137 CtOption :: new ( Self { limbs : res. into ( ) } , Choice :: from ( ( err == 0 ) as u8 ) )
165138 }
139+
140+ /// Create a new [`BoxedUint`] from a big-endian string in a given base.
141+ ///
142+ /// The string may begin with a `+` character, and may use underscore
143+ /// characters to separate digits.
144+ ///
145+ /// If the input value contains non-digit characters or digits outside of the range `0..radix`
146+ /// this function will return [`DecodeError::InvalidDigit`].
147+ /// Panics if `radix` is not in the range from 2 to 36.
148+ pub fn from_str_radix_vartime ( src : & str , radix : u32 ) -> Result < Self , DecodeError > {
149+ let mut dec = VecDecodeByLimb :: default ( ) ;
150+ encoding:: decode_str_radix ( src, radix, & mut dec) ?;
151+ Ok ( Self {
152+ limbs : dec. limbs . into ( ) ,
153+ } )
154+ }
155+
156+ /// Create a new [`BoxedUint`] from a big-endian string in a given base,
157+ /// with a given precision.
158+ ///
159+ /// The string may begin with a `+` character, and may use underscore
160+ /// characters to separate digits.
161+ ///
162+ /// The `bits_precision` argument represents the precision of the resulting integer, which is
163+ /// fixed as this type is not arbitrary-precision.
164+ /// The new [`BoxedUint`] will be created with `bits_precision` rounded up to a multiple
165+ /// of [`Limb::BITS`].
166+ ///
167+ /// If the input value contains non-digit characters or digits outside of the range `0..radix`
168+ /// this function will return [`DecodeError::InvalidDigit`].
169+ /// If the length of `bytes` is larger than `bits_precision` (rounded up to a multiple of 8)
170+ /// this function will return [`DecodeError::InputSize`].
171+ /// If the size of the decoded integer is larger than `bits_precision`,
172+ /// this function will return [`DecodeError::Precision`].
173+ /// Panics if `radix` is not in the range from 2 to 36.
174+ pub fn from_str_radix_with_precision_vartime (
175+ src : & str ,
176+ radix : u32 ,
177+ bits_precision : u32 ,
178+ ) -> Result < Self , DecodeError > {
179+ let mut ret = Self :: zero_with_precision ( bits_precision) ;
180+ encoding:: decode_str_radix (
181+ src,
182+ radix,
183+ & mut encoding:: SliceDecodeByLimb :: new ( & mut ret. limbs ) ,
184+ ) ?;
185+ if bits_precision < ret. bits ( ) {
186+ return Err ( DecodeError :: Precision ) ;
187+ }
188+ Ok ( ret)
189+ }
190+ }
191+
192+ /// Decoder target producing a Vec<Limb>
193+ #[ derive( Default ) ]
194+ struct VecDecodeByLimb {
195+ limbs : Vec < Limb > ,
196+ }
197+
198+ impl encoding:: DecodeByLimb for VecDecodeByLimb {
199+ #[ inline]
200+ fn limbs_mut ( & mut self ) -> & mut [ Limb ] {
201+ self . limbs . as_mut_slice ( )
202+ }
203+
204+ #[ inline]
205+ fn push_limb ( & mut self , limb : Limb ) -> bool {
206+ self . limbs . push ( limb) ;
207+ true
208+ }
166209}
167210
168211#[ cfg( test) ]
@@ -381,4 +424,38 @@ mod tests {
381424 let n = BoxedUint :: from_be_slice ( & bytes, 128 ) . unwrap ( ) ;
382425 assert_eq ! ( bytes. as_slice( ) , & * n. to_be_bytes( ) ) ;
383426 }
427+
428+ #[ test]
429+ fn from_str_radix_invalid ( ) {
430+ assert_eq ! (
431+ BoxedUint :: from_str_radix_vartime( "?" , 10 , ) ,
432+ Err ( DecodeError :: InvalidDigit )
433+ ) ;
434+ assert_eq ! (
435+ BoxedUint :: from_str_radix_with_precision_vartime(
436+ "ffffffffffffffff_ffffffffffffffff_f" ,
437+ 16 ,
438+ 128
439+ ) ,
440+ Err ( DecodeError :: InputSize )
441+ ) ;
442+ assert_eq ! (
443+ BoxedUint :: from_str_radix_with_precision_vartime( "1111111111111111" , 2 , 10 ) ,
444+ Err ( DecodeError :: Precision )
445+ ) ;
446+ }
447+
448+ #[ test]
449+ fn from_str_radix_10 ( ) {
450+ let dec = "+340_282_366_920_938_463_463_374_607_431_768_211_455" ;
451+ let res = BoxedUint :: from_str_radix_vartime ( dec, 10 ) . expect ( "error decoding" ) ;
452+ assert_eq ! ( res, BoxedUint :: max( 128 ) ) ;
453+ }
454+
455+ #[ test]
456+ fn from_str_radix_16 ( ) {
457+ let hex = "fedcba9876543210fedcba9876543210" ;
458+ let res = BoxedUint :: from_str_radix_vartime ( hex, 16 ) . expect ( "error decoding" ) ;
459+ assert_eq ! ( hex, format!( "{res:x}" ) ) ;
460+ }
384461}
0 commit comments