@@ -86,28 +86,20 @@ internal ContentType(string contentType!!)
86
86
ValidateCarriageReturns ( contentType ) ;
87
87
88
88
//Begin Parsing
89
- int semiColonIndex = contentType . IndexOf ( SemicolonSeparator ) ;
89
+ int semiColonIndex = contentType . IndexOf ( ';' ) ;
90
90
91
91
if ( semiColonIndex == - 1 )
92
92
{
93
93
// Parse content type similar to - type/subtype
94
- ParseTypeAndSubType ( contentType ) ;
94
+ ParseTypeAndSubType ( contentType . AsSpan ( ) ) ;
95
95
}
96
96
else
97
97
{
98
98
// 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 ) ) ;
101
101
}
102
102
}
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 ;
111
103
}
112
104
113
105
#endregion Internal Constructors
@@ -146,14 +138,8 @@ internal string SubTypeComponent
146
138
/// type/subtype ; param1=value1 ; param2=value2 ; param3="value3"
147
139
/// This will return an enumerator over a dictionary of the parameter/value pairs.
148
140
/// </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 ( ) ;
157
143
#endregion Internal Properties
158
144
159
145
#region Internal Methods
@@ -225,13 +211,7 @@ public override string ToString()
225
211
{
226
212
if ( _contentType == null )
227
213
{
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 ) ) ;
235
215
236
216
StringBuilder stringBuilder = new StringBuilder ( _type ) ;
237
217
stringBuilder . Append ( PackUriHelper . ForwardSlashChar ) ;
@@ -242,10 +222,10 @@ public override string ToString()
242
222
foreach ( string parameterKey in _parameterDictionary . Keys )
243
223
{
244
224
stringBuilder . Append ( s_linearWhiteSpaceChars [ 0 ] ) ;
245
- stringBuilder . Append ( SemicolonSeparator ) ;
225
+ stringBuilder . Append ( ';' ) ;
246
226
stringBuilder . Append ( s_linearWhiteSpaceChars [ 0 ] ) ;
247
227
stringBuilder . Append ( parameterKey ) ;
248
- stringBuilder . Append ( EqualSeparator ) ;
228
+ stringBuilder . Append ( '=' ) ;
249
229
stringBuilder . Append ( _parameterDictionary [ parameterKey ] ) ;
250
230
}
251
231
}
@@ -284,7 +264,9 @@ private static void ValidateCarriageReturns(string contentType)
284
264
index = contentType . IndexOf ( s_linearWhiteSpaceChars [ 2 ] , ++ index ) ;
285
265
}
286
266
else
267
+ {
287
268
throw new ArgumentException ( SR . InvalidLinearWhiteSpaceCharacter ) ;
269
+ }
288
270
}
289
271
}
290
272
@@ -294,18 +276,20 @@ private static void ValidateCarriageReturns(string contentType)
294
276
/// </summary>
295
277
/// <param name="typeAndSubType">substring that has the type and subType of the content type</param>
296
278
/// <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 )
298
280
{
299
281
//okay to trim at this point the end of the string as Linear White Spaces(LWS) chars are allowed here.
300
282
typeAndSubType = typeAndSubType . TrimEnd ( s_linearWhiteSpaceChars ) ;
301
283
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
+ {
305
288
throw new ArgumentException ( SR . InvalidTypeSubType ) ;
289
+ }
306
290
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 ( ) ) ;
309
293
}
310
294
311
295
/// <summary>
@@ -314,13 +298,13 @@ private void ParseTypeAndSubType(string typeAndSubType)
314
298
/// <param name="parameterAndValue">This string has the parameter and value pair of the form
315
299
/// parameter=value</param>
316
300
/// <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 )
318
302
{
319
- while ( parameterAndValue != string . Empty )
303
+ while ( ! parameterAndValue . IsEmpty )
320
304
{
321
305
//At this point the first character MUST be a semi-colon
322
306
//First time through this test is serving more as an assert.
323
- if ( parameterAndValue [ 0 ] != SemicolonSeparator )
307
+ if ( parameterAndValue [ 0 ] != ';' )
324
308
throw new ArgumentException ( SR . ExpectingSemicolon ) ;
325
309
326
310
//At this point if we have just one semicolon, then its an error.
@@ -330,13 +314,13 @@ private void ParseParameterAndValue(string parameterAndValue)
330
314
throw new ArgumentException ( SR . ExpectingParameterValuePairs ) ;
331
315
332
316
//Removing the leading ; from the string
333
- parameterAndValue = parameterAndValue . Substring ( 1 ) ;
317
+ parameterAndValue = parameterAndValue . Slice ( 1 ) ;
334
318
335
319
//okay to trim start as there can be spaces before the beginning
336
320
//of the parameter name.
337
321
parameterAndValue = parameterAndValue . TrimStart ( s_linearWhiteSpaceChars ) ;
338
322
339
- int equalSignIndex = parameterAndValue . IndexOf ( EqualSeparator ) ;
323
+ int equalSignIndex = parameterAndValue . IndexOf ( '=' ) ;
340
324
341
325
if ( equalSignIndex <= 0 || equalSignIndex == ( parameterAndValue . Length - 1 ) )
342
326
throw new ArgumentException ( SR . InvalidParameterValuePair ) ;
@@ -346,13 +330,11 @@ private void ParseParameterAndValue(string parameterAndValue)
346
330
//Get length of the parameter value
347
331
int parameterValueLength = GetLengthOfParameterValue ( parameterAndValue , parameterStartIndex ) ;
348
332
349
- EnsureParameterDictionary ( ) ;
333
+ ( _parameterDictionary ??= new ( ) ) . Add (
334
+ ValidateToken ( parameterAndValue . Slice ( 0 , equalSignIndex ) . ToString ( ) ) ,
335
+ ValidateQuotedStringOrToken ( parameterAndValue . Slice ( parameterStartIndex , parameterValueLength ) . ToString ( ) ) ) ;
350
336
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 ) ;
356
338
}
357
339
}
358
340
@@ -362,7 +344,7 @@ private void ParseParameterAndValue(string parameterAndValue)
362
344
/// <param name="s"></param>
363
345
/// <param name="startIndex">Starting index for parsing</param>
364
346
/// <returns></returns>
365
- private static int GetLengthOfParameterValue ( string s , int startIndex )
347
+ private static int GetLengthOfParameterValue ( ReadOnlySpan < char > s , int startIndex )
366
348
{
367
349
Debug . Assert ( s != null ) ;
368
350
@@ -373,23 +355,20 @@ private static int GetLengthOfParameterValue(string s, int startIndex)
373
355
//a ';' as the terminator for the token value.
374
356
if ( s [ startIndex ] != '"' )
375
357
{
376
- int semicolonIndex = s . IndexOf ( SemicolonSeparator , startIndex ) ;
358
+ int semicolonIndex = s . Slice ( startIndex ) . IndexOf ( ';' ) ;
377
359
378
360
if ( semicolonIndex != - 1 )
379
361
{
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
385
365
}
386
366
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.
392
370
length = s . Length ;
371
+ }
393
372
}
394
373
else
395
374
{
@@ -400,10 +379,14 @@ private static int GetLengthOfParameterValue(string s, int startIndex)
400
379
401
380
while ( ! found )
402
381
{
403
- length = s . IndexOf ( '"' , ++ length ) ;
382
+ int startingLength = ++ length ;
383
+ length = s . Slice ( startingLength ) . IndexOf ( '"' ) ;
404
384
405
385
if ( length == - 1 )
386
+ {
406
387
throw new ArgumentException ( SR . InvalidParameterValue ) ;
388
+ }
389
+ length += startingLength ; // IndexOf result is based on slicing from startingLength
407
390
408
391
if ( s [ length - 1 ] != '\\ ' )
409
392
{
@@ -453,11 +436,15 @@ private static string ValidateQuotedStringOrToken(string parameterValue)
453
436
throw new ArgumentException ( SR . InvalidParameterValue ) ;
454
437
455
438
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
+ }
459
444
else
445
+ {
460
446
ValidateToken ( parameterValue ) ;
447
+ }
461
448
462
449
return parameterValue ;
463
450
}
@@ -466,7 +453,7 @@ private static string ValidateQuotedStringOrToken(string parameterValue)
466
453
/// This method validates if the text in the quoted string
467
454
/// </summary>
468
455
/// <param name="quotedText"></param>
469
- private static void ValidateQuotedText ( string quotedText )
456
+ private static void ValidateQuotedText ( ReadOnlySpan < char > quotedText )
470
457
{
471
458
//empty is okay
472
459
@@ -477,9 +464,8 @@ private static void ValidateQuotedText(string quotedText)
477
464
478
465
if ( quotedText [ i ] <= ' ' || quotedText [ i ] >= 0xFF )
479
466
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 ] != '\\ ' ) )
483
469
throw new ArgumentException ( SR . InvalidParameterValue ) ;
484
470
}
485
471
}
@@ -490,34 +476,18 @@ private static void ValidateQuotedText(string quotedText)
490
476
/// </summary>
491
477
/// <param name="character">input character</param>
492
478
/// <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 ;
497
481
498
482
/// <summary>
499
483
/// Returns true if the input character is an ASCII digit or letter
500
484
/// Returns false if the input character is not an ASCII digit or letter
501
485
/// </summary>
502
486
/// <param name="character">input character</param>
503
487
/// <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 ) ;
521
491
522
492
/// <summary>
523
493
/// Returns true if the input character is one of the Linear White Space characters -
@@ -526,28 +496,8 @@ private static bool IsAsciiLetter(char character)
526
496
/// </summary>
527
497
/// <param name="ch">input character</param>
528
498
/// <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 ;
551
501
552
502
#endregion Private Methods
553
503
@@ -556,13 +506,7 @@ private void EnsureParameterDictionary()
556
506
private string ? _contentType ;
557
507
private string _type = string . Empty ;
558
508
private string _subType = string . Empty ;
559
- private readonly string _originalString ;
560
509
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 = '=' ;
566
510
567
511
//This array is sorted by the ascii value of these characters.
568
512
private static readonly char [ ] s_allowedCharacters =
0 commit comments