@@ -987,27 +987,26 @@ private static bool DescHasData(PhoneNumberDesc desc)
987987 || desc . HasNationalNumberPattern ;
988988 }
989989
990+ // Pre-filtered: excludes FIXED_LINE_OR_MOBILE (convenience aggregate) and UNKNOWN (non-type).
991+ private static readonly PhoneNumberType [ ] s_checkablePhoneNumberTypes =
992+ [
993+ PhoneNumberType . FIXED_LINE , PhoneNumberType . MOBILE , PhoneNumberType . TOLL_FREE ,
994+ PhoneNumberType . PREMIUM_RATE , PhoneNumberType . SHARED_COST , PhoneNumberType . VOIP ,
995+ PhoneNumberType . PERSONAL_NUMBER , PhoneNumberType . PAGER , PhoneNumberType . UAN ,
996+ PhoneNumberType . VOICEMAIL ,
997+ ] ;
998+
990999 /// <summary>
9911000 /// Returns the types we have metadata for based on the PhoneMetadata object passed in, which must
9921001 /// be non-null.
9931002 /// </summary>
994- /// <param name="metadata"></param>
995- /// <returns></returns>
9961003 private HashSet < PhoneNumberType > GetSupportedTypesForMetadata ( PhoneMetadata metadata )
9971004 {
9981005 var types = new HashSet < PhoneNumberType > ( ) ;
999- foreach ( PhoneNumberType type in Enum . GetValues ( typeof ( PhoneNumberType ) ) )
1006+ foreach ( var type in s_checkablePhoneNumberTypes )
10001007 {
1001- if ( type == PhoneNumberType . FIXED_LINE_OR_MOBILE || type == PhoneNumberType . UNKNOWN )
1002- {
1003- // Never return FIXED_LINE_OR_MOBILE (it is a convenience type, and represents that a
1004- // particular number type can't be determined) or UNKNOWN (the non-type).
1005- continue ;
1006- }
10071008 if ( DescHasData ( GetNumberDescByType ( metadata , type ) ) )
1008- {
10091009 types . Add ( type ) ;
1010- }
10111010 }
10121011 return types ;
10131012 }
@@ -2307,6 +2306,14 @@ private int MaybeExtractCountryCode(string number, PhoneMetadata defaultRegionMe
23072306 nationalNumber , keepRawInput , phoneNumber ) ;
23082307 }
23092308
2309+ private static bool StringBuilderStartsWith ( StringBuilder sb , string value )
2310+ {
2311+ if ( sb . Length < value . Length ) return false ;
2312+ for ( var i = 0 ; i < value . Length ; i ++ )
2313+ if ( sb [ i ] != value [ i ] ) return false ;
2314+ return true ;
2315+ }
2316+
23102317 // Same as the string overload above, but takes a StringBuilder workspace directly so callers
23112318 // that already hold a StringBuilder can avoid an extra string<->StringBuilder round-trip.
23122319 // The fullNumber buffer is always mutated (Normalize is unconditional, and any leading '+' or
@@ -2357,9 +2364,10 @@ private int MaybeExtractCountryCode(StringBuilder fullNumber, PhoneMetadata defa
23572364 // before and after.
23582365 var defaultCountryCode = defaultRegionMetadata . CountryCode ;
23592366 var defaultCountryCodeString = defaultCountryCode . ToString ( ) ;
2360- var normalizedNumber = fullNumber . ToString ( ) ;
2361- if ( normalizedNumber . StartsWith ( defaultCountryCodeString , StringComparison . Ordinal ) )
2367+ // Avoid the ToString() allocation unless the prefix actually matches (common case: it won't).
2368+ if ( StringBuilderStartsWith ( fullNumber , defaultCountryCodeString ) )
23622369 {
2370+ var normalizedNumber = fullNumber . ToString ( ) ;
23632371 var potentialNationalNumberString = normalizedNumber . Substring ( defaultCountryCodeString . Length ) ;
23642372 // Use a separate buffer for the strip-CC trial so callers that share fullNumber as
23652373 // their workspace are not corrupted when this branch decides not to commit.
@@ -2373,7 +2381,7 @@ private int MaybeExtractCountryCode(StringBuilder fullNumber, PhoneMetadata defa
23732381 var validNumberPattern = generalDesc . GetNationalNumberPattern ( ) ;
23742382 if ( ( ! validNumberPattern . IsMatchAll ( normalizedNumber ) &&
23752383 validNumberPattern . IsMatchAll ( potentialNationalNumberString ) ) ||
2376- TestNumberLength ( normalizedNumber . Length , defaultRegionMetadata ) == ValidationResult . TOO_LONG )
2384+ TestNumberLength ( fullNumber . Length , defaultRegionMetadata ) == ValidationResult . TOO_LONG )
23772385 {
23782386 nationalNumber . Append ( potentialNationalNumberString ) ;
23792387 if ( keepRawInput )
@@ -2531,7 +2539,11 @@ static string MaybeStripExtension(StringBuilder number, string numberString)
25312539 var m = ExtnPattern ( ) . Match ( numberString ) ;
25322540 // If we find a potential extension, and the number preceding this is a viable number, we assume
25332541 // it is an extension.
2542+ #if NET7_0_OR_GREATER
2543+ if ( m . Success && IsViablePhoneNumberSpan ( numberString . AsSpan ( 0 , m . Index ) ) )
2544+ #else
25342545 if ( m . Success && IsViablePhoneNumber ( numberString . Substring ( 0 , m . Index ) ) )
2546+ #endif
25352547 {
25362548 // The numbers are captured into groups in the regular expression.
25372549 for ( int i = 1 , length = m . Groups . Count ; i < length ; i ++ )
0 commit comments