@@ -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