44using System ;
55using System . Buffers . Binary ;
66using System . Collections . Generic ;
7+ using System . Numerics ;
8+ using System . Runtime . CompilerServices ;
79
810namespace Microsoft . Diagnostics . DataContractReader ;
911
@@ -15,7 +17,7 @@ public struct TargetPointer
1517 public TargetPointer ( ulong value ) => Value = value ;
1618}
1719
18- internal sealed unsafe class Target
20+ public sealed unsafe class Target
1921{
2022 private const int StackAllocByteThreshold = 1024 ;
2123
@@ -29,7 +31,7 @@ private readonly struct Configuration
2931 private readonly Reader _reader ;
3032
3133 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 ? ) > ( ) ;
3335
3436 public static bool TryCreate ( ulong contractDescriptor , delegate * unmanaged< ulong , byte * , uint , void * , int > readFromTarget , void * readContext , out Target ? target )
3537 {
@@ -49,11 +51,30 @@ private Target(Configuration config, ContractDescriptorParser.ContractDescriptor
4951 _config = config ;
5052 _reader = reader ;
5153
52- // TODO: [cdac] Read globals and types
54+ // TODO: [cdac] Read types
5355 // note: we will probably want to store the globals and types into a more usable form
5456 _contracts = descriptor . Contracts ?? [ ] ;
5557
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+ }
5778 }
5879
5980 // See docs/design/datacontracts/contract-descriptor.md
@@ -81,7 +102,7 @@ private static bool TryReadContractDescriptor(
81102 return false ;
82103
83104 // Flags - uint32_t
84- if ( ! TryReadUInt32 ( address , isLittleEndian , reader , out uint flags ) )
105+ if ( ! TryRead ( address , isLittleEndian , reader , out uint flags ) )
85106 return false ;
86107
87108 address += sizeof ( uint ) ;
@@ -92,7 +113,7 @@ private static bool TryReadContractDescriptor(
92113 config = new Configuration { IsLittleEndian = isLittleEndian , PointerSize = pointerSize } ;
93114
94115 // Descriptor size - uint32_t
95- if ( ! TryReadUInt32 ( address , config . IsLittleEndian , reader , out uint descriptorSize ) )
116+ if ( ! TryRead ( address , config . IsLittleEndian , reader , out uint descriptorSize ) )
96117 return false ;
97118
98119 address += sizeof ( uint ) ;
@@ -104,7 +125,7 @@ private static bool TryReadContractDescriptor(
104125 address += ( uint ) pointerSize ;
105126
106127 // 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 ) )
108129 return false ;
109130
110131 address += sizeof ( uint ) ;
@@ -138,30 +159,33 @@ private static bool TryReadContractDescriptor(
138159 return true ;
139160 }
140161
141- public uint ReadUInt32 ( ulong address )
162+ public T Read < T > ( ulong address , out T value ) where T : unmanaged , IBinaryInteger < T > , IMinMaxValue < T >
142163 {
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} .") ;
145166
146167 return value ;
147168 }
148169
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 ) ;
151172
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 >
153174 {
154- value = 0 ;
155-
156- Span < byte > buffer = stackalloc byte [ sizeof ( uint ) ] ;
175+ value = default ;
176+ Span < byte > buffer = stackalloc byte [ sizeof ( T ) ] ;
157177 if ( reader . ReadFromTarget ( address , buffer ) < 0 )
158178 return false ;
159179
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+ }
163184
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 ) ;
165189 }
166190
167191 public TargetPointer ReadPointer ( ulong address )
@@ -183,21 +207,79 @@ private static bool TryReadPointer(ulong address, Configuration config, Reader r
183207 if ( reader . ReadFromTarget ( address , buffer ) < 0 )
184208 return false ;
185209
186- if ( config . PointerSize == sizeof ( uint ) )
210+ if ( config . PointerSize == sizeof ( uint )
211+ && TryRead ( address , config . IsLittleEndian , reader , out uint value32 ) )
187212 {
188- pointer = new TargetPointer (
189- config . IsLittleEndian
190- ? BinaryPrimitives . ReadUInt32LittleEndian ( buffer )
191- : BinaryPrimitives . ReadUInt32BigEndian ( buffer ) ) ;
213+ pointer = new TargetPointer ( value32 ) ;
214+ return true ;
192215 }
193- else if ( config . PointerSize == sizeof ( ulong ) )
216+ else if ( config . PointerSize == sizeof ( ulong )
217+ && TryRead ( address , config . IsLittleEndian , reader , out ulong value64 ) )
194218 {
195- pointer = new TargetPointer (
196- config . IsLittleEndian
197- ? BinaryPrimitives . ReadUInt64LittleEndian ( buffer )
198- : BinaryPrimitives . ReadUInt64BigEndian ( buffer ) ) ;
219+ pointer = new TargetPointer ( value64 ) ;
220+ return true ;
199221 }
200222
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 ) ;
201283 return true ;
202284 }
203285
0 commit comments