@@ -21,8 +21,16 @@ public static class LightweightTrace
2121 {
2222 private const int Capacity = 1024 ;
2323
24- private const int KeyValueShift = 1 << 10 ;
25- private const int MapValue = 1 << 28 ;
24+ /// <summary>
25+ /// The stride used to encode a composite key where <c>key = id + (value * CompositeValueShift)</c>.
26+ /// Exposed to clarify how counters/events pack both an ID and a value into a single <see cref="int"/>.
27+ /// </summary>
28+ public const int CompositeValueShift = 1 << 10 ; // 1024 distinct IDs per value bucket
29+ /// <summary>
30+ /// A flag bit used in composite keys to indicate that the encoded value should be mapped via <c>eventNameMap</c>.
31+ /// This lets values represent enum-like categories instead of plain numbers when formatting output.
32+ /// </summary>
33+ public const int MapValueFlag = 1 << 28 ;
2634
2735 private static readonly DateTime StartTime = DateTime . UtcNow ;
2836 private static readonly Stopwatch Stopwatch = Stopwatch . StartNew ( ) ;
@@ -57,7 +65,7 @@ public static class LightweightTrace
5765 /// <param name="eventId">The ID of the event to log.</param>
5866 /// <param name="value">The value associated with the event, which can be used for additional context or categorization.</param>
5967 /// <param name="mapValue">If true, the value is treated as a mapped value when generating the diagnostic log.</param>
60- public static void Add ( int eventId , int value , bool mapValue = false ) => Add ( eventId + value * KeyValueShift , mapValue ) ;
68+ public static void Add ( int eventId , int value , bool mapValue = false ) => Add ( EncodeKey ( eventId , value , mapValue ) ) ;
6169 /// <summary>
6270 /// Adds an event to the trace log with the specified numeric event ID.
6371 /// </summary>
@@ -66,14 +74,14 @@ public static class LightweightTrace
6674 public static void Add ( int eventId , bool mapValue = false )
6775 {
6876 var index = Interlocked . Increment ( ref _index ) % Capacity ;
69- Events [ index ] = ( Stopwatch . ElapsedTicks , eventId | ( mapValue ? MapValue : 0 ) ) ;
77+ Events [ index ] = ( Stopwatch . ElapsedTicks , eventId | ( mapValue ? MapValueFlag : 0 ) ) ;
7078#if ! DATACUTE_EXCLUDE_GENERATORSTAGE
71- if ( ( eventId / KeyValueShift ) != Convert . ToInt32 ( GeneratorStage . MethodExit ) )
79+ if ( ( eventId / CompositeValueShift ) != Convert . ToInt32 ( GeneratorStage . MethodExit ) )
7280 {
73- IncrementCount ( GeneratorStage . MethodCall , eventId % KeyValueShift , true ) ;
81+ IncrementCount ( GeneratorStage . MethodCall , eventId % CompositeValueShift , true ) ;
7482 }
7583#else
76- IncrementCount ( eventId % KeyValueShift ) ;
84+ IncrementCount ( eventId % CompositeValueShift ) ;
7785#endif
7886 }
7987
@@ -114,7 +122,7 @@ public static void AppendTrace(this StringBuilder stringBuilder, Dictionary<int,
114122 var textAndValue = GetTextAndValue ( eventNameMap , eventId ) ;
115123 stringBuilder . AppendFormat ( "{0:o} [{1:000}] {2}" ,
116124 StartTime . AddTicks ( timestamp ) ,
117- eventId % KeyValueShift ,
125+ eventId % CompositeValueShift ,
118126 textAndValue )
119127 . AppendLine ( ) ;
120128 }
@@ -170,7 +178,7 @@ public static void AppendTrace(this StringBuilder stringBuilder, Dictionary<int,
170178 /// <param name="counterId">The ID of the counter to increment.</param>
171179 /// <param name="value">The value associated with the counter, which can be used for additional context or categorization.</param>
172180 /// <param name="mapValue">If true, the value is treated as a mapped value when generating the diagnostic log.</param>
173- public static void IncrementCount ( int counterId , int value , bool mapValue = false ) => Counters . AddOrUpdate ( counterId + value * KeyValueShift + ( mapValue ? MapValue : 0 ) , 1 , ( _ , count ) => count + 1 ) ;
181+ public static void IncrementCount ( int counterId , int value , bool mapValue = false ) => Counters . AddOrUpdate ( EncodeKey ( counterId , value , mapValue ) , 1 , ( _ , count ) => count + 1 ) ;
174182
175183 /// <summary>
176184 /// Decrements the value of a given key by 1.
@@ -183,7 +191,7 @@ public static void AppendTrace(this StringBuilder stringBuilder, Dictionary<int,
183191 /// <param name="counterId">The ID of the counter to decrement.</param>
184192 /// <param name="value">The value associated with the counter, which can be used for additional context or categorization.</param>
185193 /// <param name="mapValue">If true, the value is treated as a mapped value when generating the diagnostic log.</param>
186- public static void DecrementCount ( int counterId , int value , bool mapValue = false ) => Counters . AddOrUpdate ( counterId + value * KeyValueShift + ( mapValue ? MapValue : 0 ) , - 1 , ( _ , count ) => count - 1 ) ;
194+ public static void DecrementCount ( int counterId , int value , bool mapValue = false ) => Counters . AddOrUpdate ( EncodeKey ( counterId , value , mapValue ) , - 1 , ( _ , count ) => count - 1 ) ;
187195
188196 /// <summary>
189197 /// Gets a string with the current cache performance metrics.
@@ -199,27 +207,51 @@ public static void AppendCounts(this StringBuilder stringBuilder, Dictionary<int
199207 }
200208
201209 // Order by key for a consistent, readable output
202- foreach ( var kvp in Counters . OrderBy ( kvp => kvp . Key % KeyValueShift ) . ThenBy ( kvp => kvp . Key ) )
210+ foreach ( var kvp in Counters . OrderBy ( kvp => kvp . Key % CompositeValueShift ) . ThenBy ( kvp => kvp . Key ) )
203211 {
204212 int counterId = kvp . Key ;
205213 long count = kvp . Value ;
206214
207215 var textAndValue = GetTextAndValue ( eventNameMap , counterId ) ;
208216 stringBuilder . AppendFormat (
209217 "[{0:000}] {1}: {2}" ,
210- counterId % KeyValueShift , textAndValue , count )
218+ counterId % CompositeValueShift , textAndValue , count )
211219 . AppendLine ( ) ;
212220 }
213221 }
214222
223+ /// <summary>
224+ /// Encodes a composite key combining an ID and a value into a single int.
225+ /// Set <paramref name="mapValue"/> when the value represents a categorical/enum mapping rather than a numeric measurement.
226+ /// </summary>
227+ /// <param name="id">The base ID (0..CompositeValueShift-1).</param>
228+ /// <param name="value">The associated value bucket or enum ordinal.</param>
229+ /// <param name="mapValue">True to mark the value as mapped (name lookup) instead of numeric.</param>
230+ public static int EncodeKey ( int id , int value , bool mapValue = false ) => id + ( value * CompositeValueShift ) + ( mapValue ? MapValueFlag : 0 ) ;
231+
232+ /// <summary>
233+ /// Decodes a composite key into its ID, value, and mapped-value flag.
234+ /// </summary>
235+ /// <param name="key">The composite key previously produced by <see cref="EncodeKey"/> or any API that accepts (id,value).</param>
236+ /// <param name="id">The extracted base ID.</param>
237+ /// <param name="value">The extracted associated value.</param>
238+ /// <param name="isMappedValue">True if the value should be mapped by name for display.</param>
239+ public static void DecodeKey ( int key , out int id , out int value , out bool isMappedValue )
240+ {
241+ isMappedValue = ( key & MapValueFlag ) != 0 ;
242+ var unflagged = key & ~ MapValueFlag ;
243+ id = unflagged % CompositeValueShift ;
244+ value = unflagged / CompositeValueShift ;
245+ }
246+
215247 private static string GetTextAndValue ( Dictionary < int , string > eventNameMap , int key )
216248 {
217- int id = key % KeyValueShift ;
218- int value = key / KeyValueShift ;
249+ int id = key % CompositeValueShift ;
250+ int value = key / CompositeValueShift ;
219251
220- if ( ( key & MapValue ) != 0 )
252+ if ( ( key & MapValueFlag ) != 0 )
221253 {
222- value = ( key & ~ MapValue ) / KeyValueShift ;
254+ value = ( key & ~ MapValueFlag ) / CompositeValueShift ;
223255 }
224256
225257 string text = null ;
@@ -233,9 +265,9 @@ private static string GetTextAndValue(Dictionary<int, string> eventNameMap, int
233265 }
234266
235267 string valueText = null ;
236- if ( key >= KeyValueShift )
268+ if ( key >= CompositeValueShift )
237269 {
238- if ( eventNameMap != null && ( key & MapValue ) != 0 )
270+ if ( eventNameMap != null && ( key & MapValueFlag ) != 0 )
239271 {
240272 eventNameMap . TryGetValue ( value , out valueText ) ;
241273 }
@@ -245,7 +277,7 @@ private static string GetTextAndValue(Dictionary<int, string> eventNameMap, int
245277 }
246278 }
247279
248- return ( key >= KeyValueShift ) ? $ "{ text } ({ valueText } )" : text ;
280+ return ( key >= CompositeValueShift ) ? $ "{ text } ({ valueText } )" : text ;
249281 }
250282
251283 /// <summary>
0 commit comments