@@ -44,6 +44,94 @@ public sealed class DynamicEventSchema
4444
4545 public int MaxOccurrence { get ; init ; } = 1 ;
4646
47+ internal static bool TryComputeOffset ( byte [ ] payload , List < int > unadjustedArrayLengthOffsets , List < int > arrayElementSizes , int unadjustedOffset , out int adjustedOffset )
48+ {
49+ //
50+ // Any offsets on or before the first array content will have their
51+ // actual offset equals to the unadjusted offsets. In particular,
52+ // the offset to the first array's length is never adjusted. So we can
53+ // find the length of the first array.
54+ //
55+ int adjustment = 0 ;
56+ for ( int i = 0 ; i < unadjustedArrayLengthOffsets . Count ; i ++ )
57+ {
58+ int unadjustedArrayLengthOffset = unadjustedArrayLengthOffsets [ i ] ;
59+ if ( unadjustedOffset > unadjustedArrayLengthOffset )
60+ {
61+ if ( payload . Length <= unadjustedArrayLengthOffset )
62+ {
63+ adjustedOffset = 0 ;
64+ return false ;
65+ }
66+
67+ //
68+ // If we had a second array, the second arrays unadjusted offsets will
69+ // be earlier than its actual offset, but we know how to adjust it. So
70+ // we can get the actual offset to the second array's length.
71+ //
72+ byte arrayLength = payload [ unadjustedArrayLengthOffset + adjustment ] ;
73+ adjustment += arrayLength * arrayElementSizes [ i ] ;
74+ }
75+ else
76+ {
77+ //
78+ // If the offset are are looking for is not after the kth array length
79+ // Then we should stop computing the adjustment
80+ //
81+ break ;
82+ }
83+ }
84+
85+ adjustedOffset = unadjustedOffset + adjustment ;
86+ return true ;
87+ }
88+
89+ internal static int ComputeOffset ( byte [ ] payload , List < int > unadjustedArrayLengthOffsets , List < int > arrayElementSizes , int unadjustedOffset )
90+ {
91+ if ( TryComputeOffset ( payload , unadjustedArrayLengthOffsets , arrayElementSizes , unadjustedOffset , out int adjustedOffset ) )
92+ {
93+ return adjustedOffset ;
94+ }
95+ else
96+ {
97+ throw new Exception ( "Fail to compute offset, this should not happen" ) ;
98+ }
99+ }
100+
101+ internal static bool IsSupportedPrimitiveType ( Type type )
102+ {
103+ return
104+ ( type == typeof ( ushort ) ) ||
105+ ( type == typeof ( uint ) ) ||
106+ ( type == typeof ( float ) ) ||
107+ ( type == typeof ( ulong ) ) ||
108+ ( type == typeof ( byte ) ) ||
109+ ( type == typeof ( bool ) ) ||
110+ false ;
111+ }
112+
113+ internal static int Size ( Type type )
114+ {
115+ if ( type == typeof ( ushort ) ) { return 2 ; }
116+ else if ( type == typeof ( uint ) ) { return 4 ; }
117+ else if ( type == typeof ( float ) ) { return 8 ; }
118+ else if ( type == typeof ( ulong ) ) { return 8 ; }
119+ else if ( type == typeof ( byte ) ) { return 1 ; }
120+ else if ( type == typeof ( bool ) ) { return 1 ; }
121+ else { throw new Exception ( "Wrong type" ) ; }
122+ }
123+
124+ internal static object Decode ( Type type , byte [ ] payload , int offset )
125+ {
126+ if ( type == typeof ( ushort ) ) { return BitConverter . ToUInt16 ( payload , offset ) ; }
127+ else if ( type == typeof ( uint ) ) { return BitConverter . ToUInt32 ( payload , offset ) ; }
128+ else if ( type == typeof ( float ) ) { return BitConverter . ToSingle ( payload , offset ) ; }
129+ else if ( type == typeof ( ulong ) ) { return BitConverter . ToUInt64 ( payload , offset ) ; }
130+ else if ( type == typeof ( byte ) ) { return payload [ offset ] ; }
131+ else if ( type == typeof ( bool ) ) { return BitConverter . ToBoolean ( payload , offset ) ; }
132+ else { throw new Exception ( "Wrong type" ) ; }
133+ }
134+
47135 internal static CompiledSchema Compile ( DynamicEventSchema dynamicEventSchema , bool allowPartialSchema = true )
48136 {
49137 if ( DynamicEventSchemas != null && DynamicEventSchemas . ContainsKey ( dynamicEventSchema . DynamicEventName ) )
@@ -61,6 +149,17 @@ internal static CompiledSchema Compile(DynamicEventSchema dynamicEventSchema, bo
61149 }
62150 schema . MinOccurrence = dynamicEventSchema . MinOccurrence ;
63151 schema . MaxOccurrence = dynamicEventSchema . MaxOccurrence ;
152+
153+ //
154+ // With array, a field in an event no longer have a fixed offset.
155+ // Unadjusted offsets are offsets as if all the arrays are empty
156+ // This list will store the unadjusted offsets to array lengths
157+ //
158+ // This is sufficient to get to the actual offsets, once we have
159+ // the payload, see TryComputeOffset for more details.
160+ //
161+ List < int > unadjustedArrayLengthOffsets = new List < int > ( ) ;
162+ List < int > arrayElementSizes = new List < int > ( ) ;
64163 int offset = 0 ;
65164 foreach ( KeyValuePair < string , Type > field in dynamicEventSchema . Fields )
66165 {
@@ -69,39 +168,42 @@ internal static CompiledSchema Compile(DynamicEventSchema dynamicEventSchema, bo
69168 DynamicEventSchemas ? . Clear ( ) ;
70169 throw new Exception ( $ "Provided event named { dynamicEventSchema . DynamicEventName } has a duplicated field named { field . Key } ") ;
71170 }
72- schema . Add ( field . Key , new DynamicEventField { FieldOffset = offset , FieldType = field . Value } ) ;
171+ Func < byte [ ] , object > ? fieldFetcher = null ;
73172
74- if ( field . Value == typeof ( ushort ) )
75- {
76- offset += 2 ;
77- }
78- else if ( field . Value == typeof ( uint ) )
79- {
80- offset += 4 ;
81- }
82- else if ( field . Value == typeof ( float ) )
83- {
84- offset += 4 ;
85- }
86- else if ( field . Value == typeof ( ulong ) )
87- {
88- offset += 8 ;
89- }
90- else if ( field . Value == typeof ( byte ) )
173+ // The local variable makes sure we capture the value of the offset variable in the lambdas
174+ int currentOffset = offset ;
175+ if ( IsSupportedPrimitiveType ( field . Value ) )
91176 {
92- offset += 1 ;
177+ fieldFetcher = ( payload ) => Decode ( field . Value , payload , ComputeOffset ( payload , unadjustedArrayLengthOffsets , arrayElementSizes , currentOffset ) ) ;
178+ offset += Size ( field . Value ) ;
93179 }
94- else if ( field . Value == typeof ( bool ) )
180+ else if ( field . Value . IsArray && IsSupportedPrimitiveType ( field . Value . GetElementType ( ) ) )
95181 {
182+ Type elementType = field . Value . GetElementType ( ) ;
183+ int elementSize = Size ( elementType ) ;
184+ fieldFetcher = ( payload ) =>
185+ {
186+ int unadjustedArrayLengthOffset = ComputeOffset ( payload , unadjustedArrayLengthOffsets , arrayElementSizes , currentOffset ) ;
187+ int length = ( int ) payload [ unadjustedArrayLengthOffset ] ;
188+ Array result = Array . CreateInstance ( elementType , length ) ;
189+ for ( int i = 0 ; i < length ; i ++ )
190+ {
191+ result . SetValue ( Decode ( elementType , payload , unadjustedArrayLengthOffset + 1 + elementSize * i ) , i ) ;
192+ }
193+ return result ;
194+ } ;
195+ unadjustedArrayLengthOffsets . Add ( offset ) ;
196+ arrayElementSizes . Add ( elementSize ) ;
96197 offset += 1 ;
97198 }
98199 else
99200 {
100201 DynamicEventSchemas ? . Clear ( ) ;
101202 throw new Exception ( $ "Provided event named { dynamicEventSchema . DynamicEventName } has a field named { field . Key } using an unsupported type { field . Value } ") ;
102203 }
204+ schema . Add ( field . Key , new DynamicEventField { FieldFetcher = fieldFetcher } ) ;
103205 }
104- schema . Size = offset ;
206+ schema . SizeValidator = ( payload ) => payload . Length == ComputeOffset ( payload , unadjustedArrayLengthOffsets , arrayElementSizes , offset ) ;
105207 return schema ;
106208 }
107209
@@ -124,15 +226,14 @@ public static void Set(List<DynamicEventSchema> dynamicEventSchemas, bool allowP
124226
125227 internal sealed class DynamicEventField
126228 {
127- public required int FieldOffset { get ; init ; }
128- public required Type FieldType { get ; init ; }
229+ public required Func < byte [ ] , object > FieldFetcher ;
129230 }
130231
131232 internal sealed class CompiledSchema : Dictionary < string , DynamicEventField >
132233 {
133234 public int MinOccurrence { get ; set ; }
134235 public int MaxOccurrence { get ; set ; }
135- public int Size { get ; set ; }
236+ public Func < byte [ ] , bool > SizeValidator { get ; set ; }
136237 }
137238
138239 internal sealed class DynamicIndex : DynamicObject
@@ -212,44 +313,13 @@ public DynamicEventObject(GCDynamicEvent dynamicEvent, CompiledSchema schema)
212313 {
213314 this . name = dynamicEvent . Name ;
214315 this . fieldValues = new Dictionary < string , object > ( ) ;
215- if ( dynamicEvent . Payload . Length != schema . Size )
316+ if ( ! schema . SizeValidator ( dynamicEvent . Payload ) )
216317 {
217318 throw new Exception ( $ "Event { dynamicEvent . Name } does not have matching size") ;
218319 }
219320 foreach ( KeyValuePair < string , DynamicEventField > field in schema )
220321 {
221- object ? value = null ;
222- int fieldOffset = field . Value . FieldOffset ;
223- Type fieldType = field . Value . FieldType ;
224-
225- if ( fieldType == typeof ( ushort ) )
226- {
227- value = BitConverter . ToUInt16 ( dynamicEvent . Payload , fieldOffset ) ;
228- }
229- else if ( fieldType == typeof ( uint ) )
230- {
231- value = BitConverter . ToUInt32 ( dynamicEvent . Payload , fieldOffset ) ;
232- }
233- else if ( fieldType == typeof ( float ) )
234- {
235- value = BitConverter . ToSingle ( dynamicEvent . Payload , fieldOffset ) ;
236- }
237- else if ( fieldType == typeof ( ulong ) )
238- {
239- value = BitConverter . ToUInt64 ( dynamicEvent . Payload , fieldOffset ) ;
240- }
241- else if ( fieldType == typeof ( byte ) )
242- {
243- value = dynamicEvent . Payload [ fieldOffset ] ;
244- }
245- else if ( fieldType == typeof ( bool ) )
246- {
247- value = BitConverter . ToBoolean ( dynamicEvent . Payload , fieldOffset ) ;
248- }
249- else
250- {
251- throw new Exception ( $ "Provided schema has a field named { field . Key } using an unsupported type { fieldType } ") ;
252- }
322+ object value = field . Value . FieldFetcher ( dynamicEvent . Payload ) ;
253323 this . fieldValues . Add ( field . Key , value ) ;
254324 }
255325 this . fieldValues . Add ( "TimeStamp" , dynamicEvent . TimeStamp ) ;
0 commit comments