Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / mscorlib / system / io / binaryreader.cs
blobbccfd3b510df28bd6b1779fbad2d36c9357d98de
1 // ==++==
2 //
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 //
5 // ==--==
6 /*============================================================
7 **
8 ** Class: BinaryReader
9 **
10 ** <OWNER>gpaperin</OWNER>
13 ** Purpose: Wraps a stream and provides convenient read functionality
14 ** for strings and primitive types.
17 ============================================================*/
18 namespace System.IO {
20 using System;
21 using System.Runtime;
22 using System.Text;
23 using System.Globalization;
24 using System.Diagnostics.Contracts;
25 using System.Security;
27 [System.Runtime.InteropServices.ComVisible(true)]
28 public class BinaryReader : IDisposable
30 private const int MaxCharBytesSize = 128;
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
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;
45 public BinaryReader(Stream input) : this(input, new UTF8Encoding(), false) {
48 public BinaryReader(Stream input, Encoding encoding) : this(input, encoding, false) {
51 public BinaryReader(Stream input, Encoding encoding, bool leaveOpen) {
52 if (input==null) {
53 throw new ArgumentNullException("input");
55 if (encoding==null) {
56 throw new ArgumentNullException("encoding");
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.
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;
78 Contract.Assert(m_decoder!=null, "[BinaryReader.ctor]m_decoder!=null");
81 public virtual Stream BaseStream {
82 get {
83 return m_stream;
87 public virtual void Close() {
88 Dispose(true);
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();
98 m_stream = null;
99 m_buffer = null;
100 m_decoder = null;
101 m_charBytes = null;
102 m_singleChar = null;
103 m_charBuffer = null;
106 public void Dispose()
108 Dispose(true);
111 public virtual int PeekChar() {
112 Contract.Ensures(Contract.Result<int>() >= -1);
114 if (m_stream==null) __Error.FileNotOpen();
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;
124 public virtual int Read() {
125 Contract.Ensures(Contract.Result<int>() >= -1);
127 if (m_stream==null) {
128 __Error.FileNotOpen();
130 return InternalReadOneChar();
133 public virtual bool ReadBoolean(){
134 FillBuffer(1);
135 return (m_buffer[0]!=0);
138 public virtual byte ReadByte() {
139 // Inlined to avoid some method call overhead with FillBuffer.
140 if (m_stream==null) __Error.FileNotOpen();
142 int b = m_stream.ReadByte();
143 if (b == -1)
144 __Error.EndOfFile();
145 return (byte) b;
148 [CLSCompliant(false)]
149 public virtual sbyte ReadSByte() {
150 FillBuffer(1);
151 return (sbyte)(m_buffer[0]);
154 public virtual char ReadChar() {
155 int value = Read();
156 if (value==-1) {
157 __Error.EndOfFile();
159 return (char)value;
162 public virtual short ReadInt16() {
163 FillBuffer(2);
164 return (short)(m_buffer[0] | m_buffer[1] << 8);
167 [CLSCompliant(false)]
168 public virtual ushort ReadUInt16(){
169 FillBuffer(2);
170 return (ushort)(m_buffer[0] | m_buffer[1] << 8);
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");
180 return mStream.InternalReadInt32();
182 else
184 FillBuffer(4);
185 return (int)(m_buffer[0] | m_buffer[1] << 8 | m_buffer[2] << 16 | m_buffer[3] << 24);
189 [CLSCompliant(false)]
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);
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;
204 [CLSCompliant(false)]
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;
214 [System.Security.SecuritySafeCritical] // auto-generated
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
225 [System.Security.SecuritySafeCritical] // auto-generated
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);
236 ulong tmpBuffer = ((ulong)hi) << 32 | lo;
237 return *((double*)&tmpBuffer);
238 #endif
241 public virtual decimal ReadDecimal() {
242 FillBuffer(16);
243 try {
244 return Decimal.ToDecimal(m_buffer);
246 catch (ArgumentException e) {
247 // ReadDecimal cannot leak out ArgumentException
248 throw new IOException(Environment.GetResourceString("Arg_DecBitCtor"), e);
252 public virtual String ReadString() {
253 Contract.Ensures(Contract.Result<String>() != null);
255 if (m_stream == null)
256 __Error.FileNotOpen();
258 int currPos = 0;
259 int n;
260 int stringLength;
261 int readLength;
262 int charsRead;
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));
270 if (stringLength==0) {
271 return String.Empty;
274 if (m_charBytes==null) {
275 m_charBytes = new byte[MaxCharBytesSize];
278 if (m_charBuffer == null) {
279 m_charBuffer = new char[m_maxCharsSize];
282 StringBuilder sb = null;
285 readLength = ((stringLength - currPos)>MaxCharBytesSize)?MaxCharBytesSize:(stringLength - currPos);
287 n = m_stream.Read(m_charBytes, 0, readLength);
288 if (n==0) {
289 __Error.EndOfFile();
292 charsRead = m_decoder.GetChars(m_charBytes, 0, n, m_charBuffer, 0);
294 if (currPos == 0 && n == stringLength)
295 return new String(m_charBuffer, 0, charsRead);
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;
302 } while (currPos<stringLength);
304 return StringBuilderCache.GetStringAndRelease(sb);
307 [SecuritySafeCritical]
308 public virtual int Read(char[] buffer, int index, int count) {
309 if (buffer==null) {
310 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
312 if (index < 0) {
313 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
315 if (count < 0) {
316 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
318 if (buffer.Length - index < count) {
319 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
321 Contract.Ensures(Contract.Result<int>() >= 0);
322 Contract.Ensures(Contract.Result<int>() <= count);
323 Contract.EndContractBlock();
325 if (m_stream==null)
326 __Error.FileNotOpen();
328 // SafeCritical: index and count have already been verified to be a valid range for the buffer
329 return InternalReadChars(buffer, index, count);
332 [SecurityCritical]
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);
338 int numBytes = 0;
339 int charsRemaining = count;
341 if (m_charBytes==null) {
342 m_charBytes = new byte[MaxCharBytesSize];
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;
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;
358 if (m_2BytesPerChar)
359 numBytes <<= 1;
360 if (numBytes > MaxCharBytesSize)
361 numBytes = MaxCharBytesSize;
363 int position = 0;
364 byte[] byteBuffer = null;
365 if (m_isMemoryStream)
367 MemoryStream mStream = m_stream as MemoryStream;
368 Contract.Assert(mStream != null, "m_stream as MemoryStream != null");
370 position = mStream.InternalGetPosition();
371 numBytes = mStream.InternalEmulateRead(numBytes);
372 byteBuffer = mStream.InternalGetBuffer();
374 else
376 numBytes = m_stream.Read(m_charBytes, 0, numBytes);
377 byteBuffer = m_charBytes;
380 if (numBytes == 0) {
381 return (count - charsRemaining);
384 Contract.Assert(byteBuffer != null, "expected byteBuffer to be non-null");
386 checked {
388 if (position < 0 || numBytes < 0 || position + numBytes > byteBuffer.Length) {
389 throw new ArgumentOutOfRangeException("byteCount");
392 if (index < 0 || charsRemaining < 0 || index + charsRemaining > buffer.Length) {
393 throw new ArgumentOutOfRangeException("charsRemaining");
396 unsafe {
397 fixed (byte* pBytes = byteBuffer) {
398 fixed (char* pChars = buffer) {
399 charsRead = m_decoder.GetChars(pBytes + position, numBytes, pChars + index, charsRemaining, false);
405 charsRemaining -= charsRead;
406 index+=charsRead;
409 // this should never fail
410 Contract.Assert(charsRemaining >= 0, "We read too many characters.");
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);
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;
426 if (m_stream.CanSeek)
427 posSav = m_stream.Position;
429 if (m_charBytes==null) {
430 m_charBytes = new byte[MaxCharBytesSize]; //
432 if (m_singleChar==null) {
433 m_singleChar = new char[1];
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;
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;
454 if (numBytes==0) {
455 // Console.WriteLine("Found no bytes. We're outta here.");
456 return -1;
459 Contract.Assert(numBytes == 1 || numBytes == 2, "BinaryReader::InternalReadOneChar assumes it's reading one or 2 bytes only.");
461 try {
463 charsRead = m_decoder.GetChars(m_charBytes, 0, numBytes, m_singleChar, 0);
465 catch
467 // Handle surrogate char
469 if (m_stream.CanSeek)
470 m_stream.Seek((posSav - m_stream.Position), SeekOrigin.Current);
471 // else - we can't do much here
473 throw;
476 Contract.Assert(charsRead < 2, "InternalReadOneChar - assuming we only got 0 or 1 char, not 2!");
477 // Console.WriteLine("That became: " + charsRead + " characters.");
479 if (charsRead == 0)
480 return -1;
481 return m_singleChar[0];
484 [SecuritySafeCritical]
485 public virtual char[] ReadChars(int count) {
486 if (count<0) {
487 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
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();
496 if (count == 0) {
497 return EmptyArray<Char>.Value;
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;
509 return chars;
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();
525 if (m_stream==null) __Error.FileNotOpen();
526 return m_stream.Read(buffer, index, 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();
536 if (count == 0) {
537 return EmptyArray<Byte>.Value;
540 byte[] result = new byte[count];
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);
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;
558 return result;
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"));
565 int bytesRead=0;
566 int n = 0;
568 if (m_stream==null) __Error.FileNotOpen();
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;
581 do {
582 n = m_stream.Read(m_buffer, bytesRead, numBytes-bytesRead);
583 if (n==0) {
584 __Error.EndOfFile();
586 bytesRead+=n;
587 } while (bytesRead<numBytes);
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"));
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;