1 // ==++== 2 // 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // 5 // ==--== 6 /*============================================================ 7 ** 8 ** Class: BinaryReader 9 ** 10 ** <OWNER>gpaperin</OWNER> 11 ** 12 ** 13 ** Purpose: Wraps a stream and provides convenient read functionality 14 ** for strings and primitive types. 15 ** 16 ** 17 ============================================================*/ 18 namespace System.IO { 19 20 using System; 21 using System.Runtime; 22 using System.Text; 23 using System.Globalization; 24 using System.Diagnostics.Contracts; 25 using System.Security; 26 27 [System.Runtime.InteropServices.ComVisible(true)] 28 public class BinaryReader : IDisposable 29 { 30 private const int MaxCharBytesSize = 128; 31 32 private Stream m_stream; 33 private byte[] m_buffer; 34 private Decoder m_decoder; 35 private byte[] m_charBytes; 36 private char[] m_singleChar; 37 private char[] m_charBuffer; 38 private int m_maxCharsSize; // From MaxCharBytesSize & Encoding 39 40 // Performance optimization for Read() w/ Unicode. Speeds us up by ~40% 41 private bool m_2BytesPerChar; 42 private bool m_isMemoryStream; // "do we sit on MemoryStream?" for Read/ReadInt32 perf 43 private bool m_leaveOpen; 44 BinaryReader(Stream input)45 public BinaryReader(Stream input) : this(input, new UTF8Encoding(), false) { 46 } 47 BinaryReader(Stream input, Encoding encoding)48 public BinaryReader(Stream input, Encoding encoding) : this(input, encoding, false) { 49 } 50 BinaryReader(Stream input, Encoding encoding, bool leaveOpen)51 public BinaryReader(Stream input, Encoding encoding, bool leaveOpen) { 52 if (input==null) { 53 throw new ArgumentNullException("input"); 54 } 55 if (encoding==null) { 56 throw new ArgumentNullException("encoding"); 57 } 58 if (!input.CanRead) 59 throw new ArgumentException(Environment.GetResourceString("Argument_StreamNotReadable")); 60 Contract.EndContractBlock(); 61 m_stream = input; 62 m_decoder = encoding.GetDecoder(); 63 m_maxCharsSize = encoding.GetMaxCharCount(MaxCharBytesSize); 64 int minBufferSize = encoding.GetMaxByteCount(1); // max bytes per one char 65 if (minBufferSize < 16) 66 minBufferSize = 16; 67 m_buffer = new byte[minBufferSize]; 68 // m_charBuffer and m_charBytes will be left null. 69 70 // For Encodings that always use 2 bytes per char (or more), 71 // special case them here to make Read() & Peek() faster. 72 m_2BytesPerChar = encoding is UnicodeEncoding; 73 // check if BinaryReader is based on MemoryStream, and keep this for it's life 74 // we cannot use "as" operator, since derived classes are not allowed 75 m_isMemoryStream = (m_stream.GetType() == typeof(MemoryStream)); 76 m_leaveOpen = leaveOpen; 77 78 Contract.Assert(m_decoder!=null, "[BinaryReader.ctor]m_decoder!=null"); 79 } 80 81 public virtual Stream BaseStream { 82 get { 83 return m_stream; 84 } 85 } 86 Close()87 public virtual void Close() { 88 Dispose(true); 89 } 90 Dispose(bool disposing)91 protected virtual void Dispose(bool disposing) { 92 if (disposing) { 93 Stream copyOfStream = m_stream; 94 m_stream = null; 95 if (copyOfStream != null && !m_leaveOpen) 96 copyOfStream.Close(); 97 } 98 m_stream = null; 99 m_buffer = null; 100 m_decoder = null; 101 m_charBytes = null; 102 m_singleChar = null; 103 m_charBuffer = null; 104 } 105 Dispose()106 public void Dispose() 107 { 108 Dispose(true); 109 } 110 PeekChar()111 public virtual int PeekChar() { 112 Contract.Ensures(Contract.Result<int>() >= -1); 113 114 if (m_stream==null) __Error.FileNotOpen(); 115 116 if (!m_stream.CanSeek) 117 return -1; 118 long origPos = m_stream.Position; 119 int ch = Read(); 120 m_stream.Position = origPos; 121 return ch; 122 } 123 Read()124 public virtual int Read() { 125 Contract.Ensures(Contract.Result<int>() >= -1); 126 127 if (m_stream==null) { 128 __Error.FileNotOpen(); 129 } 130 return InternalReadOneChar(); 131 } 132 ReadBoolean()133 public virtual bool ReadBoolean(){ 134 FillBuffer(1); 135 return (m_buffer[0]!=0); 136 } 137 ReadByte()138 public virtual byte ReadByte() { 139 // Inlined to avoid some method call overhead with FillBuffer. 140 if (m_stream==null) __Error.FileNotOpen(); 141 142 int b = m_stream.ReadByte(); 143 if (b == -1) 144 __Error.EndOfFile(); 145 return (byte) b; 146 } 147 148 [CLSCompliant(false)] ReadSByte()149 public virtual sbyte ReadSByte() { 150 FillBuffer(1); 151 return (sbyte)(m_buffer[0]); 152 } 153 ReadChar()154 public virtual char ReadChar() { 155 int value = Read(); 156 if (value==-1) { 157 __Error.EndOfFile(); 158 } 159 return (char)value; 160 } 161 ReadInt16()162 public virtual short ReadInt16() { 163 FillBuffer(2); 164 return (short)(m_buffer[0] | m_buffer[1] << 8); 165 } 166 167 [CLSCompliant(false)] ReadUInt16()168 public virtual ushort ReadUInt16(){ 169 FillBuffer(2); 170 return (ushort)(m_buffer[0] | m_buffer[1] << 8); 171 } 172 ReadInt32()173 public virtual int ReadInt32() { 174 if (m_isMemoryStream) { 175 if (m_stream==null) __Error.FileNotOpen(); 176 // read directly from MemoryStream buffer 177 MemoryStream mStream = m_stream as MemoryStream; 178 Contract.Assert(mStream != null, "m_stream as MemoryStream != null"); 179 180 return mStream.InternalReadInt32(); 181 } 182 else 183 { 184 FillBuffer(4); 185 return (int)(m_buffer[0] | m_buffer[1] << 8 | m_buffer[2] << 16 | m_buffer[3] << 24); 186 } 187 } 188 189 [CLSCompliant(false)] ReadUInt32()190 public virtual uint ReadUInt32() { 191 FillBuffer(4); 192 return (uint)(m_buffer[0] | m_buffer[1] << 8 | m_buffer[2] << 16 | m_buffer[3] << 24); 193 } 194 ReadInt64()195 public virtual long ReadInt64() { 196 FillBuffer(8); 197 uint lo = (uint)(m_buffer[0] | m_buffer[1] << 8 | 198 m_buffer[2] << 16 | m_buffer[3] << 24); 199 uint hi = (uint)(m_buffer[4] | m_buffer[5] << 8 | 200 m_buffer[6] << 16 | m_buffer[7] << 24); 201 return (long) ((ulong)hi) << 32 | lo; 202 } 203 204 [CLSCompliant(false)] ReadUInt64()205 public virtual ulong ReadUInt64() { 206 FillBuffer(8); 207 uint lo = (uint)(m_buffer[0] | m_buffer[1] << 8 | 208 m_buffer[2] << 16 | m_buffer[3] << 24); 209 uint hi = (uint)(m_buffer[4] | m_buffer[5] << 8 | 210 m_buffer[6] << 16 | m_buffer[7] << 24); 211 return ((ulong)hi) << 32 | lo; 212 } 213 214 [System.Security.SecuritySafeCritical] // auto-generated ReadSingle()215 public virtual unsafe float ReadSingle() { 216 FillBuffer(4); 217 #if MONO 218 return Mono.Security.BitConverterLE.ToSingle (m_buffer, 0); 219 #else 220 uint tmpBuffer = (uint)(m_buffer[0] | m_buffer[1] << 8 | m_buffer[2] << 16 | m_buffer[3] << 24); 221 return *((float*)&tmpBuffer); 222 #endif 223 } 224 225 [System.Security.SecuritySafeCritical] // auto-generated ReadDouble()226 public virtual unsafe double ReadDouble() { 227 FillBuffer(8); 228 #if MONO 229 return Mono.Security.BitConverterLE.ToDouble (m_buffer, 0); 230 #else 231 uint lo = (uint)(m_buffer[0] | m_buffer[1] << 8 | 232 m_buffer[2] << 16 | m_buffer[3] << 24); 233 uint hi = (uint)(m_buffer[4] | m_buffer[5] << 8 | 234 m_buffer[6] << 16 | m_buffer[7] << 24); 235 236 ulong tmpBuffer = ((ulong)hi) << 32 | lo; 237 return *((double*)&tmpBuffer); 238 #endif 239 } 240 ReadDecimal()241 public virtual decimal ReadDecimal() { 242 FillBuffer(16); 243 try { 244 return Decimal.ToDecimal(m_buffer); 245 } 246 catch (ArgumentException e) { 247 // ReadDecimal cannot leak out ArgumentException 248 throw new IOException(Environment.GetResourceString("Arg_DecBitCtor"), e); 249 } 250 } 251 ReadString()252 public virtual String ReadString() { 253 Contract.Ensures(Contract.Result<String>() != null); 254 255 if (m_stream == null) 256 __Error.FileNotOpen(); 257 258 int currPos = 0; 259 int n; 260 int stringLength; 261 int readLength; 262 int charsRead; 263 264 // Length of the string in bytes, not chars 265 stringLength = Read7BitEncodedInt(); 266 if (stringLength<0) { 267 throw new IOException(Environment.GetResourceString("IO.IO_InvalidStringLen_Len", stringLength)); 268 } 269 270 if (stringLength==0) { 271 return String.Empty; 272 } 273 274 if (m_charBytes==null) { 275 m_charBytes = new byte[MaxCharBytesSize]; 276 } 277 278 if (m_charBuffer == null) { 279 m_charBuffer = new char[m_maxCharsSize]; 280 } 281 282 StringBuilder sb = null; 283 do 284 { 285 readLength = ((stringLength - currPos)>MaxCharBytesSize)?MaxCharBytesSize:(stringLength - currPos); 286 287 n = m_stream.Read(m_charBytes, 0, readLength); 288 if (n==0) { 289 __Error.EndOfFile(); 290 } 291 292 charsRead = m_decoder.GetChars(m_charBytes, 0, n, m_charBuffer, 0); 293 294 if (currPos == 0 && n == stringLength) 295 return new String(m_charBuffer, 0, charsRead); 296 297 if (sb == null) 298 sb = StringBuilderCache.Acquire(stringLength); // Actual string length in chars may be smaller. 299 sb.Append(m_charBuffer, 0, charsRead); 300 currPos +=n; 301 302 } while (currPos<stringLength); 303 304 return StringBuilderCache.GetStringAndRelease(sb); 305 } 306 307 [SecuritySafeCritical] Read(char[] buffer, int index, int count)308 public virtual int Read(char[] buffer, int index, int count) { 309 if (buffer==null) { 310 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); 311 } 312 if (index < 0) { 313 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); 314 } 315 if (count < 0) { 316 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); 317 } 318 if (buffer.Length - index < count) { 319 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); 320 } 321 Contract.Ensures(Contract.Result<int>() >= 0); 322 Contract.Ensures(Contract.Result<int>() <= count); 323 Contract.EndContractBlock(); 324 325 if (m_stream==null) 326 __Error.FileNotOpen(); 327 328 // SafeCritical: index and count have already been verified to be a valid range for the buffer 329 return InternalReadChars(buffer, index, count); 330 } 331 332 [SecurityCritical] InternalReadChars(char[] buffer, int index, int count)333 private int InternalReadChars(char[] buffer, int index, int count) { 334 Contract.Requires(buffer != null); 335 Contract.Requires(index >= 0 && count >= 0); 336 Contract.Assert(m_stream != null); 337 338 int numBytes = 0; 339 int charsRemaining = count; 340 341 if (m_charBytes==null) { 342 m_charBytes = new byte[MaxCharBytesSize]; 343 } 344 345 while (charsRemaining > 0) { 346 int charsRead = 0; 347 // We really want to know what the minimum number of bytes per char 348 // is for our encoding. Otherwise for UnicodeEncoding we'd have to 349 // do ~1+log(n) reads to read n characters. 350 numBytes = charsRemaining; 351 352 // special case for DecoderNLS subclasses when there is a hanging byte from the previous loop 353 DecoderNLS decoder = m_decoder as DecoderNLS; 354 if (decoder != null && decoder.HasState && numBytes > 1) { 355 numBytes -= 1; 356 } 357 358 if (m_2BytesPerChar) 359 numBytes <<= 1; 360 if (numBytes > MaxCharBytesSize) 361 numBytes = MaxCharBytesSize; 362 363 int position = 0; 364 byte[] byteBuffer = null; 365 if (m_isMemoryStream) 366 { 367 MemoryStream mStream = m_stream as MemoryStream; 368 Contract.Assert(mStream != null, "m_stream as MemoryStream != null"); 369 370 position = mStream.InternalGetPosition(); 371 numBytes = mStream.InternalEmulateRead(numBytes); 372 byteBuffer = mStream.InternalGetBuffer(); 373 } 374 else 375 { 376 numBytes = m_stream.Read(m_charBytes, 0, numBytes); 377 byteBuffer = m_charBytes; 378 } 379 380 if (numBytes == 0) { 381 return (count - charsRemaining); 382 } 383 384 Contract.Assert(byteBuffer != null, "expected byteBuffer to be non-null"); 385 386 checked { 387 388 if (position < 0 || numBytes < 0 || position + numBytes > byteBuffer.Length) { 389 throw new ArgumentOutOfRangeException("byteCount"); 390 } 391 392 if (index < 0 || charsRemaining < 0 || index + charsRemaining > buffer.Length) { 393 throw new ArgumentOutOfRangeException("charsRemaining"); 394 } 395 396 unsafe { 397 fixed (byte* pBytes = byteBuffer) { 398 fixed (char* pChars = buffer) { 399 charsRead = m_decoder.GetChars(pBytes + position, numBytes, pChars + index, charsRemaining, false); 400 } 401 } 402 } 403 } 404 405 charsRemaining -= charsRead; 406 index+=charsRead; 407 } 408 409 // this should never fail 410 Contract.Assert(charsRemaining >= 0, "We read too many characters."); 411 412 // we may have read fewer than the number of characters requested if end of stream reached 413 // or if the encoding makes the char count too big for the buffer (e.g. fallback sequence) 414 return (count - charsRemaining); 415 } 416 InternalReadOneChar()417 private int InternalReadOneChar() { 418 // I know having a separate InternalReadOneChar method seems a little 419 // redundant, but this makes a scenario like the security parser code 420 // 20% faster, in addition to the optimizations for UnicodeEncoding I 421 // put in InternalReadChars. 422 int charsRead = 0; 423 int numBytes = 0; 424 long posSav = 0; 425 426 if (m_stream.CanSeek) 427 posSav = m_stream.Position; 428 429 if (m_charBytes==null) { 430 m_charBytes = new byte[MaxCharBytesSize]; // 431 } 432 if (m_singleChar==null) { 433 m_singleChar = new char[1]; 434 } 435 436 while (charsRead == 0) { 437 // We really want to know what the minimum number of bytes per char 438 // is for our encoding. Otherwise for UnicodeEncoding we'd have to 439 // do ~1+log(n) reads to read n characters. 440 // Assume 1 byte can be 1 char unless m_2BytesPerChar is true. 441 numBytes = m_2BytesPerChar ? 2 : 1; 442 443 int r = m_stream.ReadByte(); 444 m_charBytes[0] = (byte) r; 445 if (r == -1) 446 numBytes = 0; 447 if (numBytes == 2) { 448 r = m_stream.ReadByte(); 449 m_charBytes[1] = (byte) r; 450 if (r == -1) 451 numBytes = 1; 452 } 453 454 if (numBytes==0) { 455 // Console.WriteLine("Found no bytes. We're outta here."); 456 return -1; 457 } 458 459 Contract.Assert(numBytes == 1 || numBytes == 2, "BinaryReader::InternalReadOneChar assumes it's reading one or 2 bytes only."); 460 461 try { 462 463 charsRead = m_decoder.GetChars(m_charBytes, 0, numBytes, m_singleChar, 0); 464 } 465 catch 466 { 467 // Handle surrogate char 468 469 if (m_stream.CanSeek) 470 m_stream.Seek((posSav - m_stream.Position), SeekOrigin.Current); 471 // else - we can't do much here 472 473 throw; 474 } 475 476 Contract.Assert(charsRead < 2, "InternalReadOneChar - assuming we only got 0 or 1 char, not 2!"); 477 // Console.WriteLine("That became: " + charsRead + " characters."); 478 } 479 if (charsRead == 0) 480 return -1; 481 return m_singleChar[0]; 482 } 483 484 [SecuritySafeCritical] ReadChars(int count)485 public virtual char[] ReadChars(int count) { 486 if (count<0) { 487 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); 488 } 489 Contract.Ensures(Contract.Result<char[]>() != null); 490 Contract.Ensures(Contract.Result<char[]>().Length <= count); 491 Contract.EndContractBlock(); 492 if (m_stream == null) { 493 __Error.FileNotOpen(); 494 } 495 496 if (count == 0) { 497 return EmptyArray<Char>.Value; 498 } 499 500 // SafeCritical: we own the chars buffer, and therefore can guarantee that the index and count are valid 501 char[] chars = new char[count]; 502 int n = InternalReadChars(chars, 0, count); 503 if (n!=count) { 504 char[] copy = new char[n]; 505 Buffer.InternalBlockCopy(chars, 0, copy, 0, 2*n); // sizeof(char) 506 chars = copy; 507 } 508 509 return chars; 510 } 511 Read(byte[] buffer, int index, int count)512 public virtual int Read(byte[] buffer, int index, int count) { 513 if (buffer==null) 514 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer")); 515 if (index < 0) 516 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); 517 if (count < 0) 518 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); 519 if (buffer.Length - index < count) 520 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen")); 521 Contract.Ensures(Contract.Result<int>() >= 0); 522 Contract.Ensures(Contract.Result<int>() <= count); 523 Contract.EndContractBlock(); 524 525 if (m_stream==null) __Error.FileNotOpen(); 526 return m_stream.Read(buffer, index, count); 527 } 528 ReadBytes(int count)529 public virtual byte[] ReadBytes(int count) { 530 if (count < 0) throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); 531 Contract.Ensures(Contract.Result<byte[]>() != null); 532 Contract.Ensures(Contract.Result<byte[]>().Length <= Contract.OldValue(count)); 533 Contract.EndContractBlock(); 534 if (m_stream==null) __Error.FileNotOpen(); 535 536 if (count == 0) { 537 return EmptyArray<Byte>.Value; 538 } 539 540 byte[] result = new byte[count]; 541 542 int numRead = 0; 543 do { 544 int n = m_stream.Read(result, numRead, count); 545 if (n == 0) 546 break; 547 numRead += n; 548 count -= n; 549 } while (count > 0); 550 551 if (numRead != result.Length) { 552 // Trim array. This should happen on EOF & possibly net streams. 553 byte[] copy = new byte[numRead]; 554 Buffer.InternalBlockCopy(result, 0, copy, 0, numRead); 555 result = copy; 556 } 557 558 return result; 559 } 560 FillBuffer(int numBytes)561 protected virtual void FillBuffer(int numBytes) { 562 if (m_buffer != null && (numBytes < 0 || numBytes > m_buffer.Length)) { 563 throw new ArgumentOutOfRangeException("numBytes", Environment.GetResourceString("ArgumentOutOfRange_BinaryReaderFillBuffer")); 564 } 565 int bytesRead=0; 566 int n = 0; 567 568 if (m_stream==null) __Error.FileNotOpen(); 569 570 // Need to find a good threshold for calling ReadByte() repeatedly 571 // vs. calling Read(byte[], int, int) for both buffered & unbuffered 572 // streams. 573 if (numBytes==1) { 574 n = m_stream.ReadByte(); 575 if (n==-1) 576 __Error.EndOfFile(); 577 m_buffer[0] = (byte)n; 578 return; 579 } 580 581 do { 582 n = m_stream.Read(m_buffer, bytesRead, numBytes-bytesRead); 583 if (n==0) { 584 __Error.EndOfFile(); 585 } 586 bytesRead+=n; 587 } while (bytesRead<numBytes); 588 } 589 Read7BitEncodedInt()590 internal protected int Read7BitEncodedInt() { 591 // Read out an Int32 7 bits at a time. The high bit 592 // of the byte when on means to continue reading more bytes. 593 int count = 0; 594 int shift = 0; 595 byte b; 596 do { 597 // Check for a corrupted stream. Read a max of 5 bytes. 598 // In a future version, add a DataFormatException. 599 if (shift == 5 * 7) // 5 bytes max per Int32, shift += 7 600 throw new FormatException(Environment.GetResourceString("Format_Bad7BitInt32")); 601 602 // ReadByte handles end of stream cases for us. 603 b = ReadByte(); 604 count |= (b & 0x7F) << shift; 605 shift += 7; 606 } while ((b & 0x80) != 0); 607 return count; 608 } 609 } 610 } 611