@@ -195,65 +195,111 @@ private static PropagationContext ExtractFromSingleHeader<T>(PropagationContext
195195 {
196196 try
197197 {
198- var header = getter ( carrier , XB3Combined ) ? . FirstOrDefault ( ) ;
199- if ( string . IsNullOrWhiteSpace ( header ) )
198+ var headers = getter ( carrier , XB3Combined ) ;
199+ if ( headers == null )
200200 {
201201 return context ;
202202 }
203203
204- var parts =
205- #if NET
206- header . Split ( XB3CombinedDelimiter ) ;
207- #else
208- header ! . Split ( XB3CombinedDelimiter ) ;
209- #endif
204+ var header = headers . FirstOrDefault ( ) ;
210205
211- if ( parts . Length is < 2 or > 4 )
212- {
213- return context ;
214- }
206+ return string . IsNullOrWhiteSpace ( header )
207+ ? context
208+ : ! TryExtractSingleHeaderContext ( header , out var traceId , out var spanId , out var traceOptions )
209+ ? context
210+ : new PropagationContext (
211+ new ActivityContext ( traceId , spanId , traceOptions , isRemote : true ) ,
212+ context . Baggage ) ;
213+ }
214+ catch ( Exception e )
215+ {
216+ OpenTelemetryApiEventSource . Log . ActivityContextExtractException ( nameof ( B3Propagator ) , e ) ;
217+ return context ;
218+ }
219+ }
215220
216- var traceIdStr = parts [ 0 ] ;
217- if ( string . IsNullOrWhiteSpace ( traceIdStr ) )
218- {
219- return context ;
220- }
221+ private static bool TryExtractSingleHeaderContext (
222+ string header ,
223+ out ActivityTraceId traceId ,
224+ out ActivitySpanId spanId ,
225+ out ActivityTraceFlags traceOptions )
226+ {
227+ traceId = default ;
228+ spanId = default ;
229+ traceOptions = ActivityTraceFlags . None ;
230+
231+ var headerValue = header . AsSpan ( ) ;
232+ var position = 0 ;
233+ var traceIdStr = ReadNextPart ( headerValue , position , out position ) ;
234+ if ( position >= headerValue . Length || traceIdStr . IsEmpty )
235+ {
236+ return false ;
237+ }
238+
239+ var spanIdStr = ReadNextPart ( headerValue , position , out position ) ;
240+ if ( spanIdStr . IsEmpty )
241+ {
242+ return false ;
243+ }
221244
222- if ( traceIdStr . Length == 16 )
245+ ReadOnlySpan < char > traceFlagsStr = default ;
246+ if ( position < headerValue . Length )
247+ {
248+ traceFlagsStr = ReadNextPart ( headerValue , position , out position ) ;
249+ if ( position < headerValue . Length )
223250 {
224- // This is an 8-byte traceID.
225- traceIdStr = UpperTraceId + traceIdStr ;
251+ _ = ReadNextPart ( headerValue , position , out position ) ;
252+ if ( position < headerValue . Length )
253+ {
254+ return false ;
255+ }
226256 }
257+ }
227258
228- var traceId = ActivityTraceId . CreateFromString ( traceIdStr . AsSpan ( ) ) ;
259+ traceId = CreateTraceId ( traceIdStr ) ;
260+ spanId = ActivitySpanId . CreateFromString ( spanIdStr ) ;
229261
230- var spanIdStr = parts [ 1 ] ;
231- if ( string . IsNullOrWhiteSpace ( spanIdStr ) )
232- {
233- return context ;
234- }
262+ if ( IsSampledValue ( traceFlagsStr ) ||
263+ traceFlagsStr . Equals ( FlagsValue . AsSpan ( ) , StringComparison . Ordinal ) )
264+ {
265+ traceOptions |= ActivityTraceFlags . Recorded ;
266+ }
235267
236- var spanId = ActivitySpanId . CreateFromString ( spanIdStr . AsSpan ( ) ) ;
268+ return true ;
269+ }
237270
238- var traceOptions = ActivityTraceFlags . None ;
239- if ( parts . Length > 2 )
240- {
241- var traceFlagsStr = parts [ 2 ] ;
242- if ( SampledValues . Contains ( traceFlagsStr )
243- || FlagsValue . Equals ( traceFlagsStr , StringComparison . Ordinal ) )
244- {
245- traceOptions |= ActivityTraceFlags . Recorded ;
246- }
247- }
271+ private static bool IsSampledValue ( ReadOnlySpan < char > value ) =>
272+ value . Equals ( SampledValue . AsSpan ( ) , StringComparison . Ordinal ) ||
273+ value . Equals ( LegacySampledValue . AsSpan ( ) , StringComparison . Ordinal ) ;
248274
249- return new PropagationContext (
250- new ActivityContext ( traceId , spanId , traceOptions , isRemote : true ) ,
251- context . Baggage ) ;
275+ private static ActivityTraceId CreateTraceId ( ReadOnlySpan < char > traceId )
276+ {
277+ if ( traceId . Length == 16 )
278+ {
279+ Span < char > fullTraceId = stackalloc char [ UpperTraceId . Length + 16 ] ;
280+
281+ UpperTraceId . AsSpan ( ) . CopyTo ( fullTraceId ) ;
282+ traceId . CopyTo ( fullTraceId . Slice ( UpperTraceId . Length ) ) ;
283+
284+ return ActivityTraceId . CreateFromString ( fullTraceId ) ;
252285 }
253- catch ( Exception e )
286+
287+ return ActivityTraceId . CreateFromString ( traceId ) ;
288+ }
289+
290+ private static ReadOnlySpan < char > ReadNextPart ( ReadOnlySpan < char > header , int position , out int nextPosition )
291+ {
292+ var remaining = header . Slice ( position ) ;
293+ var separatorIndex = remaining . IndexOf ( XB3CombinedDelimiter ) ;
294+ if ( separatorIndex < 0 )
254295 {
255- OpenTelemetryApiEventSource . Log . ActivityContextExtractException ( nameof ( B3Propagator ) , e ) ;
256- return context ;
296+ nextPosition = header . Length ;
297+ var part = remaining ;
298+ return part ;
257299 }
300+
301+ var result = remaining . Slice ( 0 , separatorIndex ) ;
302+ nextPosition = position + separatorIndex + 1 ;
303+ return result ;
258304 }
259305}
0 commit comments