1
1
// Copyright The OpenTelemetry Authors
2
2
// SPDX-License-Identifier: Apache-2.0
3
3
4
- using System . Buffers ;
5
4
using System . Diagnostics ;
6
5
using System . Runtime . CompilerServices ;
6
+ using System . Text ;
7
7
using OpenTelemetry . Internal ;
8
8
9
9
namespace OpenTelemetry . Context . Propagation ;
@@ -76,7 +76,7 @@ public override PropagationContext Extract<T>(PropagationContext context, T carr
76
76
var tracestateCollection = getter ( carrier , TraceState ) ;
77
77
if ( tracestateCollection ? . Any ( ) ?? false )
78
78
{
79
- TryExtractTracestate ( tracestateCollection , out tracestate ) ;
79
+ TryExtractTracestate ( tracestateCollection . ToArray ( ) , out tracestate ) ;
80
80
}
81
81
82
82
return new PropagationContext (
@@ -220,37 +220,31 @@ internal static bool TryExtractTraceparent(string traceparent, out ActivityTrace
220
220
return true ;
221
221
}
222
222
223
- internal static bool TryExtractTracestate ( IEnumerable < string > tracestateCollection , out string tracestateResult )
223
+ internal static bool TryExtractTracestate ( string [ ] tracestateCollection , out string tracestateResult )
224
224
{
225
225
tracestateResult = string . Empty ;
226
226
227
- char [ ] ? rentedArray = null ;
228
- Span < char > traceStateBuffer = stackalloc char [ 128 ] ; // 256B
229
- Span < char > keyLookupBuffer = stackalloc char [ 96 ] ; // 192B (3x32 keys)
230
- int keys = 0 ;
231
- int charsWritten = 0 ;
232
-
233
- try
227
+ if ( tracestateCollection != null )
234
228
{
235
- foreach ( var tracestateItem in tracestateCollection )
229
+ var keySet = new HashSet < string > ( ) ;
230
+ var result = new StringBuilder ( ) ;
231
+ for ( int i = 0 ; i < tracestateCollection . Length ; ++ i )
236
232
{
237
- var tracestate = tracestateItem . AsSpan ( ) ;
238
- int position = 0 ;
239
-
240
- while ( position < tracestate . Length )
233
+ var tracestate = tracestateCollection [ i ] . AsSpan ( ) ;
234
+ int begin = 0 ;
235
+ while ( begin < tracestate . Length )
241
236
{
242
- int length = tracestate . Slice ( position ) . IndexOf ( ',' ) ;
237
+ int length = tracestate . Slice ( begin ) . IndexOf ( ',' ) ;
243
238
ReadOnlySpan < char > listMember ;
244
-
245
239
if ( length != - 1 )
246
240
{
247
- listMember = tracestate . Slice ( position , length ) . Trim ( ) ;
248
- position += length + 1 ;
241
+ listMember = tracestate . Slice ( begin , length ) . Trim ( ) ;
242
+ begin += length + 1 ;
249
243
}
250
244
else
251
245
{
252
- listMember = tracestate . Slice ( position ) . Trim ( ) ;
253
- position = tracestate . Length ;
246
+ listMember = tracestate . Slice ( begin ) . Trim ( ) ;
247
+ begin = tracestate . Length ;
254
248
}
255
249
256
250
// https://github.com/w3c/trace-context/blob/master/spec/20-http_request_header_format.md#tracestate-header-field-values
@@ -261,7 +255,7 @@ internal static bool TryExtractTracestate(IEnumerable<string> tracestateCollecti
261
255
continue ;
262
256
}
263
257
264
- if ( keys >= 32 )
258
+ if ( keySet . Count >= 32 )
265
259
{
266
260
// https://github.com/w3c/trace-context/blob/master/spec/20-http_request_header_format.md#list
267
261
// test_tracestate_member_count_limit
@@ -292,107 +286,25 @@ internal static bool TryExtractTracestate(IEnumerable<string> tracestateCollecti
292
286
}
293
287
294
288
// ValidateKey() call above has ensured the key does not contain upper case letters.
295
-
296
- var duplicationCheckLength = Math . Min ( key . Length , 3 ) ;
297
-
298
- if ( keys > 0 )
299
- {
300
- // Fast path check of first three chars for potential duplicated keys
301
- var potentialMatchingKeyPosition = 1 ;
302
- var found = false ;
303
- for ( int i = 0 ; i < keys * 3 ; i += 3 )
304
- {
305
- if ( keyLookupBuffer . Slice ( i , duplicationCheckLength ) . SequenceEqual ( key . Slice ( 0 , duplicationCheckLength ) ) )
306
- {
307
- found = true ;
308
- break ;
309
- }
310
-
311
- potentialMatchingKeyPosition ++ ;
312
- }
313
-
314
- // If the fast check has found a possible duplicate, we need to do a full check
315
- if ( found )
316
- {
317
- var bufferToCompare = traceStateBuffer . Slice ( 0 , charsWritten ) ;
318
-
319
- // We know which key is the first possible duplicate, so skip to that key
320
- // by slicing to the position after the appropriate comma.
321
- for ( int i = 1 ; i < potentialMatchingKeyPosition ; i ++ )
322
- {
323
- var commaIndex = bufferToCompare . IndexOf ( ',' ) ;
324
-
325
- if ( commaIndex > - 1 )
326
- {
327
- bufferToCompare . Slice ( commaIndex ) ;
328
- }
329
- }
330
-
331
- int existingIndex = - 1 ;
332
- while ( ( existingIndex = bufferToCompare . IndexOf ( key ) ) > - 1 )
333
- {
334
- if ( ( existingIndex > 0 && bufferToCompare [ existingIndex - 1 ] != ',' ) || bufferToCompare [ existingIndex + key . Length ] != '=' )
335
- {
336
- continue ; // this is not a key
337
- }
338
-
339
- return false ; // test_tracestate_duplicated_keys
340
- }
341
- }
342
- }
343
-
344
- // Store up to the first three characters of the key for use in the duplicate lookup fast path
345
- var startKeyLookupIndex = keys > 0 ? keys * 3 : 0 ;
346
- key . Slice ( 0 , duplicationCheckLength ) . CopyTo ( keyLookupBuffer . Slice ( startKeyLookupIndex ) ) ;
347
-
348
- // Check we have capacity to write the key and value
349
- var requiredCapacity = charsWritten > 0 ? listMember . Length + 1 : listMember . Length ;
350
-
351
- while ( charsWritten + requiredCapacity > traceStateBuffer . Length )
289
+ if ( ! keySet . Add ( key . ToString ( ) ) )
352
290
{
353
- GrowBuffer ( ref rentedArray , ref traceStateBuffer ) ;
291
+ // test_tracestate_duplicated_keys
292
+ return false ;
354
293
}
355
294
356
- if ( charsWritten > 0 )
295
+ if ( result . Length > 0 )
357
296
{
358
- traceStateBuffer [ charsWritten ++ ] = ',' ;
297
+ result . Append ( ',' ) ;
359
298
}
360
299
361
- listMember . CopyTo ( traceStateBuffer . Slice ( charsWritten ) ) ;
362
- charsWritten += listMember . Length ;
363
-
364
- keys ++ ;
300
+ result . Append ( listMember . ToString ( ) ) ;
365
301
}
366
302
}
367
303
368
- tracestateResult = traceStateBuffer . Slice ( 0 , charsWritten ) . ToString ( ) ;
369
-
370
- return true ;
304
+ tracestateResult = result . ToString ( ) ;
371
305
}
372
- finally
373
- {
374
- if ( rentedArray is not null )
375
- {
376
- ArrayPool < char > . Shared . Return ( rentedArray ) ;
377
- rentedArray = null ;
378
- }
379
- }
380
-
381
- [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
382
- static void GrowBuffer ( ref char [ ] ? array , ref Span < char > buffer )
383
- {
384
- var newBuffer = ArrayPool < char > . Shared . Rent ( buffer . Length * 2 ) ;
385
306
386
- buffer . CopyTo ( newBuffer . AsSpan ( ) ) ;
387
-
388
- if ( array is not null )
389
- {
390
- ArrayPool < char > . Shared . Return ( array ) ;
391
- }
392
-
393
- array = newBuffer ;
394
- buffer = array . AsSpan ( ) ;
395
- }
307
+ return true ;
396
308
}
397
309
398
310
private static byte HexCharToByte ( char c )
0 commit comments