3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 /*============================================================
10 ** <OWNER>gpaperin</OWNER>
13 ** Purpose: Wraps a stream and provides convenient read functionality
14 ** for strings and primitive types.
17 ============================================================*/
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
) {
53 throw new ArgumentNullException("input");
56 throw new ArgumentNullException("encoding");
59 throw new ArgumentException(Environment
.GetResourceString("Argument_StreamNotReadable"));
60 Contract
.EndContractBlock();
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)
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
{
87 public virtual void Close() {
91 protected virtual void Dispose(bool disposing
) {
93 Stream copyOfStream
= m_stream
;
95 if (copyOfStream
!= null && !m_leaveOpen
)
106 public void Dispose()
111 public virtual int PeekChar() {
112 Contract
.Ensures(Contract
.Result
<int>() >= -1);
114 if (m_stream
==null) __Error
.FileNotOpen();
116 if (!m_stream
.CanSeek
)
118 long origPos
= m_stream
.Position
;
120 m_stream
.Position
= origPos
;
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(){
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();
148 [CLSCompliant(false)]
149 public virtual sbyte ReadSByte() {
151 return (sbyte)(m_buffer
[0]);
154 public virtual char ReadChar() {
162 public virtual short ReadInt16() {
164 return (short)(m_buffer
[0] | m_buffer
[1] << 8);
167 [CLSCompliant(false)]
168 public virtual ushort ReadUInt16(){
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();
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() {
192 return (uint)(m_buffer
[0] | m_buffer
[1] << 8 | m_buffer
[2] << 16 | m_buffer
[3] << 24);
195 public virtual long ReadInt64() {
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() {
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() {
218 return Mono
.Security
.BitConverterLE
.ToSingle (m_buffer
, 0);
220 uint tmpBuffer
= (uint)(m_buffer
[0] | m_buffer
[1] << 8 | m_buffer
[2] << 16 | m_buffer
[3] << 24);
221 return *((float*)&tmpBuffer
);
225 [System
.Security
.SecuritySafeCritical
] // auto-generated
226 public virtual unsafe double ReadDouble() {
229 return Mono
.Security
.BitConverterLE
.ToDouble (m_buffer
, 0);
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
);
241 public virtual decimal ReadDecimal() {
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();
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) {
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
);
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
);
298 sb
= StringBuilderCache
.Acquire(stringLength
); // Actual string length in chars may be smaller.
299 sb
.Append(m_charBuffer
, 0, charsRead
);
302 } while (currPos
<stringLength
);
304 return StringBuilderCache
.GetStringAndRelease(sb
);
307 [SecuritySafeCritical
]
308 public virtual int Read(char[] buffer
, int index
, int count
) {
310 throw new ArgumentNullException("buffer", Environment
.GetResourceString("ArgumentNull_Buffer"));
313 throw new ArgumentOutOfRangeException("index", Environment
.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
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();
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
);
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);
339 int charsRemaining
= count
;
341 if (m_charBytes
==null) {
342 m_charBytes
= new byte[MaxCharBytesSize
];
345 while (charsRemaining
> 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) {
360 if (numBytes
> MaxCharBytesSize
)
361 numBytes
= MaxCharBytesSize
;
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();
376 numBytes
= m_stream
.Read(m_charBytes
, 0, numBytes
);
377 byteBuffer
= m_charBytes
;
381 return (count
- charsRemaining
);
384 Contract
.Assert(byteBuffer
!= null, "expected byteBuffer to be non-null");
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");
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
;
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.
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
;
448 r
= m_stream
.ReadByte();
449 m_charBytes
[1] = (byte) r
;
455 // Console.WriteLine("Found no bytes. We're outta here.");
459 Contract
.Assert(numBytes
== 1 || numBytes
== 2, "BinaryReader::InternalReadOneChar assumes it's reading one or 2 bytes only.");
463 charsRead
= m_decoder
.GetChars(m_charBytes
, 0, numBytes
, m_singleChar
, 0);
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
476 Contract
.Assert(charsRead
< 2, "InternalReadOneChar - assuming we only got 0 or 1 char, not 2!");
477 // Console.WriteLine("That became: " + charsRead + " characters.");
481 return m_singleChar
[0];
484 [SecuritySafeCritical
]
485 public virtual char[] ReadChars(int count
) {
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();
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
);
504 char[] copy
= new char[n
];
505 Buffer
.InternalBlockCopy(chars
, 0, copy
, 0, 2*n
); // sizeof(char)
512 public virtual int Read(byte[] buffer
, int index
, int count
) {
514 throw new ArgumentNullException("buffer", Environment
.GetResourceString("ArgumentNull_Buffer"));
516 throw new ArgumentOutOfRangeException("index", Environment
.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
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();
537 return EmptyArray
<Byte
>.Value
;
540 byte[] result
= new byte[count
];
544 int n
= m_stream
.Read(result
, numRead
, count
);
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
);
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"));
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
574 n
= m_stream
.ReadByte();
577 m_buffer
[0] = (byte)n
;
582 n
= m_stream
.Read(m_buffer
, bytesRead
, numBytes
-bytesRead
);
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.
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.
604 count
|= (b
& 0x7F) << shift
;
606 } while ((b
& 0x80) != 0);