@@ -429,6 +429,8 @@ priv static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u;
429
429
* `FFp128`. The exponent string itself is always base 10.
430
430
* Can conflict with `radix`, see Failure.
431
431
* - `empty_zero` - Whether to accept a empty `buf` as a 0 or not.
432
+ * - `ignore_underscores` - Whether all underscores within the string should
433
+ * be ignored.
432
434
*
433
435
* # Return value
434
436
* Returns `Some(n)` if `buf` parses to a number n without overflowing, and
@@ -443,16 +445,13 @@ priv static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u;
443
445
* between digit and exponent sign `'p'`.
444
446
* - Fails if `radix` > 18 and `special == true` due to conflict
445
447
* between digit and lowest first character in `inf` and `NaN`, the `'i'`.
446
- *
447
- * # Possible improvements
448
- * - Could accept option to allow ignoring underscores, allowing for numbers
449
- * formated like `FF_AE_FF_FF`.
450
448
*/
451
- pub fn from_str_bytes_common < T : NumCast +Zero +One +Ord +Copy +Div < T , T > +
449
+ pub fn from_str_bytes_common < T : NumCast +Zero +One +Eq + Ord +Copy +Div < T , T > +
452
450
Mul < T , T > +Sub < T , T > +Neg < T > +Add < T , T > +
453
451
NumStrConv > (
454
452
buf : & [ u8 ] , radix : uint , negative : bool , fractional : bool ,
455
- special : bool , exponent : ExponentFormat , empty_zero : bool
453
+ special : bool , exponent : ExponentFormat , empty_zero : bool ,
454
+ ignore_underscores : bool
456
455
) -> Option < T > {
457
456
match exponent {
458
457
ExpDec if radix >= DIGIT_E_RADIX // decimal exponent 'e'
@@ -531,12 +530,16 @@ pub fn from_str_bytes_common<T:NumCast+Zero+One+Ord+Copy+Div<T,T>+
531
530
accum -= cast ( digit as int ) ;
532
531
}
533
532
534
- // Detect overflow by comparing to last value
535
- if accum_positive && accum < last_accum { return None ; }
536
- if !accum_positive && accum > last_accum { return None ; }
533
+ // Detect overflow by comparing to last value, except
534
+ // if we've not seen any non-zero digits.
535
+ if last_accum != _0 {
536
+ if accum_positive && accum <= last_accum { return None ; }
537
+ if !accum_positive && accum >= last_accum { return None ; }
538
+ }
537
539
last_accum = accum;
538
540
}
539
541
None => match c {
542
+ '_' if ignore_underscores => { }
540
543
'e' | 'E' | 'p' | 'P' => {
541
544
exp_found = true ;
542
545
break ; // start of exponent
@@ -580,6 +583,7 @@ pub fn from_str_bytes_common<T:NumCast+Zero+One+Ord+Copy+Div<T,T>+
580
583
last_accum = accum;
581
584
}
582
585
None => match c {
586
+ '_' if ignore_underscores => { }
583
587
'e' | 'E' | 'p' | 'P' => {
584
588
exp_found = true ;
585
589
break ; // start of exponent
@@ -607,6 +611,7 @@ pub fn from_str_bytes_common<T:NumCast+Zero+One+Ord+Copy+Div<T,T>+
607
611
if exp_found {
608
612
let c = buf[ i] as char ;
609
613
let base = match ( c, exponent) {
614
+ // c is never _ so don't need to handle specially
610
615
( 'e' , ExpDec ) | ( 'E' , ExpDec ) => 10 u,
611
616
( 'p' , ExpBin ) | ( 'P' , ExpBin ) => 2 u,
612
617
_ => return None // char doesn't fit given exponent format
@@ -615,7 +620,8 @@ pub fn from_str_bytes_common<T:NumCast+Zero+One+Ord+Copy+Div<T,T>+
615
620
// parse remaining bytes as decimal integer,
616
621
// skipping the exponent char
617
622
let exp: Option < int > = from_str_bytes_common (
618
- buf. slice ( i+1 , len) , 10 , true , false , false , ExpNone , false ) ;
623
+ buf. slice ( i+1 , len) , 10 , true , false , false , ExpNone , false ,
624
+ ignore_underscores) ;
619
625
620
626
match exp {
621
627
Some ( exp_pow) => {
@@ -637,11 +643,44 @@ pub fn from_str_bytes_common<T:NumCast+Zero+One+Ord+Copy+Div<T,T>+
637
643
* `from_str_bytes_common()`, for details see there.
638
644
*/
639
645
#[ inline( always) ]
640
- pub fn from_str_common < T : NumCast +Zero +One +Ord +Copy +Div < T , T > +Mul < T , T > +
646
+ pub fn from_str_common < T : NumCast +Zero +One +Eq + Ord +Copy +Div < T , T > +Mul < T , T > +
641
647
Sub < T , T > +Neg < T > +Add < T , T > +NumStrConv > (
642
648
buf : & str , radix : uint , negative : bool , fractional : bool ,
643
- special : bool , exponent : ExponentFormat , empty_zero : bool
649
+ special : bool , exponent : ExponentFormat , empty_zero : bool ,
650
+ ignore_underscores : bool
644
651
) -> Option < T > {
645
652
from_str_bytes_common ( str:: to_bytes ( buf) , radix, negative,
646
- fractional, special, exponent, empty_zero)
653
+ fractional, special, exponent, empty_zero,
654
+ ignore_underscores)
655
+ }
656
+
657
+ #[ cfg( test) ]
658
+ mod test {
659
+ use super :: * ;
660
+ use option:: * ;
661
+
662
+ #[ test]
663
+ fn from_str_ignore_underscores ( ) {
664
+ let s : Option < u8 > = from_str_common ( "__1__" , 2 , false , false , false ,
665
+ ExpNone , false , true ) ;
666
+ assert_eq ! ( s, Some ( 1u8 ) ) ;
667
+
668
+ let n : Option < u8 > = from_str_common ( "__1__" , 2 , false , false , false ,
669
+ ExpNone , false , false ) ;
670
+ assert_eq ! ( n, None ) ;
671
+
672
+ let f : Option < f32 > = from_str_common ( "_1_._1_e_1_" , 10 , false , true , false ,
673
+ ExpDec , false , true ) ;
674
+ assert_eq ! ( f, Some ( 1.1e1f32 ) ) ;
675
+ }
676
+
677
+ #[ test]
678
+ fn from_str_issue5770 ( ) {
679
+ // try to parse 0b1_1111_1111 = 511 as a u8. Caused problems
680
+ // since 255*2+1 == 255 (mod 256) so the overflow wasn't
681
+ // detected.
682
+ let n : Option < u8 > = from_str_common ( "111111111" , 2 , false , false , false ,
683
+ ExpNone , false , false ) ;
684
+ assert_eq ! ( n, None ) ;
685
+ }
647
686
}
0 commit comments