4
4
using System ;
5
5
using System . Buffers . Binary ;
6
6
using System . Collections . Generic ;
7
+ using System . Numerics ;
8
+ using System . Runtime . CompilerServices ;
7
9
8
10
namespace Microsoft . Diagnostics . DataContractReader ;
9
11
@@ -15,7 +17,7 @@ public struct TargetPointer
15
17
public TargetPointer ( ulong value ) => Value = value ;
16
18
}
17
19
18
- internal sealed unsafe class Target
20
+ public sealed unsafe class Target
19
21
{
20
22
private const int StackAllocByteThreshold = 1024 ;
21
23
@@ -29,7 +31,7 @@ private readonly struct Configuration
29
31
private readonly Reader _reader ;
30
32
31
33
private readonly IReadOnlyDictionary < string , int > _contracts = new Dictionary < string , int > ( ) ;
32
- private readonly TargetPointer [ ] _pointerData = [ ] ;
34
+ private readonly IReadOnlyDictionary < string , ( ulong Value , string ? Type ) > _globals = new Dictionary < string , ( ulong , string ? ) > ( ) ;
33
35
34
36
public static bool TryCreate ( ulong contractDescriptor , delegate * unmanaged< ulong , byte * , uint , void * , int > readFromTarget , void * readContext , out Target ? target )
35
37
{
@@ -49,11 +51,30 @@ private Target(Configuration config, ContractDescriptorParser.ContractDescriptor
49
51
_config = config ;
50
52
_reader = reader ;
51
53
52
- // TODO: [cdac] Read globals and types
54
+ // TODO: [cdac] Read types
53
55
// note: we will probably want to store the globals and types into a more usable form
54
56
_contracts = descriptor . Contracts ?? [ ] ;
55
57
56
- _pointerData = pointerData ;
58
+ // Read globals and map indirect values to pointer data
59
+ if ( descriptor . Globals is not null )
60
+ {
61
+ Dictionary < string , ( ulong Value , string ? Type ) > globals = [ ] ;
62
+ foreach ( ( string name , ContractDescriptorParser . GlobalDescriptor global ) in descriptor . Globals )
63
+ {
64
+ ulong value = global . Value ;
65
+ if ( global . Indirect )
66
+ {
67
+ if ( value >= ( ulong ) pointerData . Length )
68
+ throw new InvalidOperationException ( $ "Invalid pointer data index { value } .") ;
69
+
70
+ value = pointerData [ value ] . Value ;
71
+ }
72
+
73
+ globals [ name ] = ( value , global . Type ) ;
74
+ }
75
+
76
+ _globals = globals ;
77
+ }
57
78
}
58
79
59
80
// See docs/design/datacontracts/contract-descriptor.md
@@ -81,7 +102,7 @@ private static bool TryReadContractDescriptor(
81
102
return false ;
82
103
83
104
// Flags - uint32_t
84
- if ( ! TryReadUInt32 ( address , isLittleEndian , reader , out uint flags ) )
105
+ if ( ! TryRead ( address , isLittleEndian , reader , out uint flags ) )
85
106
return false ;
86
107
87
108
address += sizeof ( uint ) ;
@@ -92,7 +113,7 @@ private static bool TryReadContractDescriptor(
92
113
config = new Configuration { IsLittleEndian = isLittleEndian , PointerSize = pointerSize } ;
93
114
94
115
// Descriptor size - uint32_t
95
- if ( ! TryReadUInt32 ( address , config . IsLittleEndian , reader , out uint descriptorSize ) )
116
+ if ( ! TryRead ( address , config . IsLittleEndian , reader , out uint descriptorSize ) )
96
117
return false ;
97
118
98
119
address += sizeof ( uint ) ;
@@ -104,7 +125,7 @@ private static bool TryReadContractDescriptor(
104
125
address += ( uint ) pointerSize ;
105
126
106
127
// Pointer data count - uint32_t
107
- if ( ! TryReadUInt32 ( address , config . IsLittleEndian , reader , out uint pointerDataCount ) )
128
+ if ( ! TryRead ( address , config . IsLittleEndian , reader , out uint pointerDataCount ) )
108
129
return false ;
109
130
110
131
address += sizeof ( uint ) ;
@@ -138,30 +159,33 @@ private static bool TryReadContractDescriptor(
138
159
return true ;
139
160
}
140
161
141
- public uint ReadUInt32 ( ulong address )
162
+ public T Read < T > ( ulong address , out T value ) where T : unmanaged , IBinaryInteger < T > , IMinMaxValue < T >
142
163
{
143
- if ( ! TryReadUInt32 ( address , out uint value ) )
144
- throw new InvalidOperationException ( $ "Failed to read uint32 at 0x{ address : x8} .") ;
164
+ if ( ! TryRead ( address , out value ) )
165
+ throw new InvalidOperationException ( $ "Failed to read { typeof ( T ) } at 0x{ address : x8} .") ;
145
166
146
167
return value ;
147
168
}
148
169
149
- public bool TryReadUInt32 ( ulong address , out uint value )
150
- => TryReadUInt32 ( address , _config . IsLittleEndian , _reader , out value ) ;
170
+ public bool TryRead < T > ( ulong address , out T value ) where T : unmanaged , IBinaryInteger < T > , IMinMaxValue < T >
171
+ => TryRead ( address , _config . IsLittleEndian , _reader , out value ) ;
151
172
152
- private static bool TryReadUInt32 ( ulong address , bool isLittleEndian , Reader reader , out uint value )
173
+ private static bool TryRead < T > ( ulong address , bool isLittleEndian , Reader reader , out T value ) where T : unmanaged , IBinaryInteger < T > , IMinMaxValue < T >
153
174
{
154
- value = 0 ;
155
-
156
- Span < byte > buffer = stackalloc byte [ sizeof ( uint ) ] ;
175
+ value = default ;
176
+ Span < byte > buffer = stackalloc byte [ sizeof ( T ) ] ;
157
177
if ( reader . ReadFromTarget ( address , buffer ) < 0 )
158
178
return false ;
159
179
160
- value = isLittleEndian
161
- ? BinaryPrimitives . ReadUInt32LittleEndian ( buffer )
162
- : BinaryPrimitives . ReadUInt32BigEndian ( buffer ) ;
180
+ return isLittleEndian
181
+ ? T . TryReadLittleEndian ( buffer , ! IsSigned < T > ( ) , out value )
182
+ : T . TryReadBigEndian ( buffer , ! IsSigned < T > ( ) , out value ) ;
183
+ }
163
184
164
- return true ;
185
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
186
+ private static bool IsSigned < T > ( ) where T : struct , INumberBase < T > , IMinMaxValue < T >
187
+ {
188
+ return T . IsNegative ( T . MinValue ) ;
165
189
}
166
190
167
191
public TargetPointer ReadPointer ( ulong address )
@@ -183,21 +207,79 @@ private static bool TryReadPointer(ulong address, Configuration config, Reader r
183
207
if ( reader . ReadFromTarget ( address , buffer ) < 0 )
184
208
return false ;
185
209
186
- if ( config . PointerSize == sizeof ( uint ) )
210
+ if ( config . PointerSize == sizeof ( uint )
211
+ && TryRead ( address , config . IsLittleEndian , reader , out uint value32 ) )
187
212
{
188
- pointer = new TargetPointer (
189
- config . IsLittleEndian
190
- ? BinaryPrimitives . ReadUInt32LittleEndian ( buffer )
191
- : BinaryPrimitives . ReadUInt32BigEndian ( buffer ) ) ;
213
+ pointer = new TargetPointer ( value32 ) ;
214
+ return true ;
192
215
}
193
- else if ( config . PointerSize == sizeof ( ulong ) )
216
+ else if ( config . PointerSize == sizeof ( ulong )
217
+ && TryRead ( address , config . IsLittleEndian , reader , out ulong value64 ) )
194
218
{
195
- pointer = new TargetPointer (
196
- config . IsLittleEndian
197
- ? BinaryPrimitives . ReadUInt64LittleEndian ( buffer )
198
- : BinaryPrimitives . ReadUInt64BigEndian ( buffer ) ) ;
219
+ pointer = new TargetPointer ( value64 ) ;
220
+ return true ;
199
221
}
200
222
223
+ return false ;
224
+ }
225
+
226
+ public T ReadGlobal < T > ( string name ) where T : struct , INumber < T >
227
+ {
228
+ if ( ! TryReadGlobal ( name , out T value ) )
229
+ throw new InvalidOperationException ( $ "Failed to read global { typeof ( T ) } '{ name } '.") ;
230
+
231
+ return value ;
232
+ }
233
+
234
+ public bool TryReadGlobal < T > ( string name , out T value ) where T : struct , INumber < T > , INumberBase < T >
235
+ {
236
+ value = default ;
237
+ if ( ! _globals . TryGetValue ( name , out ( ulong Value , string ? Type ) global ) )
238
+ return false ;
239
+
240
+ // TODO: [cdac] Move type validation out of the read such that it does not have to happen for every read
241
+ if ( global . Type is not null )
242
+ {
243
+ string ? expectedType = Type . GetTypeCode ( typeof ( T ) ) switch
244
+ {
245
+ TypeCode . SByte => "int8" ,
246
+ TypeCode . Byte => "uint8" ,
247
+ TypeCode . Int16 => "int16" ,
248
+ TypeCode . UInt16 => "uint16" ,
249
+ TypeCode . Int32 => "int32" ,
250
+ TypeCode . UInt32 => "uint32" ,
251
+ TypeCode . Int64 => "int64" ,
252
+ TypeCode . UInt64 => "uint64" ,
253
+ _ => null ,
254
+ } ;
255
+ if ( expectedType is null || global . Type != expectedType )
256
+ {
257
+ return false ;
258
+ }
259
+ }
260
+
261
+ value = T . CreateChecked ( global . Value ) ;
262
+ return true ;
263
+ }
264
+
265
+ public TargetPointer ReadGlobalPointer ( string name )
266
+ {
267
+ if ( ! TryReadGlobalPointer ( name , out TargetPointer pointer ) )
268
+ throw new InvalidOperationException ( $ "Failed to read global pointer '{ name } '.") ;
269
+
270
+ return pointer ;
271
+ }
272
+
273
+ public bool TryReadGlobalPointer ( string name , out TargetPointer pointer )
274
+ {
275
+ pointer = TargetPointer . Null ;
276
+ if ( ! _globals . TryGetValue ( name , out ( ulong Value , string ? Type ) global ) )
277
+ return false ;
278
+
279
+ if ( global . Type is not null && Array . IndexOf ( [ "pointer" , "nint" , "nuint" ] , global . Type ) == - 1 )
280
+ return false ;
281
+
282
+ pointer = new TargetPointer ( global . Value ) ;
201
283
return true ;
202
284
}
203
285
0 commit comments