@@ -44,6 +44,94 @@ public sealed class DynamicEventSchema
44
44
45
45
public int MaxOccurrence { get ; init ; } = 1 ;
46
46
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
+
47
135
internal static CompiledSchema Compile ( DynamicEventSchema dynamicEventSchema , bool allowPartialSchema = true )
48
136
{
49
137
if ( DynamicEventSchemas != null && DynamicEventSchemas . ContainsKey ( dynamicEventSchema . DynamicEventName ) )
@@ -61,6 +149,17 @@ internal static CompiledSchema Compile(DynamicEventSchema dynamicEventSchema, bo
61
149
}
62
150
schema . MinOccurrence = dynamicEventSchema . MinOccurrence ;
63
151
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 > ( ) ;
64
163
int offset = 0 ;
65
164
foreach ( KeyValuePair < string , Type > field in dynamicEventSchema . Fields )
66
165
{
@@ -69,39 +168,42 @@ internal static CompiledSchema Compile(DynamicEventSchema dynamicEventSchema, bo
69
168
DynamicEventSchemas ? . Clear ( ) ;
70
169
throw new Exception ( $ "Provided event named { dynamicEventSchema . DynamicEventName } has a duplicated field named { field . Key } ") ;
71
170
}
72
- schema . Add ( field . Key , new DynamicEventField { FieldOffset = offset , FieldType = field . Value } ) ;
171
+ Func < byte [ ] , object > ? fieldFetcher = null ;
73
172
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 ) )
91
176
{
92
- offset += 1 ;
177
+ fieldFetcher = ( payload ) => Decode ( field . Value , payload , ComputeOffset ( payload , unadjustedArrayLengthOffsets , arrayElementSizes , currentOffset ) ) ;
178
+ offset += Size ( field . Value ) ;
93
179
}
94
- else if ( field . Value == typeof ( bool ) )
180
+ else if ( field . Value . IsArray && IsSupportedPrimitiveType ( field . Value . GetElementType ( ) ) )
95
181
{
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 ) ;
96
197
offset += 1 ;
97
198
}
98
199
else
99
200
{
100
201
DynamicEventSchemas ? . Clear ( ) ;
101
202
throw new Exception ( $ "Provided event named { dynamicEventSchema . DynamicEventName } has a field named { field . Key } using an unsupported type { field . Value } ") ;
102
203
}
204
+ schema . Add ( field . Key , new DynamicEventField { FieldFetcher = fieldFetcher } ) ;
103
205
}
104
- schema . Size = offset ;
206
+ schema . SizeValidator = ( payload ) => payload . Length == ComputeOffset ( payload , unadjustedArrayLengthOffsets , arrayElementSizes , offset ) ;
105
207
return schema ;
106
208
}
107
209
@@ -124,15 +226,14 @@ public static void Set(List<DynamicEventSchema> dynamicEventSchemas, bool allowP
124
226
125
227
internal sealed class DynamicEventField
126
228
{
127
- public required int FieldOffset { get ; init ; }
128
- public required Type FieldType { get ; init ; }
229
+ public required Func < byte [ ] , object > FieldFetcher ;
129
230
}
130
231
131
232
internal sealed class CompiledSchema : Dictionary < string , DynamicEventField >
132
233
{
133
234
public int MinOccurrence { get ; set ; }
134
235
public int MaxOccurrence { get ; set ; }
135
- public int Size { get ; set ; }
236
+ public Func < byte [ ] , bool > SizeValidator { get ; set ; }
136
237
}
137
238
138
239
internal sealed class DynamicIndex : DynamicObject
@@ -212,44 +313,13 @@ public DynamicEventObject(GCDynamicEvent dynamicEvent, CompiledSchema schema)
212
313
{
213
314
this . name = dynamicEvent . Name ;
214
315
this . fieldValues = new Dictionary < string , object > ( ) ;
215
- if ( dynamicEvent . Payload . Length != schema . Size )
316
+ if ( ! schema . SizeValidator ( dynamicEvent . Payload ) )
216
317
{
217
318
throw new Exception ( $ "Event { dynamicEvent . Name } does not have matching size") ;
218
319
}
219
320
foreach ( KeyValuePair < string , DynamicEventField > field in schema )
220
321
{
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 ) ;
253
323
this . fieldValues . Add ( field . Key , value ) ;
254
324
}
255
325
this . fieldValues . Add ( "TimeStamp" , dynamicEvent . TimeStamp ) ;
0 commit comments