1010using System . Collections . Generic ;
1111using System . Runtime . CompilerServices ;
1212using Datadog . Trace . SourceGenerators ;
13+ #if NETFRAMEWORK
14+ using Datadog . Trace . VendoredMicrosoftCode . System . Runtime . CompilerServices . Unsafe ;
15+ #endif
1316
1417namespace Datadog . Trace . Agent ;
1518
@@ -18,8 +21,7 @@ namespace Datadog.Trace.Agent;
1821/// </summary>
1922internal readonly struct SpanCollection : IEnumerable < Span >
2023{
21- private readonly Span ? _span ;
22- private readonly Span [ ] ? _spans ;
24+ private readonly object ? _values ;
2325 public readonly int Count ;
2426
2527 /// <summary>
@@ -28,7 +30,7 @@ namespace Datadog.Trace.Agent;
2830 /// <param name="value">The span to include in the collection.</param>
2931 public SpanCollection ( Span value )
3032 {
31- _span = value ;
33+ _values = value ;
3234 Count = 1 ;
3335 }
3436
@@ -38,7 +40,7 @@ public SpanCollection(Span value)
3840 /// <param name="arrayBuilderCapacity">The value to initializer</param>
3941 public SpanCollection ( int arrayBuilderCapacity )
4042 {
41- _spans = new Span [ arrayBuilderCapacity ] ;
43+ _values = new Span [ arrayBuilderCapacity ] ;
4244 Count = 0 ;
4345 }
4446
@@ -49,7 +51,7 @@ public SpanCollection(Span[] values, int count)
4951 {
5052 // We assume that the caller is "sensible" here, and doesn't set count > values.Length,
5153 // but that will get hit "safely" elsewhere if it happens
52- _spans = values ;
54+ _values = values ;
5355 Count = count ;
5456 }
5557
@@ -67,18 +69,20 @@ public Span? RootSpan
6769 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
6870 get
6971 {
70- if ( _span is not null )
72+ // Take local copy of _values so type checks remain valid even if the SpanCollection is overwritten in memory
73+ var value = _values ;
74+ if ( value is Span span )
7175 {
72- return _span ;
76+ return span ;
7377 }
7478
75- if ( _spans is null )
79+ if ( value is null )
7680 {
7781 return null ;
7882 }
7983
8084 // Not Span, not null, can only be SpanArray
81- return _spans [ 0 ] ;
85+ return Unsafe . As < Span [ ] > ( value ) [ 0 ] ;
8286 }
8387 }
8488
@@ -92,16 +96,19 @@ public Span this[int index]
9296 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
9397 get
9498 {
95- if ( _span is not null )
99+ // Take local copy of _values so type checks remain valid even if the SpanCollection is overwritten in memory
100+ object ? value = _values ;
101+ if ( index < Count )
96102 {
97- if ( index == 0 )
103+ if ( value is Span str )
98104 {
99- return _span ;
105+ return str ;
106+ }
107+ else if ( value != null )
108+ {
109+ // Not Span, not null, can only be Span[]
110+ return Unsafe . As < Span [ ] > ( value ) [ index ] ;
100111 }
101- }
102- else if ( _spans is not null && index < Count )
103- {
104- return _spans [ index ] ;
105112 }
106113
107114 return OutOfBounds ( ) ; // throws
@@ -116,19 +123,23 @@ public Span this[int index]
116123 /// <returns>The concatenation of <paramref name="values"/> and <paramref name="value"/>.</returns>
117124 public static SpanCollection Append ( in SpanCollection values , Span value )
118125 {
119- if ( values . _span is not null )
126+ // Take local copy of _values so type checks remain valid even if the SpanCollection is overwritten in memory
127+ var current = values . _values ;
128+ if ( current is null )
120129 {
121- // We use a default capacity of 4 spans
122- // 2 Spans would cover 25% not covered by single span case, 4 covers ~ 70%, 8 covers ~92%
123- return new SpanCollection ( [ values . _span , value , null ! , null ! ] , 2 ) ;
130+ return new SpanCollection ( value ) ;
124131 }
125132
126- if ( values . _spans is null )
133+ if ( current is Span span )
127134 {
128- return new SpanCollection ( value ) ;
135+ // We use a default capacity of 4 spans
136+ // 2 Spans would cover 25% not covered by single span case, 4 covers ~ 70%, 8 covers ~92%
137+ return new SpanCollection ( [ span , value , null ! , null ! ] , 2 ) ;
129138 }
130139
131- var array = GrowIfNeeded ( values . _spans , values . Count ) ;
140+ // Not Span, not null, can only be Span[], so add the span
141+ var array = Unsafe . As < Span [ ] > ( current ) ;
142+ array = GrowIfNeeded ( array , values . Count ) ;
132143 array [ values . Count ] = value ;
133144 return new SpanCollection ( array , values . Count + 1 ) ;
134145 }
@@ -150,17 +161,20 @@ private static Span OutOfBounds()
150161 /// </remarks>
151162 public ArraySegment < Span > ToArray ( )
152163 {
153- if ( _spans is not null )
164+ // Take local copy of _values so type checks remain valid even if the SpanCollection is overwritten in memory
165+ object ? value = _values ;
166+ if ( value is Span [ ] values )
154167 {
155- return new ArraySegment < Span > ( _spans , 0 , Count ) ;
168+ return new ArraySegment < Span > ( values , 0 , Count ) ;
156169 }
157- else if ( _span is not null )
170+ else if ( value != null )
158171 {
159- return new ArraySegment < Span > ( [ _span ] , 0 , 1 ) ;
172+ // value not array, can only be Span
173+ return new ArraySegment < Span > ( [ Unsafe . As < Span > ( value ) ] , 0 , 1 ) ;
160174 }
161175 else
162176 {
163- return new ArraySegment < Span > ( [ ] , 0 , 0 ) ;
177+ return new ArraySegment < Span > ( Array . Empty < Span > ( ) , 0 , 0 ) ;
164178 }
165179 }
166180
@@ -183,7 +197,7 @@ private static Span[] GrowIfNeeded(Span[] array, int currentCount)
183197 /// <returns>An enumerator that can be used to iterate through the <see cref="SpanCollection" />.</returns>
184198 public Enumerator GetEnumerator ( )
185199 {
186- return new Enumerator ( _span , _spans , Count ) ;
200+ return new Enumerator ( _values , Count ) ;
187201 }
188202
189203 /// <inheritdoc cref="GetEnumerator()" />
@@ -208,9 +222,9 @@ public struct Enumerator : IEnumerator<Span>
208222 private int _index ;
209223 private Span ? _current ;
210224
211- internal Enumerator ( Span ? span , Span [ ] ? spans , int count )
225+ internal Enumerator ( object ? value , int count )
212226 {
213- if ( span is not null )
227+ if ( value is Span span )
214228 {
215229 _values = null ;
216230 _current = span ;
@@ -219,13 +233,22 @@ internal Enumerator(Span? span, Span[]? spans, int count)
219233 else
220234 {
221235 _current = null ;
222- _values = spans ;
236+ _values = Unsafe . As < Span [ ] > ( value ) ;
223237 _count = count ;
224238 }
225239
226240 _index = 0 ;
227241 }
228242
243+ /// <summary>
244+ /// Initializes a new instance of the <see cref="Enumerator"/> struct.
245+ /// </summary>
246+ /// <param name="values">The <see cref="SpanCollection"/> to enumerate.</param>
247+ public Enumerator ( ref SpanCollection values )
248+ : this ( values . _values , values . Count )
249+ {
250+ }
251+
229252 /// <summary>
230253 /// Gets the element at the current position of the enumerator.
231254 /// </summary>
0 commit comments