1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Reflection.Internal;
6 using System.Text;
7 using Xunit;
8 
9 namespace System.Reflection.Metadata.Tests
10 {
11     public class BlobReaderTests
12     {
13         [Fact]
Properties()14         public unsafe void Properties()
15         {
16             byte[] buffer = new byte[] { 0, 1, 0, 2, 5, 6 };
17 
18             fixed (byte* bufferPtr = buffer)
19             {
20                 var reader = new BlobReader(bufferPtr, 4);
21                 Assert.True(reader.StartPointer == bufferPtr);
22                 Assert.True(reader.CurrentPointer == bufferPtr);
23                 Assert.Equal(0, reader.Offset);
24                 Assert.Equal(4, reader.RemainingBytes);
25                 Assert.Equal(4, reader.Length);
26 
27                 Assert.Equal(0, reader.ReadByte());
28                 Assert.True(reader.StartPointer == bufferPtr);
29                 Assert.True(reader.CurrentPointer == bufferPtr + 1);
30                 Assert.Equal(1, reader.Offset);
31                 Assert.Equal(3, reader.RemainingBytes);
32                 Assert.Equal(4, reader.Length);
33 
34                 Assert.Equal(1, reader.ReadInt16());
35                 Assert.True(reader.StartPointer == bufferPtr);
36                 Assert.True(reader.CurrentPointer == bufferPtr + 3);
37                 Assert.Equal(3, reader.Offset);
38                 Assert.Equal(1, reader.RemainingBytes);
39                 Assert.Equal(4, reader.Length);
40 
41                 Assert.Throws<BadImageFormatException>(() => reader.ReadInt16());
42                 Assert.True(reader.StartPointer == bufferPtr);
43                 Assert.True(reader.CurrentPointer == bufferPtr + 3);
44                 Assert.Equal(3, reader.Offset);
45                 Assert.Equal(1, reader.RemainingBytes);
46                 Assert.Equal(4, reader.Length);
47 
48                 Assert.Equal(2, reader.ReadByte());
49                 Assert.True(reader.StartPointer == bufferPtr);
50                 Assert.True(reader.CurrentPointer == bufferPtr + 4);
51                 Assert.Equal(4, reader.Offset);
52                 Assert.Equal(0, reader.RemainingBytes);
53                 Assert.Equal(4, reader.Length);
54             }
55         }
56 
57         [Fact]
Offset()58         public unsafe void Offset()
59         {
60             byte[] buffer = new byte[] { 0, 1, 0, 2, 5, 6 };
61 
62             fixed (byte* bufferPtr = buffer)
63             {
64                 var reader = new BlobReader(bufferPtr, 4);
65                 Assert.Equal(0, reader.Offset);
66 
67                 reader.Offset = 0;
68                 Assert.Equal(0, reader.Offset);
69                 Assert.Equal(4, reader.RemainingBytes);
70                 Assert.True(reader.CurrentPointer == bufferPtr);
71 
72                 reader.Offset = 3;
73                 Assert.Equal(3, reader.Offset);
74                 Assert.Equal(1, reader.RemainingBytes);
75                 Assert.True(reader.CurrentPointer == bufferPtr + 3);
76 
77                 reader.Offset = 1;
78                 Assert.Equal(1, reader.Offset);
79                 Assert.Equal(3, reader.RemainingBytes);
80                 Assert.True(reader.CurrentPointer == bufferPtr + 1);
81 
82                 Assert.Equal(1, reader.ReadByte());
83                 Assert.Equal(2, reader.Offset);
84                 Assert.Equal(2, reader.RemainingBytes);
85                 Assert.True(reader.CurrentPointer == bufferPtr + 2);
86 
87                 reader.Offset = 4;
88                 Assert.Equal(4, reader.Offset);
89                 Assert.Equal(0, reader.RemainingBytes);
90                 Assert.True(reader.CurrentPointer == bufferPtr + 4);
91 
92                 Assert.Throws<BadImageFormatException>(() => reader.Offset = 5);
93                 Assert.Equal(4, reader.Offset);
94                 Assert.Equal(0, reader.RemainingBytes);
95                 Assert.True(reader.CurrentPointer == bufferPtr + 4);
96 
97                 Assert.Throws<BadImageFormatException>(() => reader.Offset = -1);
98                 Assert.Equal(4, reader.Offset);
99                 Assert.Equal(0, reader.RemainingBytes);
100                 Assert.True(reader.CurrentPointer == bufferPtr + 4);
101 
102                 Assert.Throws<BadImageFormatException>(() => reader.Offset = int.MaxValue);
103                 Assert.Throws<BadImageFormatException>(() => reader.Offset = int.MinValue);
104             }
105         }
106 
107         [Fact]
PublicBlobReaderCtorValidatesArgs()108         public unsafe void PublicBlobReaderCtorValidatesArgs()
109         {
110             byte* bufferPtrForLambda;
111             byte[] buffer = new byte[4] { 0, 1, 0, 2 };
112 
113             fixed (byte* bufferPtr = buffer)
114             {
115                 bufferPtrForLambda = bufferPtr;
116                 Assert.Throws<ArgumentOutOfRangeException>(() => new BlobReader(bufferPtrForLambda, -1));
117             }
118 
119             Assert.Throws<ArgumentNullException>(() => new BlobReader(null, 1));
120 
121             Assert.Equal(0, new BlobReader(null, 0).Length); // this is valid
122             Assert.Throws<BadImageFormatException>(() => new BlobReader(null, 0).ReadByte()); // but can't read anything non-empty from it...
123             Assert.Same(String.Empty, new BlobReader(null, 0).ReadUtf8NullTerminated()); // can read empty string.
124         }
125 
126         [Fact]
ReadBoolean1()127         public unsafe void ReadBoolean1()
128         {
129             byte[] buffer = new byte[] { 1, 0xff, 0, 2 };
130             fixed (byte* bufferPtr = buffer)
131             {
132                 var reader = new BlobReader(new MemoryBlock(bufferPtr, buffer.Length));
133 
134                 Assert.True(reader.ReadBoolean());
135                 Assert.True(reader.ReadBoolean());
136                 Assert.False(reader.ReadBoolean());
137                 Assert.True(reader.ReadBoolean());
138             }
139         }
140 
141         [Fact]
ReadFromMemoryReader()142         public unsafe void ReadFromMemoryReader()
143         {
144             byte[] buffer = new byte[4] { 0, 1, 0, 2 };
145             fixed (byte* bufferPtr = buffer)
146             {
147                 var reader = new BlobReader(new MemoryBlock(bufferPtr, buffer.Length));
148 
149                 Assert.Equal(0, reader.Offset);
150                 Assert.Throws<BadImageFormatException>(() => reader.ReadUInt64());
151                 Assert.Equal(0, reader.Offset);
152 
153                 reader.Offset = 1;
154                 Assert.Throws<BadImageFormatException>(() => reader.ReadDouble());
155                 Assert.Equal(1, reader.Offset);
156 
157                 reader.Offset = 2;
158                 Assert.Throws<BadImageFormatException>(() => reader.ReadUInt32());
159                 Assert.Equal((ushort)0x0200, reader.ReadUInt16());
160                 Assert.Equal(4, reader.Offset);
161 
162                 reader.Offset = 2;
163                 Assert.Throws<BadImageFormatException>(() => reader.ReadSingle());
164                 Assert.Equal(2, reader.Offset);
165 
166                 reader.Offset = 0;
167                 Assert.Equal(9.404242E-38F, reader.ReadSingle());
168                 Assert.Equal(4, reader.Offset);
169 
170                 reader.Offset = 3;
171                 Assert.Throws<BadImageFormatException>(() => reader.ReadUInt16());
172                 Assert.Equal((byte)0x02, reader.ReadByte());
173                 Assert.Equal(4, reader.Offset);
174 
175                 reader.Offset = 0;
176                 Assert.Equal("\u0000\u0001\u0000\u0002", reader.ReadUTF8(4));
177                 Assert.Equal(4, reader.Offset);
178 
179                 reader.Offset = 0;
180                 Assert.Throws<BadImageFormatException>(() => reader.ReadUTF8(5));
181                 Assert.Equal(0, reader.Offset);
182 
183                 reader.Offset = 0;
184                 Assert.Throws<BadImageFormatException>(() => reader.ReadUTF8(-1));
185                 Assert.Equal(0, reader.Offset);
186 
187                 reader.Offset = 0;
188                 Assert.Equal("\u0100\u0200", reader.ReadUTF16(4));
189                 Assert.Equal(4, reader.Offset);
190 
191                 reader.Offset = 0;
192                 Assert.Throws<BadImageFormatException>(() => reader.ReadUTF16(5));
193                 Assert.Equal(0, reader.Offset);
194 
195                 reader.Offset = 0;
196                 Assert.Throws<BadImageFormatException>(() => reader.ReadUTF16(-1));
197                 Assert.Equal(0, reader.Offset);
198 
199                 reader.Offset = 0;
200                 Assert.Throws<BadImageFormatException>(() => reader.ReadUTF16(6));
201                 Assert.Equal(0, reader.Offset);
202 
203                 reader.Offset = 0;
204                 Assert.Equal(buffer, reader.ReadBytes(4));
205                 Assert.Equal(4, reader.Offset);
206 
207                 reader.Offset = 0;
208                 Assert.Same(string.Empty, reader.ReadUtf8NullTerminated());
209                 Assert.Equal(1, reader.Offset);
210 
211                 reader.Offset = 1;
212                 Assert.Equal("\u0001", reader.ReadUtf8NullTerminated());
213                 Assert.Equal(3, reader.Offset);
214 
215                 reader.Offset = 3;
216                 Assert.Equal("\u0002", reader.ReadUtf8NullTerminated());
217                 Assert.Equal(4, reader.Offset);
218 
219                 reader.Offset = 0;
220                 Assert.Same(string.Empty, reader.ReadUtf8NullTerminated());
221                 Assert.Equal(1, reader.Offset);
222 
223                 reader.Offset = 0;
224                 Assert.Throws<BadImageFormatException>(() => reader.ReadBytes(5));
225                 Assert.Equal(0, reader.Offset);
226 
227                 reader.Offset = 0;
228                 Assert.Throws<BadImageFormatException>(() => reader.ReadBytes(int.MinValue));
229                 Assert.Equal(0, reader.Offset);
230 
231                 reader.Offset = 0;
232                 Assert.Throws<BadImageFormatException>(() => reader.GetMemoryBlockAt(-1, 1));
233                 Assert.Equal(0, reader.Offset);
234 
235                 reader.Offset = 0;
236                 Assert.Throws<BadImageFormatException>(() => reader.GetMemoryBlockAt(1, -1));
237                 Assert.Equal(0, reader.Offset);
238 
239                 reader.Offset = 0;
240                 Assert.Equal(3, reader.GetMemoryBlockAt(1, 3).Length);
241                 Assert.Equal(0, reader.Offset);
242 
243                 reader.Offset = 3;
244                 reader.ReadByte();
245                 Assert.Equal(4, reader.Offset);
246 
247                 reader.Offset = 4;
248                 Assert.Equal(0, reader.ReadBytes(0).Length);
249 
250                 reader.Offset = 4;
251                 int value;
252                 Assert.False(reader.TryReadCompressedInteger(out value));
253                 Assert.Equal(BlobReader.InvalidCompressedInteger, value);
254 
255                 reader.Offset = 4;
256                 Assert.Throws<BadImageFormatException>(() => reader.ReadCompressedInteger());
257 
258                 reader.Offset = 4;
259                 Assert.Equal(SerializationTypeCode.Invalid, reader.ReadSerializationTypeCode());
260 
261                 reader.Offset = 4;
262                 Assert.Equal(SignatureTypeCode.Invalid, reader.ReadSignatureTypeCode());
263 
264                 reader.Offset = 4;
265                 Assert.Equal(default(EntityHandle), reader.ReadTypeHandle());
266 
267                 reader.Offset = 4;
268                 Assert.Throws<BadImageFormatException>(() => reader.ReadBoolean());
269 
270                 reader.Offset = 4;
271                 Assert.Throws<BadImageFormatException>(() => reader.ReadByte());
272 
273                 reader.Offset = 4;
274                 Assert.Throws<BadImageFormatException>(() => reader.ReadSByte());
275 
276                 reader.Offset = 4;
277                 Assert.Throws<BadImageFormatException>(() => reader.ReadUInt32());
278 
279                 reader.Offset = 4;
280                 Assert.Throws<BadImageFormatException>(() => reader.ReadInt32());
281 
282                 reader.Offset = 4;
283                 Assert.Throws<BadImageFormatException>(() => reader.ReadUInt64());
284 
285                 reader.Offset = 4;
286                 Assert.Throws<BadImageFormatException>(() => reader.ReadInt64());
287 
288                 reader.Offset = 4;
289                 Assert.Throws<BadImageFormatException>(() => reader.ReadSingle());
290 
291                 reader.Offset = 4;
292                 Assert.Throws<BadImageFormatException>(() => reader.ReadDouble());
293 
294                 reader.Offset = 4;
295             }
296 
297             byte[] buffer2 = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };
298             fixed (byte* bufferPtr2 = buffer2)
299             {
300                 var reader = new BlobReader(new MemoryBlock(bufferPtr2, buffer2.Length));
301                 Assert.Equal(reader.Offset, 0);
302                 Assert.Equal(0x0807060504030201UL, reader.ReadUInt64());
303                 Assert.Equal(reader.Offset, 8);
304 
305                 reader.Reset();
306                 Assert.Equal(reader.Offset, 0);
307                 Assert.Equal(0x0807060504030201L, reader.ReadInt64());
308 
309                 reader.Reset();
310                 Assert.Equal(reader.Offset, 0);
311                 Assert.Equal(BitConverter.ToDouble(buffer2, 0), reader.ReadDouble());
312             }
313         }
314 
315         [Fact]
ValidatePeekReferenceSize()316         public unsafe void ValidatePeekReferenceSize()
317         {
318             byte[] buffer = new byte[8] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01 };
319             fixed (byte* bufferPtr = buffer)
320             {
321                 var block = new MemoryBlock(bufferPtr, buffer.Length);
322 
323                 // small ref size always fits in 16 bits
324                 Assert.Equal(0xFFFF, block.PeekReference(0, smallRefSize: true));
325                 Assert.Equal(0xFFFF, block.PeekReference(4, smallRefSize: true));
326                 Assert.Equal(0xFFFFU, block.PeekTaggedReference(0, smallRefSize: true));
327                 Assert.Equal(0xFFFFU, block.PeekTaggedReference(4, smallRefSize: true));
328                 Assert.Equal(0x01FFU, block.PeekTaggedReference(6, smallRefSize: true));
329 
330                 // large ref size throws on > RIDMask when tagged variant is not used.
331                 Assert.Throws<BadImageFormatException>(() => block.PeekReference(0, smallRefSize: false));
332                 Assert.Throws<BadImageFormatException>(() => block.PeekReference(4, smallRefSize: false));
333 
334                 // large ref size does not throw when Tagged variant is used.
335                 Assert.Equal(0xFFFFFFFFU, block.PeekTaggedReference(0, smallRefSize: false));
336                 Assert.Equal(0x01FFFFFFU, block.PeekTaggedReference(4, smallRefSize: false));
337 
338                 // bounds check applies in all cases
339                 Assert.Throws<BadImageFormatException>(() => block.PeekReference(7, smallRefSize: true));
340                 Assert.Throws<BadImageFormatException>(() => block.PeekReference(5, smallRefSize: false));
341             }
342         }
343 
344         [Fact]
ReadFromMemoryBlock()345         public unsafe void ReadFromMemoryBlock()
346         {
347             byte[] buffer = new byte[4] { 0, 1, 0, 2 };
348             fixed (byte* bufferPtr = buffer)
349             {
350                 var block = new MemoryBlock(bufferPtr, buffer.Length);
351 
352                 Assert.Throws<BadImageFormatException>(() => block.PeekUInt32(int.MaxValue));
353                 Assert.Throws<BadImageFormatException>(() => block.PeekUInt32(-1));
354                 Assert.Throws<BadImageFormatException>(() => block.PeekUInt32(int.MinValue));
355                 Assert.Throws<BadImageFormatException>(() => block.PeekUInt32(4));
356                 Assert.Throws<BadImageFormatException>(() => block.PeekUInt32(1));
357                 Assert.Equal(0x02000100U, block.PeekUInt32(0));
358 
359                 Assert.Throws<BadImageFormatException>(() => block.PeekUInt16(int.MaxValue));
360                 Assert.Throws<BadImageFormatException>(() => block.PeekUInt16(-1));
361                 Assert.Throws<BadImageFormatException>(() => block.PeekUInt16(int.MinValue));
362                 Assert.Throws<BadImageFormatException>(() => block.PeekUInt16(4));
363                 Assert.Equal(0x0200, block.PeekUInt16(2));
364 
365                 int bytesRead;
366 
367                 MetadataStringDecoder stringDecoder = MetadataStringDecoder.DefaultUTF8;
368                 Assert.Throws<BadImageFormatException>(() => block.PeekUtf8NullTerminated(int.MaxValue, null, stringDecoder, out bytesRead));
369                 Assert.Throws<BadImageFormatException>(() => block.PeekUtf8NullTerminated(-1, null, stringDecoder, out bytesRead));
370                 Assert.Throws<BadImageFormatException>(() => block.PeekUtf8NullTerminated(int.MinValue, null, stringDecoder, out bytesRead));
371                 Assert.Throws<BadImageFormatException>(() => block.PeekUtf8NullTerminated(5, null, stringDecoder, out bytesRead));
372 
373                 Assert.Throws<BadImageFormatException>(() => block.GetMemoryBlockAt(-1, 1));
374                 Assert.Throws<BadImageFormatException>(() => block.GetMemoryBlockAt(1, -1));
375                 Assert.Throws<BadImageFormatException>(() => block.GetMemoryBlockAt(0, -1));
376                 Assert.Throws<BadImageFormatException>(() => block.GetMemoryBlockAt(-1, 0));
377                 Assert.Throws<BadImageFormatException>(() => block.GetMemoryBlockAt(-int.MaxValue, int.MaxValue));
378                 Assert.Throws<BadImageFormatException>(() => block.GetMemoryBlockAt(int.MaxValue, -int.MaxValue));
379                 Assert.Throws<BadImageFormatException>(() => block.GetMemoryBlockAt(int.MaxValue, int.MaxValue));
380                 Assert.Throws<BadImageFormatException>(() => block.GetMemoryBlockAt(block.Length, -1));
381                 Assert.Throws<BadImageFormatException>(() => block.GetMemoryBlockAt(-1, block.Length));
382 
383 
384                 Assert.Equal("\u0001", block.PeekUtf8NullTerminated(1, null, stringDecoder, out bytesRead));
385                 Assert.Equal(bytesRead, 2);
386 
387                 Assert.Equal("\u0002", block.PeekUtf8NullTerminated(3, null, stringDecoder, out bytesRead));
388                 Assert.Equal(bytesRead, 1);
389 
390                 Assert.Equal("", block.PeekUtf8NullTerminated(4, null, stringDecoder, out bytesRead));
391                 Assert.Equal(bytesRead, 0);
392 
393                 byte[] helloPrefix = Encoding.UTF8.GetBytes("Hello");
394 
395                 Assert.Equal("Hello\u0001", block.PeekUtf8NullTerminated(1, helloPrefix, stringDecoder, out bytesRead));
396                 Assert.Equal(bytesRead, 2);
397 
398                 Assert.Equal("Hello\u0002", block.PeekUtf8NullTerminated(3, helloPrefix, stringDecoder, out bytesRead));
399                 Assert.Equal(bytesRead, 1);
400 
401                 Assert.Equal("Hello", block.PeekUtf8NullTerminated(4, helloPrefix, stringDecoder, out bytesRead));
402                 Assert.Equal(bytesRead, 0);
403             }
404         }
405 
406         [Fact]
IndexOf()407         public unsafe void IndexOf()
408         {
409             byte[] buffer = new byte[]
410             {
411                 0xF0, 0x90, 0x8D,
412             };
413 
414             fixed (byte* bufferPtr = buffer)
415             {
416                 var reader = new BlobReader(bufferPtr, buffer.Length);
417 
418                 Assert.Equal(0, reader.IndexOf(0xF0));
419                 Assert.Equal(1, reader.IndexOf(0x90));
420                 Assert.Equal(2, reader.IndexOf(0x8D));
421                 Assert.Equal(-1, reader.IndexOf(0x8C));
422                 Assert.Equal(-1, reader.IndexOf(0));
423                 Assert.Equal(-1, reader.IndexOf(0xff));
424 
425                 reader.ReadByte();
426                 Assert.Equal(-1, reader.IndexOf(0xF0));
427                 Assert.Equal(0, reader.IndexOf(0x90));
428                 Assert.Equal(1, reader.IndexOf(0x8D));
429 
430                 reader.ReadByte();
431                 Assert.Equal(-1, reader.IndexOf(0xF0));
432                 Assert.Equal(-1, reader.IndexOf(0x90));
433                 Assert.Equal(0, reader.IndexOf(0x8D));
434 
435                 reader.ReadByte();
436                 Assert.Equal(-1, reader.IndexOf(0xF0));
437                 Assert.Equal(-1, reader.IndexOf(0x90));
438                 Assert.Equal(-1, reader.IndexOf(0x8D));
439             }
440         }
441     }
442 }
443