// // System.IO.BinaryReader // // Author: // Matt Kimball (matt@kimball.net) // Dick Porter (dick@ximian.com) // // // Copyright (C) 2004 Novell, Inc (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Text; using System.Globalization; using Mono.Security; #if NET_2_0 using System.Runtime.InteropServices; #endif namespace System.IO { #if NET_2_0 [ComVisible (true)] #endif public class BinaryReader : IDisposable { Stream m_stream; Encoding m_encoding; byte[] m_buffer; Decoder decoder; char [] charBuffer; // // 128 chars should cover most strings in one grab. // const int MaxBufferSize = 128; private bool m_disposed = false; public BinaryReader(Stream input) : this(input, Encoding.UTF8UnmarkedUnsafe) { } public BinaryReader(Stream input, Encoding encoding) { if (input == null || encoding == null) throw new ArgumentNullException(Locale.GetText ("Input or Encoding is a null reference.")); if (!input.CanRead) throw new ArgumentException(Locale.GetText ("The stream doesn't support reading.")); m_stream = input; m_encoding = encoding; decoder = encoding.GetDecoder (); m_buffer = new byte [32]; } public virtual Stream BaseStream { get { return m_stream; } } public virtual void Close() { Dispose (true); m_disposed = true; } protected virtual void Dispose (bool disposing) { if (disposing && m_stream != null) m_stream.Close (); m_disposed = true; m_buffer = null; m_encoding = null; m_stream = null; charBuffer = null; } void IDisposable.Dispose() { Dispose (true); } protected virtual void FillBuffer (int numBytes) { if (m_disposed) throw new ObjectDisposedException ("BinaryReader", "Cannot read from a closed BinaryReader."); if (m_stream==null) throw new IOException("Stream is invalid"); CheckBuffer(numBytes); /* Cope with partial reads */ int pos=0; while(pos 0) break; } chars_read ++; } return chars_read; } protected int Read7BitEncodedInt() { int ret = 0; int shift = 0; byte b; do { b = ReadByte(); ret = ret | ((b & 0x7f) << shift); shift += 7; } while ((b & 0x80) == 0x80); return ret; } public virtual bool ReadBoolean() { // Return value: // true if the byte is non-zero; otherwise false. return ReadByte() != 0; } public virtual byte ReadByte() { if (m_stream == null) { if (m_disposed) throw new ObjectDisposedException ("BinaryReader", "Cannot read from a closed BinaryReader."); throw new IOException ("Stream is invalid"); } int val = m_stream.ReadByte (); if (val != -1) return (byte) val; throw new EndOfStreamException (); } public virtual byte[] ReadBytes(int count) { if(m_stream==null) { if (m_disposed) throw new ObjectDisposedException ("BinaryReader", "Cannot read from a closed BinaryReader."); throw new IOException("Stream is invalid"); } if (count < 0) { throw new ArgumentOutOfRangeException("count is less than 0"); } /* Can't use FillBuffer() here, because it's OK to * return fewer bytes than were requested */ byte[] buf = new byte[count]; int pos=0; while(pos < count) { int n=m_stream.Read(buf, pos, count-pos); if(n==0) { /* EOF */ break; } pos+=n; } if (pos!=count) { byte[] new_buffer=new byte[pos]; Buffer.BlockCopyInternal (buf, 0, new_buffer, 0, pos); return(new_buffer); } return(buf); } public virtual char ReadChar() { int ch=Read(); if(ch==-1) { throw new EndOfStreamException(); } return((char)ch); } public virtual char[] ReadChars(int count) { if (count < 0) { throw new ArgumentOutOfRangeException("count is less than 0"); } if (count == 0) return new char [0]; char[] full = new char[count]; int chars = Read(full, 0, count); if (chars == 0) { throw new EndOfStreamException(); } else if (chars != full.Length) { char[] ret = new char[chars]; Array.Copy(full, 0, ret, 0, chars); return ret; } else { return full; } } unsafe public virtual decimal ReadDecimal() { FillBuffer(16); decimal ret; byte* ret_ptr = (byte *)&ret; /* * internal representation of decimal is * ss32, hi32, lo32, mi32, * but in stream it is * lo32, mi32, hi32, ss32 * So we have to rerange this int32 values */ if (BitConverter.IsLittleEndian) { for (int i = 0; i < 16; i++) { if (i < 4) { // lo 8 - 12 ret_ptr [i + 8] = m_buffer [i]; } else if (i < 8) { // mid 12 - 16 ret_ptr [i + 8] = m_buffer [i]; } else if (i < 12) { // hi 4 - 8 ret_ptr [i - 4] = m_buffer [i]; } else if (i < 16) { // ss 0 - 4 ret_ptr [i - 12] = m_buffer [i]; } } } else { for (int i = 0; i < 16; i++) { if (i < 4) { // lo 8 - 12 ret_ptr [11 - i] = m_buffer [i]; } else if (i < 8) { // mid 12 - 16 ret_ptr [19 - i] = m_buffer [i]; } else if (i < 12) { // hi 4 - 8 ret_ptr [15 - i] = m_buffer [i]; } else if (i < 16) { // ss 0 - 4 ret_ptr [15 - i] = m_buffer [i]; } } } return ret; } public virtual double ReadDouble() { FillBuffer(8); return(BitConverterLE.ToDouble(m_buffer, 0)); } public virtual short ReadInt16() { FillBuffer(2); return((short) (m_buffer[0] | (m_buffer[1] << 8))); } public virtual int ReadInt32() { FillBuffer(4); return(m_buffer[0] | (m_buffer[1] << 8) | (m_buffer[2] << 16) | (m_buffer[3] << 24)); } public virtual long ReadInt64() { FillBuffer(8); uint ret_low = (uint) (m_buffer[0] | (m_buffer[1] << 8) | (m_buffer[2] << 16) | (m_buffer[3] << 24) ); uint ret_high = (uint) (m_buffer[4] | (m_buffer[5] << 8) | (m_buffer[6] << 16) | (m_buffer[7] << 24) ); return (long) ((((ulong) ret_high) << 32) | ret_low); } [CLSCompliant(false)] public virtual sbyte ReadSByte() { return (sbyte) ReadByte (); } public virtual string ReadString() { /* Inspection of BinaryWriter-written files * shows that the length is given in bytes, * not chars */ int len = Read7BitEncodedInt(); if (len < 0) throw new IOException ("Invalid binary file (string len < 0)"); if (len == 0) return String.Empty; if (charBuffer == null) charBuffer = new char [MaxBufferSize]; // // We read the string here in small chunks. Also, we // Attempt to optimize the common case of short strings. // StringBuilder sb = null; do { int readLen = (len > MaxBufferSize) ? MaxBufferSize : len; FillBuffer (readLen); int cch = decoder.GetChars (m_buffer, 0, readLen, charBuffer, 0); if (sb == null && readLen == len) // ok, we got out the easy way, dont bother with the sb return new String (charBuffer, 0, cch); if (sb == null) // Len is a fairly good estimate of the number of chars in a string // Most of the time 1 byte == 1 char sb = new StringBuilder (len); sb.Append (charBuffer, 0, cch); len -= readLen; } while (len > 0); return sb.ToString(); } public virtual float ReadSingle() { FillBuffer(4); return(BitConverterLE.ToSingle(m_buffer, 0)); } [CLSCompliant(false)] public virtual ushort ReadUInt16() { FillBuffer(2); return((ushort) (m_buffer[0] | (m_buffer[1] << 8))); } [CLSCompliant(false)] public virtual uint ReadUInt32() { FillBuffer(4); return((uint) (m_buffer[0] | (m_buffer[1] << 8) | (m_buffer[2] << 16) | (m_buffer[3] << 24))); } [CLSCompliant(false)] public virtual ulong ReadUInt64() { FillBuffer(8); uint ret_low = (uint) (m_buffer[0] | (m_buffer[1] << 8) | (m_buffer[2] << 16) | (m_buffer[3] << 24) ); uint ret_high = (uint) (m_buffer[4] | (m_buffer[5] << 8) | (m_buffer[6] << 16) | (m_buffer[7] << 24) ); return (((ulong) ret_high) << 32) | ret_low; } /* Ensures that m_buffer is at least length bytes * long, growing it if necessary */ private void CheckBuffer(int length) { if(m_buffer.Length <= length) { byte[] new_buffer=new byte[length]; Buffer.BlockCopyInternal (m_buffer, 0, new_buffer, 0, m_buffer.Length); m_buffer=new_buffer; } } } }