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