| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717 |
- // Licensed to the .NET Foundation under one or more agreements.
- // The .NET Foundation licenses this file to you under the MIT license.
- // See the LICENSE file in the project root for more information.
- /*============================================================
- **
- **
- **
- **
- **
- ** Purpose: Wraps a stream and provides convenient read functionality
- ** for strings and primitive types.
- **
- **
- ============================================================*/
- using System.Diagnostics;
- using System.Runtime.InteropServices;
- using System.Text;
- namespace System.IO
- {
- public class BinaryReader : IDisposable
- {
- private const int MaxCharBytesSize = 128;
- private Stream _stream;
- private byte[] _buffer;
- private Decoder _decoder;
- private byte[] _charBytes;
- private char[] _singleChar;
- private char[] _charBuffer;
- private int _maxCharsSize; // From MaxCharBytesSize & Encoding
- // Performance optimization for Read() w/ Unicode. Speeds us up by ~40%
- private bool _2BytesPerChar;
- private bool _isMemoryStream; // "do we sit on MemoryStream?" for Read/ReadInt32 perf
- private bool _leaveOpen;
- public BinaryReader(Stream input) : this(input, Encoding.UTF8, false)
- {
- }
- public BinaryReader(Stream input, Encoding encoding) : this(input, encoding, false)
- {
- }
- public BinaryReader(Stream input, Encoding encoding, bool leaveOpen)
- {
- if (input == null)
- {
- throw new ArgumentNullException(nameof(input));
- }
- if (encoding == null)
- {
- throw new ArgumentNullException(nameof(encoding));
- }
- if (!input.CanRead)
- {
- throw new ArgumentException(SR.Argument_StreamNotReadable);
- }
- _stream = input;
- _decoder = encoding.GetDecoder();
- _maxCharsSize = encoding.GetMaxCharCount(MaxCharBytesSize);
- int minBufferSize = encoding.GetMaxByteCount(1); // max bytes per one char
- if (minBufferSize < 16)
- {
- minBufferSize = 16;
- }
- _buffer = new byte[minBufferSize];
- // _charBuffer and _charBytes will be left null.
- // For Encodings that always use 2 bytes per char (or more),
- // special case them here to make Read() & Peek() faster.
- _2BytesPerChar = encoding is UnicodeEncoding;
- // check if BinaryReader is based on MemoryStream, and keep this for it's life
- // we cannot use "as" operator, since derived classes are not allowed
- _isMemoryStream = (_stream.GetType() == typeof(MemoryStream));
- _leaveOpen = leaveOpen;
- Debug.Assert(_decoder != null, "[BinaryReader.ctor]_decoder!=null");
- }
- public virtual Stream BaseStream
- {
- get
- {
- return _stream;
- }
- }
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
- Stream copyOfStream = _stream;
- _stream = null;
- if (copyOfStream != null && !_leaveOpen)
- {
- copyOfStream.Close();
- }
- }
- _stream = null;
- _buffer = null;
- _decoder = null;
- _charBytes = null;
- _singleChar = null;
- _charBuffer = null;
- }
- public void Dispose()
- {
- Dispose(true);
- }
- /// <remarks>
- /// Override Dispose(bool) instead of Close(). This API exists for compatibility purposes.
- /// </remarks>
- public virtual void Close()
- {
- Dispose(true);
- }
- public virtual int PeekChar()
- {
- if (_stream == null)
- {
- throw Error.GetFileNotOpen();
- }
- if (!_stream.CanSeek)
- {
- return -1;
- }
- long origPos = _stream.Position;
- int ch = Read();
- _stream.Position = origPos;
- return ch;
- }
- public virtual int Read()
- {
- if (_stream == null)
- {
- throw Error.GetFileNotOpen();
- }
- int charsRead = 0;
- int numBytes = 0;
- long posSav = posSav = 0;
- if (_stream.CanSeek)
- {
- posSav = _stream.Position;
- }
- if (_charBytes == null)
- {
- _charBytes = new byte[MaxCharBytesSize]; //REVIEW: We need at most 2 bytes/char here?
- }
- if (_singleChar == null)
- {
- _singleChar = new char[1];
- }
- while (charsRead == 0)
- {
- // We really want to know what the minimum number of bytes per char
- // is for our encoding. Otherwise for UnicodeEncoding we'd have to
- // do ~1+log(n) reads to read n characters.
- // Assume 1 byte can be 1 char unless _2BytesPerChar is true.
- numBytes = _2BytesPerChar ? 2 : 1;
- int r = _stream.ReadByte();
- _charBytes[0] = (byte)r;
- if (r == -1)
- {
- numBytes = 0;
- }
- if (numBytes == 2)
- {
- r = _stream.ReadByte();
- _charBytes[1] = (byte)r;
- if (r == -1)
- {
- numBytes = 1;
- }
- }
- if (numBytes == 0)
- {
- return -1;
- }
- Debug.Assert(numBytes == 1 || numBytes == 2, "BinaryReader::ReadOneChar assumes it's reading one or 2 bytes only.");
- try
- {
- charsRead = _decoder.GetChars(_charBytes, 0, numBytes, _singleChar, 0);
- }
- catch
- {
- // Handle surrogate char
- if (_stream.CanSeek)
- {
- _stream.Seek((posSav - _stream.Position), SeekOrigin.Current);
- }
- // else - we can't do much here
- throw;
- }
- Debug.Assert(charsRead < 2, "BinaryReader::ReadOneChar - assuming we only got 0 or 1 char, not 2!");
- }
- Debug.Assert(charsRead > 0);
- return _singleChar[0];
- }
- public virtual bool ReadBoolean()
- {
- FillBuffer(1);
- return (_buffer[0] != 0);
- }
- public virtual byte ReadByte()
- {
- // Inlined to avoid some method call overhead with FillBuffer.
- if (_stream == null)
- {
- throw Error.GetFileNotOpen();
- }
- int b = _stream.ReadByte();
- if (b == -1)
- {
- throw Error.GetEndOfFile();
- }
- return (byte)b;
- }
- [CLSCompliant(false)]
- public virtual sbyte ReadSByte()
- {
- FillBuffer(1);
- return (sbyte)(_buffer[0]);
- }
- public virtual char ReadChar()
- {
- int value = Read();
- if (value == -1)
- {
- throw Error.GetEndOfFile();
- }
- return (char)value;
- }
- public virtual short ReadInt16()
- {
- FillBuffer(2);
- return (short)(_buffer[0] | _buffer[1] << 8);
- }
- [CLSCompliant(false)]
- public virtual ushort ReadUInt16()
- {
- FillBuffer(2);
- return (ushort)(_buffer[0] | _buffer[1] << 8);
- }
- public virtual int ReadInt32()
- {
- if (_isMemoryStream)
- {
- if (_stream == null)
- {
- throw Error.GetFileNotOpen();
- }
- // read directly from MemoryStream buffer
- MemoryStream mStream = _stream as MemoryStream;
- Debug.Assert(mStream != null, "_stream as MemoryStream != null");
- return mStream.InternalReadInt32();
- }
- else
- {
- FillBuffer(4);
- return (int)(_buffer[0] | _buffer[1] << 8 | _buffer[2] << 16 | _buffer[3] << 24);
- }
- }
- [CLSCompliant(false)]
- public virtual uint ReadUInt32()
- {
- FillBuffer(4);
- return (uint)(_buffer[0] | _buffer[1] << 8 | _buffer[2] << 16 | _buffer[3] << 24);
- }
- public virtual long ReadInt64()
- {
- FillBuffer(8);
- uint lo = (uint)(_buffer[0] | _buffer[1] << 8 |
- _buffer[2] << 16 | _buffer[3] << 24);
- uint hi = (uint)(_buffer[4] | _buffer[5] << 8 |
- _buffer[6] << 16 | _buffer[7] << 24);
- return (long)((ulong)hi) << 32 | lo;
- }
- [CLSCompliant(false)]
- public virtual ulong ReadUInt64()
- {
- FillBuffer(8);
- uint lo = (uint)(_buffer[0] | _buffer[1] << 8 |
- _buffer[2] << 16 | _buffer[3] << 24);
- uint hi = (uint)(_buffer[4] | _buffer[5] << 8 |
- _buffer[6] << 16 | _buffer[7] << 24);
- return ((ulong)hi) << 32 | lo;
- }
- public virtual unsafe float ReadSingle()
- {
- FillBuffer(4);
- uint tmpBuffer = (uint)(_buffer[0] | _buffer[1] << 8 | _buffer[2] << 16 | _buffer[3] << 24);
- return *((float*)&tmpBuffer);
- }
- public virtual unsafe double ReadDouble()
- {
- FillBuffer(8);
- uint lo = (uint)(_buffer[0] | _buffer[1] << 8 |
- _buffer[2] << 16 | _buffer[3] << 24);
- uint hi = (uint)(_buffer[4] | _buffer[5] << 8 |
- _buffer[6] << 16 | _buffer[7] << 24);
- ulong tmpBuffer = ((ulong)hi) << 32 | lo;
- return *((double*)&tmpBuffer);
- }
- public virtual decimal ReadDecimal()
- {
- FillBuffer(16);
- try
- {
- return decimal.ToDecimal(_buffer);
- }
- catch (ArgumentException e)
- {
- // ReadDecimal cannot leak out ArgumentException
- throw new IOException(SR.Arg_DecBitCtor, e);
- }
- }
- public virtual string ReadString()
- {
- if (_stream == null)
- {
- throw Error.GetFileNotOpen();
- }
- int currPos = 0;
- int n;
- int stringLength;
- int readLength;
- int charsRead;
- // Length of the string in bytes, not chars
- stringLength = Read7BitEncodedInt();
- if (stringLength < 0)
- {
- throw new IOException(SR.Format(SR.IO_InvalidStringLen_Len, stringLength));
- }
- if (stringLength == 0)
- {
- return string.Empty;
- }
- if (_charBytes == null)
- {
- _charBytes = new byte[MaxCharBytesSize];
- }
- if (_charBuffer == null)
- {
- _charBuffer = new char[_maxCharsSize];
- }
- StringBuilder sb = null;
- do
- {
- readLength = ((stringLength - currPos) > MaxCharBytesSize) ? MaxCharBytesSize : (stringLength - currPos);
- n = _stream.Read(_charBytes, 0, readLength);
- if (n == 0)
- {
- throw Error.GetEndOfFile();
- }
- charsRead = _decoder.GetChars(_charBytes, 0, n, _charBuffer, 0);
- if (currPos == 0 && n == stringLength)
- {
- return new string(_charBuffer, 0, charsRead);
- }
- if (sb == null)
- {
- sb = StringBuilderCache.Acquire(stringLength); // Actual string length in chars may be smaller.
- }
- sb.Append(_charBuffer, 0, charsRead);
- currPos += n;
- } while (currPos < stringLength);
- return StringBuilderCache.GetStringAndRelease(sb);
- }
- public virtual int Read(char[] buffer, int index, int count)
- {
- if (buffer == null)
- {
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- }
- if (index < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (count < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (buffer.Length - index < count)
- {
- throw new ArgumentException(SR.Argument_InvalidOffLen);
- }
- if (_stream == null)
- {
- throw Error.GetFileNotOpen();
- }
- // SafeCritical: index and count have already been verified to be a valid range for the buffer
- return InternalReadChars(new Span<char>(buffer, index, count));
- }
- public virtual int Read(Span<char> buffer)
- {
- if (_stream == null)
- {
- throw Error.GetFileNotOpen();
- }
- return InternalReadChars(buffer);
- }
- private int InternalReadChars(Span<char> buffer)
- {
- Debug.Assert(_stream != null);
- int numBytes = 0;
- int index = 0;
- int charsRemaining = buffer.Length;
- if (_charBytes == null)
- {
- _charBytes = new byte[MaxCharBytesSize];
- }
- while (charsRemaining > 0)
- {
- int charsRead = 0;
- // We really want to know what the minimum number of bytes per char
- // is for our encoding. Otherwise for UnicodeEncoding we'd have to
- // do ~1+log(n) reads to read n characters.
- numBytes = charsRemaining;
- if (_2BytesPerChar)
- {
- numBytes <<= 1;
- }
- if (numBytes > MaxCharBytesSize)
- {
- numBytes = MaxCharBytesSize;
- }
- int position = 0;
- byte[] byteBuffer = null;
- if (_isMemoryStream)
- {
- MemoryStream mStream = _stream as MemoryStream;
- Debug.Assert(mStream != null, "_stream as MemoryStream != null");
- position = mStream.InternalGetPosition();
- numBytes = mStream.InternalEmulateRead(numBytes);
- byteBuffer = mStream.InternalGetBuffer();
- }
- else
- {
- numBytes = _stream.Read(_charBytes, 0, numBytes);
- byteBuffer = _charBytes;
- }
- if (numBytes == 0)
- {
- return (buffer.Length - charsRemaining);
- }
- Debug.Assert(byteBuffer != null, "expected byteBuffer to be non-null");
- checked
- {
- if (position < 0 || numBytes < 0 || position > byteBuffer.Length - numBytes)
- {
- throw new ArgumentOutOfRangeException(nameof(numBytes));
- }
- if (index < 0 || charsRemaining < 0 || index > buffer.Length - charsRemaining)
- {
- throw new ArgumentOutOfRangeException(nameof(charsRemaining));
- }
- unsafe
- {
- fixed (byte* pBytes = byteBuffer)
- fixed (char* pChars = &MemoryMarshal.GetReference(buffer))
- {
- charsRead = _decoder.GetChars(pBytes + position, numBytes, pChars + index, charsRemaining, flush: false);
- }
- }
- }
- charsRemaining -= charsRead;
- index += charsRead;
- }
- // this should never fail
- Debug.Assert(charsRemaining >= 0, "We read too many characters.");
- // we may have read fewer than the number of characters requested if end of stream reached
- // or if the encoding makes the char count too big for the buffer (e.g. fallback sequence)
- return (buffer.Length - charsRemaining);
- }
- public virtual char[] ReadChars(int count)
- {
- if (count < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (_stream == null)
- {
- throw Error.GetFileNotOpen();
- }
- if (count == 0)
- {
- return Array.Empty<char>();
- }
- // SafeCritical: we own the chars buffer, and therefore can guarantee that the index and count are valid
- char[] chars = new char[count];
- int n = InternalReadChars(new Span<char>(chars));
- if (n != count)
- {
- char[] copy = new char[n];
- Buffer.BlockCopy(chars, 0, copy, 0, 2 * n); // sizeof(char)
- chars = copy;
- }
- return chars;
- }
- public virtual int Read(byte[] buffer, int index, int count)
- {
- if (buffer == null)
- {
- throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
- }
- if (index < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (count < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (buffer.Length - index < count)
- {
- throw new ArgumentException(SR.Argument_InvalidOffLen);
- }
- if (_stream == null)
- {
- throw Error.GetFileNotOpen();
- }
- return _stream.Read(buffer, index, count);
- }
- public virtual int Read(Span<byte> buffer)
- {
- if (_stream == null)
- {
- throw Error.GetFileNotOpen();
- }
- return _stream.Read(buffer);
- }
- public virtual byte[] ReadBytes(int count)
- {
- if (count < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
- }
- if (_stream == null)
- {
- throw new ObjectDisposedException(null, SR.ObjectDisposed_FileClosed);
- }
- if (count == 0)
- {
- return Array.Empty<byte>();
- }
- byte[] result = new byte[count];
- int numRead = 0;
- do
- {
- int n = _stream.Read(result, numRead, count);
- if (n == 0)
- {
- break;
- }
- numRead += n;
- count -= n;
- } while (count > 0);
- if (numRead != result.Length)
- {
- // Trim array. This should happen on EOF & possibly net streams.
- byte[] copy = new byte[numRead];
- Buffer.BlockCopy(result, 0, copy, 0, numRead);
- result = copy;
- }
- return result;
- }
- protected virtual void FillBuffer(int numBytes)
- {
- if (_buffer != null && (numBytes < 0 || numBytes > _buffer.Length))
- {
- throw new ArgumentOutOfRangeException(nameof(numBytes), SR.ArgumentOutOfRange_BinaryReaderFillBuffer);
- }
- int bytesRead = 0;
- int n = 0;
- if (_stream == null)
- {
- throw Error.GetFileNotOpen();
- }
- // Need to find a good threshold for calling ReadByte() repeatedly
- // vs. calling Read(byte[], int, int) for both buffered & unbuffered
- // streams.
- if (numBytes == 1)
- {
- n = _stream.ReadByte();
- if (n == -1)
- {
- throw Error.GetEndOfFile();
- }
- _buffer[0] = (byte)n;
- return;
- }
- do
- {
- n = _stream.Read(_buffer, bytesRead, numBytes - bytesRead);
- if (n == 0)
- {
- throw Error.GetEndOfFile();
- }
- bytesRead += n;
- } while (bytesRead < numBytes);
- }
- protected internal int Read7BitEncodedInt()
- {
- // Read out an Int32 7 bits at a time. The high bit
- // of the byte when on means to continue reading more bytes.
- int count = 0;
- int shift = 0;
- byte b;
- do
- {
- // Check for a corrupted stream. Read a max of 5 bytes.
- // In a future version, add a DataFormatException.
- if (shift == 5 * 7) // 5 bytes max per Int32, shift += 7
- {
- throw new FormatException(SR.Format_Bad7BitInt32);
- }
- // ReadByte handles end of stream cases for us.
- b = ReadByte();
- count |= (b & 0x7F) << shift;
- shift += 7;
- } while ((b & 0x80) != 0);
- return count;
- }
- }
- }
|