@@ -86,28 +86,20 @@ internal ContentType(string contentType!!)
8686 ValidateCarriageReturns ( contentType ) ;
8787
8888 //Begin Parsing
89- int semiColonIndex = contentType . IndexOf ( SemicolonSeparator ) ;
89+ int semiColonIndex = contentType . IndexOf ( ';' ) ;
9090
9191 if ( semiColonIndex == - 1 )
9292 {
9393 // Parse content type similar to - type/subtype
94- ParseTypeAndSubType ( contentType ) ;
94+ ParseTypeAndSubType ( contentType . AsSpan ( ) ) ;
9595 }
9696 else
9797 {
9898 // Parse content type similar to - type/subtype ; param1=value1 ; param2=value2 ; param3="value3"
99- ParseTypeAndSubType ( contentType . Substring ( 0 , semiColonIndex ) ) ;
100- ParseParameterAndValue ( contentType . Substring ( semiColonIndex ) ) ;
99+ ParseTypeAndSubType ( contentType . AsSpan ( 0 , semiColonIndex ) ) ;
100+ ParseParameterAndValue ( contentType . AsSpan ( semiColonIndex ) ) ;
101101 }
102102 }
103-
104- // keep this untouched for return from OriginalString property
105- _originalString = contentType ;
106-
107- //This variable is used to print out the correct content type string representation
108- //using the ToString method. This is mainly important while debugging and seeing the
109- //value of the content type object in the debugger.
110- _isInitialized = true ;
111103 }
112104
113105 #endregion Internal Constructors
@@ -146,14 +138,8 @@ internal string SubTypeComponent
146138 /// type/subtype ; param1=value1 ; param2=value2 ; param3="value3"
147139 /// This will return an enumerator over a dictionary of the parameter/value pairs.
148140 /// </summary>
149- internal Dictionary < string , string > . Enumerator ParameterValuePairs
150- {
151- get
152- {
153- EnsureParameterDictionary ( ) ;
154- return _parameterDictionary . GetEnumerator ( ) ;
155- }
156- }
141+ internal Dictionary < string , string > . Enumerator ParameterValuePairs =>
142+ ( _parameterDictionary ??= new ( ) ) . GetEnumerator ( ) ;
157143 #endregion Internal Properties
158144
159145 #region Internal Methods
@@ -225,13 +211,7 @@ public override string ToString()
225211 {
226212 if ( _contentType == null )
227213 {
228- //This is needed so that while debugging we get the correct
229- //string
230- if ( ! _isInitialized )
231- return string . Empty ;
232-
233- Debug . Assert ( string . CompareOrdinal ( _type , string . Empty ) != 0
234- || string . CompareOrdinal ( _subType , string . Empty ) != 0 ) ;
214+ Debug . Assert ( ! string . IsNullOrEmpty ( _type ) || ! string . IsNullOrEmpty ( _subType ) ) ;
235215
236216 StringBuilder stringBuilder = new StringBuilder ( _type ) ;
237217 stringBuilder . Append ( PackUriHelper . ForwardSlashChar ) ;
@@ -242,10 +222,10 @@ public override string ToString()
242222 foreach ( string parameterKey in _parameterDictionary . Keys )
243223 {
244224 stringBuilder . Append ( s_linearWhiteSpaceChars [ 0 ] ) ;
245- stringBuilder . Append ( SemicolonSeparator ) ;
225+ stringBuilder . Append ( ';' ) ;
246226 stringBuilder . Append ( s_linearWhiteSpaceChars [ 0 ] ) ;
247227 stringBuilder . Append ( parameterKey ) ;
248- stringBuilder . Append ( EqualSeparator ) ;
228+ stringBuilder . Append ( '=' ) ;
249229 stringBuilder . Append ( _parameterDictionary [ parameterKey ] ) ;
250230 }
251231 }
@@ -284,7 +264,9 @@ private static void ValidateCarriageReturns(string contentType)
284264 index = contentType . IndexOf ( s_linearWhiteSpaceChars [ 2 ] , ++ index ) ;
285265 }
286266 else
267+ {
287268 throw new ArgumentException ( SR . InvalidLinearWhiteSpaceCharacter ) ;
269+ }
288270 }
289271 }
290272
@@ -294,18 +276,20 @@ private static void ValidateCarriageReturns(string contentType)
294276 /// </summary>
295277 /// <param name="typeAndSubType">substring that has the type and subType of the content type</param>
296278 /// <exception cref="ArgumentException">If the typeAndSubType parameter does not have the "/" character</exception>
297- private void ParseTypeAndSubType ( string typeAndSubType )
279+ private void ParseTypeAndSubType ( ReadOnlySpan < char > typeAndSubType )
298280 {
299281 //okay to trim at this point the end of the string as Linear White Spaces(LWS) chars are allowed here.
300282 typeAndSubType = typeAndSubType . TrimEnd ( s_linearWhiteSpaceChars ) ;
301283
302- string [ ] splitBasedOnForwardSlash = typeAndSubType . Split ( PackUriHelper . s_forwardSlashCharArray ) ;
303-
304- if ( splitBasedOnForwardSlash . Length != 2 )
284+ int forwardSlashPos = typeAndSubType . IndexOf ( '/' ) ;
285+ if ( forwardSlashPos < 0 || // no slashes
286+ typeAndSubType . Slice ( forwardSlashPos + 1 ) . IndexOf ( '/' ) >= 0 ) // more than one slash
287+ {
305288 throw new ArgumentException ( SR . InvalidTypeSubType ) ;
289+ }
306290
307- _type = ValidateToken ( splitBasedOnForwardSlash [ 0 ] ) ;
308- _subType = ValidateToken ( splitBasedOnForwardSlash [ 1 ] ) ;
291+ _type = ValidateToken ( typeAndSubType . Slice ( 0 , forwardSlashPos ) . ToString ( ) ) ;
292+ _subType = ValidateToken ( typeAndSubType . Slice ( forwardSlashPos + 1 ) . ToString ( ) ) ;
309293 }
310294
311295 /// <summary>
@@ -314,13 +298,13 @@ private void ParseTypeAndSubType(string typeAndSubType)
314298 /// <param name="parameterAndValue">This string has the parameter and value pair of the form
315299 /// parameter=value</param>
316300 /// <exception cref="ArgumentException">If the string does not have the required "="</exception>
317- private void ParseParameterAndValue ( string parameterAndValue )
301+ private void ParseParameterAndValue ( ReadOnlySpan < char > parameterAndValue )
318302 {
319- while ( parameterAndValue != string . Empty )
303+ while ( ! parameterAndValue . IsEmpty )
320304 {
321305 //At this point the first character MUST be a semi-colon
322306 //First time through this test is serving more as an assert.
323- if ( parameterAndValue [ 0 ] != SemicolonSeparator )
307+ if ( parameterAndValue [ 0 ] != ';' )
324308 throw new ArgumentException ( SR . ExpectingSemicolon ) ;
325309
326310 //At this point if we have just one semicolon, then its an error.
@@ -330,13 +314,13 @@ private void ParseParameterAndValue(string parameterAndValue)
330314 throw new ArgumentException ( SR . ExpectingParameterValuePairs ) ;
331315
332316 //Removing the leading ; from the string
333- parameterAndValue = parameterAndValue . Substring ( 1 ) ;
317+ parameterAndValue = parameterAndValue . Slice ( 1 ) ;
334318
335319 //okay to trim start as there can be spaces before the beginning
336320 //of the parameter name.
337321 parameterAndValue = parameterAndValue . TrimStart ( s_linearWhiteSpaceChars ) ;
338322
339- int equalSignIndex = parameterAndValue . IndexOf ( EqualSeparator ) ;
323+ int equalSignIndex = parameterAndValue . IndexOf ( '=' ) ;
340324
341325 if ( equalSignIndex <= 0 || equalSignIndex == ( parameterAndValue . Length - 1 ) )
342326 throw new ArgumentException ( SR . InvalidParameterValuePair ) ;
@@ -346,13 +330,11 @@ private void ParseParameterAndValue(string parameterAndValue)
346330 //Get length of the parameter value
347331 int parameterValueLength = GetLengthOfParameterValue ( parameterAndValue , parameterStartIndex ) ;
348332
349- EnsureParameterDictionary ( ) ;
333+ ( _parameterDictionary ??= new ( ) ) . Add (
334+ ValidateToken ( parameterAndValue . Slice ( 0 , equalSignIndex ) . ToString ( ) ) ,
335+ ValidateQuotedStringOrToken ( parameterAndValue . Slice ( parameterStartIndex , parameterValueLength ) . ToString ( ) ) ) ;
350336
351- _parameterDictionary . Add (
352- ValidateToken ( parameterAndValue . Substring ( 0 , equalSignIndex ) ) ,
353- ValidateQuotedStringOrToken ( parameterAndValue . Substring ( parameterStartIndex , parameterValueLength ) ) ) ;
354-
355- parameterAndValue = parameterAndValue . Substring ( parameterStartIndex + parameterValueLength ) . TrimStart ( s_linearWhiteSpaceChars ) ;
337+ parameterAndValue = parameterAndValue . Slice ( parameterStartIndex + parameterValueLength ) . TrimStart ( s_linearWhiteSpaceChars ) ;
356338 }
357339 }
358340
@@ -362,7 +344,7 @@ private void ParseParameterAndValue(string parameterAndValue)
362344 /// <param name="s"></param>
363345 /// <param name="startIndex">Starting index for parsing</param>
364346 /// <returns></returns>
365- private static int GetLengthOfParameterValue ( string s , int startIndex )
347+ private static int GetLengthOfParameterValue ( ReadOnlySpan < char > s , int startIndex )
366348 {
367349 Debug . Assert ( s != null ) ;
368350
@@ -373,23 +355,20 @@ private static int GetLengthOfParameterValue(string s, int startIndex)
373355 //a ';' as the terminator for the token value.
374356 if ( s [ startIndex ] != '"' )
375357 {
376- int semicolonIndex = s . IndexOf ( SemicolonSeparator , startIndex ) ;
358+ int semicolonIndex = s . Slice ( startIndex ) . IndexOf ( ';' ) ;
377359
378360 if ( semicolonIndex != - 1 )
379361 {
380- int lwsIndex = s . IndexOfAny ( s_linearWhiteSpaceChars , startIndex ) ;
381- if ( lwsIndex != - 1 && lwsIndex < semicolonIndex )
382- length = lwsIndex ;
383- else
384- length = semicolonIndex ;
362+ int lwsIndex = s . Slice ( startIndex ) . IndexOfAny ( s_linearWhiteSpaceChars ) ;
363+ length = lwsIndex != - 1 && lwsIndex < semicolonIndex ? lwsIndex : semicolonIndex ;
364+ length += startIndex ; // the indexes from IndexOf{Any} are based on slicing from startIndex
385365 }
386366 else
387- length = semicolonIndex ;
388-
389- //If there is no linear whitespace found we treat the entire remaining string as
390- //parameter value.
391- if ( length == - 1 )
367+ {
368+ //If there is no linear white space found we treat the entire remaining string as
369+ //parameter value.
392370 length = s . Length ;
371+ }
393372 }
394373 else
395374 {
@@ -400,10 +379,14 @@ private static int GetLengthOfParameterValue(string s, int startIndex)
400379
401380 while ( ! found )
402381 {
403- length = s . IndexOf ( '"' , ++ length ) ;
382+ int startingLength = ++ length ;
383+ length = s . Slice ( startingLength ) . IndexOf ( '"' ) ;
404384
405385 if ( length == - 1 )
386+ {
406387 throw new ArgumentException ( SR . InvalidParameterValue ) ;
388+ }
389+ length += startingLength ; // IndexOf result is based on slicing from startingLength
407390
408391 if ( s [ length - 1 ] != '\\ ' )
409392 {
@@ -453,11 +436,15 @@ private static string ValidateQuotedStringOrToken(string parameterValue)
453436 throw new ArgumentException ( SR . InvalidParameterValue ) ;
454437
455438 if ( parameterValue . Length >= 2 &&
456- parameterValue . StartsWith ( Quote , StringComparison . Ordinal ) &&
457- parameterValue . EndsWith ( Quote , StringComparison . Ordinal ) )
458- ValidateQuotedText ( parameterValue . Substring ( 1 , parameterValue . Length - 2 ) ) ;
439+ parameterValue [ 0 ] == '"' &&
440+ parameterValue [ parameterValue . Length - 1 ] == '"' )
441+ {
442+ ValidateQuotedText ( parameterValue . AsSpan ( 1 , parameterValue . Length - 2 ) ) ;
443+ }
459444 else
445+ {
460446 ValidateToken ( parameterValue ) ;
447+ }
461448
462449 return parameterValue ;
463450 }
@@ -466,7 +453,7 @@ private static string ValidateQuotedStringOrToken(string parameterValue)
466453 /// This method validates if the text in the quoted string
467454 /// </summary>
468455 /// <param name="quotedText"></param>
469- private static void ValidateQuotedText ( string quotedText )
456+ private static void ValidateQuotedText ( ReadOnlySpan < char > quotedText )
470457 {
471458 //empty is okay
472459
@@ -477,9 +464,8 @@ private static void ValidateQuotedText(string quotedText)
477464
478465 if ( quotedText [ i ] <= ' ' || quotedText [ i ] >= 0xFF )
479466 throw new ArgumentException ( SR . InvalidParameterValue ) ;
480- else
481- if ( quotedText [ i ] == '"' &&
482- ( i == 0 || quotedText [ i - 1 ] != '\\ ' ) )
467+
468+ if ( quotedText [ i ] == '"' && ( i == 0 || quotedText [ i - 1 ] != '\\ ' ) )
483469 throw new ArgumentException ( SR . InvalidParameterValue ) ;
484470 }
485471 }
@@ -490,34 +476,18 @@ private static void ValidateQuotedText(string quotedText)
490476 /// </summary>
491477 /// <param name="character">input character</param>
492478 /// <returns></returns>
493- private static bool IsAllowedCharacter ( char character )
494- {
495- return Array . IndexOf ( s_allowedCharacters , character ) >= 0 ;
496- }
479+ private static bool IsAllowedCharacter ( char character ) =>
480+ Array . IndexOf ( s_allowedCharacters , character ) >= 0 ;
497481
498482 /// <summary>
499483 /// Returns true if the input character is an ASCII digit or letter
500484 /// Returns false if the input character is not an ASCII digit or letter
501485 /// </summary>
502486 /// <param name="character">input character</param>
503487 /// <returns></returns>
504- private static bool IsAsciiLetterOrDigit ( char character )
505- {
506- return ( IsAsciiLetter ( character ) || ( character >= '0' && character <= '9' ) ) ;
507- }
508-
509- /// <summary>
510- /// Returns true if the input character is an ASCII letter
511- /// Returns false if the input character is not an ASCII letter
512- /// </summary>
513- /// <param name="character">input character</param>
514- /// <returns></returns>
515- private static bool IsAsciiLetter ( char character )
516- {
517- return
518- ( character >= 'a' && character <= 'z' ) ||
519- ( character >= 'A' && character <= 'Z' ) ;
520- }
488+ private static bool IsAsciiLetterOrDigit ( char character ) =>
489+ ( ( ( ( uint ) character - 'A' ) & ~ 0x20 ) < 26 ) ||
490+ ( ( ( uint ) character - '0' ) < 10 ) ;
521491
522492 /// <summary>
523493 /// Returns true if the input character is one of the Linear White Space characters -
@@ -526,28 +496,8 @@ private static bool IsAsciiLetter(char character)
526496 /// </summary>
527497 /// <param name="ch">input character</param>
528498 /// <returns></returns>
529- private static bool IsLinearWhiteSpaceChar ( char ch )
530- {
531- if ( ch > ' ' )
532- {
533- return false ;
534- }
535-
536- int whiteSpaceIndex = Array . IndexOf ( s_linearWhiteSpaceChars , ch ) ;
537- return whiteSpaceIndex != - 1 ;
538- }
539-
540- /// <summary>
541- /// Lazy initialization for the ParameterDictionary
542- /// </summary>
543- [ MemberNotNull ( nameof ( _parameterDictionary ) ) ]
544- private void EnsureParameterDictionary ( )
545- {
546- if ( _parameterDictionary == null )
547- {
548- _parameterDictionary = new Dictionary < string , string > ( ) ; //initial size 0
549- }
550- }
499+ private static bool IsLinearWhiteSpaceChar ( char ch ) =>
500+ ch <= ' ' && Array . IndexOf ( s_linearWhiteSpaceChars , ch ) != - 1 ;
551501
552502 #endregion Private Methods
553503
@@ -556,13 +506,7 @@ private void EnsureParameterDictionary()
556506 private string ? _contentType ;
557507 private string _type = string . Empty ;
558508 private string _subType = string . Empty ;
559- private readonly string _originalString ;
560509 private Dictionary < string , string > ? _parameterDictionary ;
561- private readonly bool _isInitialized ;
562-
563- private const string Quote = "\" " ;
564- private const char SemicolonSeparator = ';' ;
565- private const char EqualSeparator = '=' ;
566510
567511 //This array is sorted by the ascii value of these characters.
568512 private static readonly char [ ] s_allowedCharacters =
0 commit comments