@@ -396,8 +396,12 @@ internal static unsafe ulong ParseUInt64(ReadOnlySpan<char> value, NumberStyles
396
396
return i ;
397
397
}
398
398
399
- private static unsafe bool ParseNumber ( ref char * str , NumberStyles options , ref NumberBuffer number , NumberFormatInfo numfmt , bool parseDecimal )
399
+ private static unsafe bool ParseNumber ( ref char * str , char * strEnd , NumberStyles options , ref NumberBuffer number , NumberFormatInfo numfmt , bool parseDecimal )
400
400
{
401
+ Debug . Assert ( str != null ) ;
402
+ Debug . Assert ( strEnd != null ) ;
403
+ Debug . Assert ( str <= strEnd ) ;
404
+
401
405
const int StateSign = 0x0001 ;
402
406
const int StateParens = 0x0002 ;
403
407
const int StateDigits = 0x0004 ;
@@ -430,7 +434,7 @@ private static unsafe bool ParseNumber(ref char* str, NumberStyles options, ref
430
434
431
435
int state = 0 ;
432
436
char * p = str ;
433
- char ch = * p ;
437
+ char ch = p < strEnd ? * p : ' \0 ' ;
434
438
char * next ;
435
439
436
440
while ( true )
@@ -439,7 +443,7 @@ private static unsafe bool ParseNumber(ref char* str, NumberStyles options, ref
439
443
// "-Kr 1231.47" is legal but "- 1231.47" is not.
440
444
if ( ! IsWhite ( ch ) || ( options & NumberStyles . AllowLeadingWhite ) == 0 || ( ( state & StateSign ) != 0 && ( ( state & StateCurrency ) == 0 && numfmt . NumberNegativePattern != 2 ) ) )
441
445
{
442
- if ( ( ( ( options & NumberStyles . AllowLeadingSign ) != 0 ) && ( state & StateSign ) == 0 ) && ( ( next = MatchChars ( p , numfmt . PositiveSign ) ) != null || ( ( next = MatchChars ( p , numfmt . NegativeSign ) ) != null && ( number . sign = true ) ) ) )
446
+ if ( ( ( ( options & NumberStyles . AllowLeadingSign ) != 0 ) && ( state & StateSign ) == 0 ) && ( ( next = MatchChars ( p , strEnd , numfmt . PositiveSign ) ) != null || ( ( next = MatchChars ( p , strEnd , numfmt . NegativeSign ) ) != null && ( number . sign = true ) ) ) )
443
447
{
444
448
state |= StateSign ;
445
449
p = next - 1 ;
@@ -449,7 +453,7 @@ private static unsafe bool ParseNumber(ref char* str, NumberStyles options, ref
449
453
state |= StateSign | StateParens ;
450
454
number . sign = true ;
451
455
}
452
- else if ( currSymbol != null && ( next = MatchChars ( p , currSymbol ) ) != null )
456
+ else if ( currSymbol != null && ( next = MatchChars ( p , strEnd , currSymbol ) ) != null )
453
457
{
454
458
state |= StateCurrency ;
455
459
currSymbol = null ;
@@ -462,7 +466,7 @@ private static unsafe bool ParseNumber(ref char* str, NumberStyles options, ref
462
466
break ;
463
467
}
464
468
}
465
- ch = * ++ p ;
469
+ ch = ++ p < strEnd ? * p : ' \0 ' ;
466
470
}
467
471
int digCount = 0 ;
468
472
int digEnd = 0 ;
@@ -493,20 +497,20 @@ private static unsafe bool ParseNumber(ref char* str, NumberStyles options, ref
493
497
number . scale -- ;
494
498
}
495
499
}
496
- else if ( ( ( options & NumberStyles . AllowDecimalPoint ) != 0 ) && ( ( state & StateDecimal ) == 0 ) && ( ( next = MatchChars ( p , decSep ) ) != null || ( ( parsingCurrency ) & & ( state & StateCurrency ) == 0 ) && ( next = MatchChars ( p , numfmt . NumberDecimalSeparator ) ) != null ) )
500
+ else if ( ( ( options & NumberStyles . AllowDecimalPoint ) != 0 ) && ( ( state & StateDecimal ) == 0 ) && ( ( next = MatchChars ( p , strEnd , decSep ) ) != null || ( ( parsingCurrency ) & & ( state & StateCurrency ) == 0 ) && ( next = MatchChars ( p , strEnd , numfmt . NumberDecimalSeparator ) ) != null ) )
497
501
{
498
502
state |= StateDecimal ;
499
503
p = next - 1 ;
500
504
}
501
- else if ( ( ( options & NumberStyles . AllowThousands ) != 0 ) && ( ( state & StateDigits ) != 0 ) && ( ( state & StateDecimal ) == 0 ) && ( ( next = MatchChars ( p , groupSep ) ) != null || ( ( parsingCurrency ) & & ( state & StateCurrency ) == 0 ) && ( next = MatchChars ( p , numfmt . NumberGroupSeparator ) ) != null ) )
505
+ else if ( ( ( options & NumberStyles . AllowThousands ) != 0 ) && ( ( state & StateDigits ) != 0 ) && ( ( state & StateDecimal ) == 0 ) && ( ( next = MatchChars ( p , strEnd , groupSep ) ) != null || ( ( parsingCurrency ) & & ( state & StateCurrency ) == 0 ) && ( next = MatchChars ( p , strEnd , numfmt . NumberGroupSeparator ) ) != null ) )
502
506
{
503
507
p = next - 1 ;
504
508
}
505
509
else
506
510
{
507
511
break ;
508
512
}
509
- ch = * ++ p ;
513
+ ch = ++ p < strEnd ? * p : ' \0 ' ;
510
514
}
511
515
512
516
bool negExp = false ;
@@ -517,14 +521,14 @@ private static unsafe bool ParseNumber(ref char* str, NumberStyles options, ref
517
521
if ( ( ch == 'E' || ch == 'e' ) && ( ( options & NumberStyles . AllowExponent ) != 0 ) )
518
522
{
519
523
char * temp = p ;
520
- ch = * ++ p ;
521
- if ( ( next = MatchChars ( p , numfmt . positiveSign ) ) != null )
524
+ ch = ++ p < strEnd ? * p : ' \0 ' ;
525
+ if ( ( next = MatchChars ( p , strEnd , numfmt . positiveSign ) ) != null )
522
526
{
523
- ch = * ( p = next ) ;
527
+ ch = ( p = next ) < strEnd ? * p : ' \0 ' ;
524
528
}
525
- else if ( ( next = MatchChars ( p , numfmt . negativeSign ) ) != null )
529
+ else if ( ( next = MatchChars ( p , strEnd , numfmt . negativeSign ) ) != null )
526
530
{
527
- ch = * ( p = next ) ;
531
+ ch = ( p = next ) < strEnd ? * p : ' \0 ' ;
528
532
negExp = true ;
529
533
}
530
534
if ( ch >= '0' && ch <= '9' )
@@ -533,13 +537,13 @@ private static unsafe bool ParseNumber(ref char* str, NumberStyles options, ref
533
537
do
534
538
{
535
539
exp = exp * 10 + ( ch - '0' ) ;
536
- ch = * ++ p ;
540
+ ch = ++ p < strEnd ? * p : ' \0 ' ;
537
541
if ( exp > 1000 )
538
542
{
539
543
exp = 9999 ;
540
544
while ( ch >= '0' && ch <= '9' )
541
545
{
542
- ch = * ++ p ;
546
+ ch = ++ p < strEnd ? * p : ' \0 ' ;
543
547
}
544
548
}
545
549
} while ( ch >= '0' && ch <= '9' ) ;
@@ -552,14 +556,14 @@ private static unsafe bool ParseNumber(ref char* str, NumberStyles options, ref
552
556
else
553
557
{
554
558
p = temp ;
555
- ch = * p ;
559
+ ch = p < strEnd ? * p : ' \0 ' ;
556
560
}
557
561
}
558
562
while ( true )
559
563
{
560
564
if ( ! IsWhite ( ch ) || ( options & NumberStyles . AllowTrailingWhite ) == 0 )
561
565
{
562
- if ( ( ( options & NumberStyles . AllowTrailingSign ) != 0 && ( ( state & StateSign ) == 0 ) ) && ( ( next = MatchChars ( p , numfmt . PositiveSign ) ) != null || ( ( ( next = MatchChars ( p , numfmt . NegativeSign ) ) != null ) && ( number . sign = true ) ) ) )
566
+ if ( ( ( options & NumberStyles . AllowTrailingSign ) != 0 && ( ( state & StateSign ) == 0 ) ) && ( ( next = MatchChars ( p , strEnd , numfmt . PositiveSign ) ) != null || ( ( ( next = MatchChars ( p , strEnd , numfmt . NegativeSign ) ) != null ) && ( number . sign = true ) ) ) )
563
567
{
564
568
state |= StateSign ;
565
569
p = next - 1 ;
@@ -568,7 +572,7 @@ private static unsafe bool ParseNumber(ref char* str, NumberStyles options, ref
568
572
{
569
573
state &= ~ StateParens ;
570
574
}
571
- else if ( currSymbol ! = null && ( next = MatchChars ( p , currSymbol ) ) != null )
575
+ else if ( currSymbol != null && ( next = MatchChars ( p , strEnd , currSymbol ) ) != null )
572
576
{
573
577
currSymbol = null ;
574
578
p = next - 1 ;
@@ -578,7 +582,7 @@ private static unsafe bool ParseNumber(ref char* str, NumberStyles options, ref
578
582
break ;
579
583
}
580
584
}
581
- ch = * ++ p ;
585
+ ch = ++ p < strEnd ? * p : ' \0 ' ;
582
586
}
583
587
if ( ( state & StateParens ) == 0 )
584
588
{
@@ -859,7 +863,7 @@ private static unsafe void StringToNumber(ReadOnlySpan<char> str, NumberStyles o
859
863
fixed ( char * stringPointer = & MemoryMarshal . GetReference ( str ) )
860
864
{
861
865
char * p = stringPointer ;
862
- if ( ! ParseNumber ( ref p , options , ref number , info , parseDecimal )
866
+ if ( ! ParseNumber ( ref p , p + str . Length , options , ref number , info , parseDecimal )
863
867
|| ( p - stringPointer < str . Length && ! TrailingZeros ( str , ( int ) ( p - stringPointer ) ) ) )
864
868
{
865
869
throw new FormatException ( SR . Format_InvalidString ) ;
@@ -873,7 +877,7 @@ internal static unsafe bool TryStringToNumber(ReadOnlySpan<char> str, NumberStyl
873
877
fixed ( char * stringPointer = & MemoryMarshal . GetReference ( str ) )
874
878
{
875
879
char * p = stringPointer ;
876
- if ( ! ParseNumber ( ref p , options , ref number , numfmt , parseDecimal )
880
+ if ( ! ParseNumber ( ref p , p + str . Length , options , ref number , numfmt , parseDecimal )
877
881
|| ( p - stringPointer < str . Length && ! TrailingZeros ( str , ( int ) ( p - stringPointer ) ) ) )
878
882
{
879
883
return false ;
@@ -897,17 +901,17 @@ private static bool TrailingZeros(ReadOnlySpan<char> s, int index)
897
901
return true ;
898
902
}
899
903
900
- private static unsafe char* MatchChars ( char * p , string str )
904
+ private static unsafe char * MatchChars ( char * p , char * pEnd , string str )
901
905
{
902
906
fixed ( char * stringPointer = str )
903
907
{
904
- return MatchChars ( p , stringPointer ) ;
908
+ return MatchChars ( p , pEnd , stringPointer ) ;
905
909
}
906
910
}
907
911
908
- private static unsafe char * MatchChars ( char * p , char * str )
912
+ private static unsafe char * MatchChars ( char * p , char * pEnd , char * str )
909
913
{
910
- Debug . Assert ( p != null && str != null ) ;
914
+ Debug . Assert ( p != null && pEnd != null && p <= pEnd && str != null ) ;
911
915
912
916
if ( * str == '\0 ' )
913
917
{
@@ -917,8 +921,13 @@ private static bool TrailingZeros(ReadOnlySpan<char> s, int index)
917
921
// We only hurt the failure case
918
922
// This fix is for French or Kazakh cultures. Since a user cannot type 0xA0 as a
919
923
// space character we use 0x20 space character instead to mean the same.
920
- while ( * p == * str || ( * str == ' \u00a0 ' && * p == ' \u0020 ' ) )
924
+ while ( true )
921
925
{
926
+ char cp = p < pEnd ? * p : '\0 ' ;
927
+ if ( cp != * str && ! ( * str == '\u00a0 ' && cp == '\u0020 ' ) )
928
+ {
929
+ break ;
930
+ }
922
931
p ++ ;
923
932
str ++ ;
924
933
if ( * str == '\0 ' ) return p ;
0 commit comments