2002-08-27 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[mono.git] / mcs / class / corlib / System.IO / BinaryReader.cs
1 //
2 // System.IO.BinaryReader
3 //
4 // Author:
5 //   Matt Kimball (matt@kimball.net)
6 //   Dick Porter (dick@ximian.com)
7 //
8
9 using System;
10 using System.Text;
11 using System.Globalization;
12
13 namespace System.IO {
14         public class BinaryReader : IDisposable {
15                 Stream m_stream;
16                 Encoding m_encoding;
17                 int m_encoding_max_byte;
18
19                 byte[] m_buffer;
20
21                 public BinaryReader(Stream input) : this(input, Encoding.UTF8) {
22                 }
23
24                 public BinaryReader(Stream input, Encoding encoding) {
25                         if (input == null || encoding == null) 
26                                 throw new ArgumentNullException(Locale.GetText ("Input or Encoding is a null reference."));
27                         if (!input.CanRead)
28                                 throw new ArgumentException(Locale.GetText ("The stream doesn't support reading."));
29
30                         m_stream = input;
31                         m_encoding = encoding;
32                         m_encoding_max_byte = m_encoding.GetMaxByteCount(1);
33                         m_buffer = new byte [32];
34                 }
35
36                 public virtual Stream BaseStream {
37                         get {
38                                 return m_stream;
39                         }
40                 }
41
42                 public virtual void Close() {
43                         Dispose (true);
44                 }
45                 
46                 protected virtual void Dispose (bool disposing)
47                 {
48                         if (disposing && m_stream != null)
49                                 m_stream.Close ();
50
51                         m_buffer = null;
52                         m_encoding = null;
53                         m_stream = null;
54                 }
55
56                 void IDisposable.Dispose() 
57                 {
58                         Dispose (true);
59                 }
60
61                 protected virtual void FillBuffer(int bytes) {
62                         if(m_stream==null) {
63                                 throw new IOException("Stream is invalid");
64                         }
65                         
66                         CheckBuffer(bytes);
67
68                         /* Cope with partial reads */
69                         int pos=0;
70                         while(pos<bytes) {
71                                 int n=m_stream.Read(m_buffer, pos, bytes-pos);
72                                 if(n==0) {
73                                         throw new EndOfStreamException();
74                                 }
75
76                                 pos+=n;
77                         }
78                 }
79
80                 public virtual int PeekChar() {
81                         if(m_stream==null) {
82                                 throw new IOException("Stream is invalid");
83                         }
84
85                         if(!m_stream.CanSeek) {
86                                 return(-1);
87                         }
88
89                         long pos=m_stream.Position;
90                         int ch=Read();
91                         m_stream.Position=pos;
92
93                         return(ch);
94                 }
95
96                 public virtual int Read() {
97                         char[] decode = new char[1];
98
99                         int count=Read(decode, 0, 1);
100                         if(count==0) {
101                                 /* No chars available */
102                                 return(-1);
103                         }
104                         
105                         return decode[0];
106                 }
107
108                 public virtual int Read(byte[] buffer, int index, int count) {
109                         if(m_stream==null) {
110                                 throw new IOException("Stream is invalid");
111                         }
112                         
113                         if (buffer == null) {
114                                 throw new ArgumentNullException("buffer is null");
115                         }
116                         if (buffer.Length - index < count) {
117                                 throw new ArgumentException("buffer is too small");
118                         }
119                         if (index < 0) {
120                                 throw new ArgumentOutOfRangeException("index is less than 0");
121                         }
122                         if (count < 0) {
123                                 throw new ArgumentOutOfRangeException("count is less than 0");
124                         }
125
126                         int bytes_read=m_stream.Read(buffer, index, count);
127
128                         return(bytes_read);
129                 }
130
131                 public virtual int Read(char[] buffer, int index, int count) {
132                         if(m_stream==null) {
133                                 throw new IOException("Stream is invalid");
134                         }
135                         
136                         if (buffer == null) {
137                                 throw new ArgumentNullException("buffer is null");
138                         }
139                         if (buffer.Length - index < count) {
140                                 throw new ArgumentException("buffer is too small");
141                         }
142                         if (index < 0) {
143                                 throw new ArgumentOutOfRangeException("index is less than 0");
144                         }
145                         if (count < 0) {
146                                 throw new ArgumentOutOfRangeException("count is less than 0");
147                         }
148
149                         int chars_read=0;
150                         int bytes_read=0;
151                         
152                         while(chars_read < count) {
153                                 CheckBuffer(bytes_read);
154
155                                 int read_byte=m_stream.ReadByte();
156                                 if(read_byte==-1) {
157                                         /* EOF */
158                                         return(chars_read);
159                                 }
160
161                                 m_buffer[bytes_read]=(byte)read_byte;
162                                 bytes_read++;
163                                 
164                                 chars_read=m_encoding.GetChars(m_buffer, 0,
165                                                                bytes_read,
166                                                                buffer, index);
167                                 
168                         }
169
170                         return(chars_read);
171                 }
172
173                 protected int Read7BitEncodedInt() {
174                         int ret = 0;
175                         int shift = 0;
176                         byte b;
177
178                         do {
179                                 b = ReadByte();
180                                 
181                                 ret = ret | ((b & 0x7f) << shift);
182                                 shift += 7;
183                         } while ((b & 0x80) == 0x80);
184
185                         return ret;
186                 }
187
188                 public virtual bool ReadBoolean() {
189                         FillBuffer(1);
190
191                         // Return value:
192                         //  true if the byte is non-zero; otherwise false.
193                         return(m_buffer[0] != 0);
194                 }
195
196                 public virtual byte ReadByte() {
197                         FillBuffer(1);
198
199                         return(m_buffer[0]);
200                 }
201
202                 public virtual byte[] ReadBytes(int count) {
203                         if(m_stream==null) {
204                                 throw new IOException("Stream is invalid");
205                         }
206                         
207                         if (count < 0) {
208                                 throw new ArgumentOutOfRangeException("count is less than 0");
209                         }
210
211                         /* Can't use FillBuffer() here, because it's OK to
212                          * return fewer bytes than were requested
213                          */
214
215                         byte[] buf = new byte[count];
216                         int pos=0;
217                         
218                         while(pos < count) {
219                                 int n=m_stream.Read(buf, pos, count-pos);
220                                 if(n==0) {
221                                         /* EOF */
222                                         break;
223                                 }
224
225                                 pos+=n;
226                         }
227                                 
228                         if (pos!=count) {
229                                 byte[] new_buffer=new byte[pos];
230                                 Array.Copy(buf, new_buffer, pos);
231                                 return(new_buffer);
232                         }
233                         
234                         return(buf);
235                 }
236
237                 public virtual char ReadChar() {
238                         int ch=Read();
239
240                         if(ch==-1) {
241                                 throw new EndOfStreamException();
242                         }
243
244                         return((char)ch);
245                 }
246
247                 public virtual char[] ReadChars(int count) {
248                         if (count < 0) {
249                                 throw new ArgumentOutOfRangeException("count is less than 0");
250                         }
251
252                         char[] full = new char[count];
253                         int chars = Read(full, 0, count);
254                         
255                         if (chars == 0) {
256                                 throw new EndOfStreamException();
257                         } else if (chars != full.Length) {
258                                 char[] ret = new char[chars];
259                                 Array.Copy(full, 0, ret, 0, chars);
260                                 return ret;
261                         } else {
262                                 return full;
263                         }
264                 }
265
266                 unsafe public virtual decimal ReadDecimal() {
267                         FillBuffer(16);
268
269                         decimal ret;
270                         byte* ret_ptr = (byte *)&ret;
271                         for (int i = 0; i < 16; i++) {
272                                 ret_ptr[i] = m_buffer[i];
273                         }
274
275                         return ret;
276                 }
277
278                 public virtual double ReadDouble() {
279                         FillBuffer(8);
280
281                         return(BitConverter.ToDouble(m_buffer, 0));
282                 }
283
284                 public virtual short ReadInt16() {
285                         FillBuffer(2);
286
287                         return((short) (m_buffer[0] | (m_buffer[1] << 8)));
288                 }
289
290                 public virtual int ReadInt32() {
291                         FillBuffer(4);
292
293                         return(m_buffer[0] | (m_buffer[1] << 8) |
294                                (m_buffer[2] << 16) | (m_buffer[3] << 24));
295                 }
296
297                 public virtual long ReadInt64() {
298                         FillBuffer(8);
299
300                         uint ret_low  = (uint) (m_buffer[0]            |
301                                                (m_buffer[1] << 8)  |
302                                                (m_buffer[2] << 16) |
303                                                (m_buffer[3] << 24)
304                                                );
305                         uint ret_high = (uint) (m_buffer[4]        |
306                                                (m_buffer[5] << 8)  |
307                                                (m_buffer[6] << 16) |
308                                                (m_buffer[7] << 24)
309                                                );
310                         return (long) ((((ulong) ret_high) << 32) | ret_low);
311                 }
312
313                 [CLSCompliant(false)]
314                 public virtual sbyte ReadSByte() {
315                         FillBuffer(1);
316
317                         return((sbyte)m_buffer[0]);
318                 }
319
320                 public virtual string ReadString() {
321                         /* Inspection of BinaryWriter-written files
322                          * shows that the length is given in bytes,
323                          * not chars
324                          */
325                         int len = Read7BitEncodedInt();
326
327                         FillBuffer(len);
328                         
329                         char[] str = m_encoding.GetChars(m_buffer, 0, len);
330
331                         return(new String(str));
332                 }
333
334                 public virtual float ReadSingle() {
335                         FillBuffer(4);
336
337                         return(BitConverter.ToSingle(m_buffer, 0));
338                 }
339
340                 [CLSCompliant(false)]
341                 public virtual ushort ReadUInt16() {
342                         FillBuffer(2);
343
344                         return((ushort) (m_buffer[0] | (m_buffer[1] << 8)));
345                 }
346
347                 [CLSCompliant(false)]
348                 public virtual uint ReadUInt32() {
349                         FillBuffer(4);
350                                 
351
352                         return((uint) (m_buffer[0] |
353                                        (m_buffer[1] << 8) |
354                                        (m_buffer[2] << 16) |
355                                        (m_buffer[3] << 24)));
356                 }
357
358                 [CLSCompliant(false)]
359                 public virtual ulong ReadUInt64() {
360                         FillBuffer(8);
361
362                         uint ret_low  = (uint) (m_buffer[0]            |
363                                                (m_buffer[1] << 8)  |
364                                                (m_buffer[2] << 16) |
365                                                (m_buffer[3] << 24)
366                                                );
367                         uint ret_high = (uint) (m_buffer[4]        |
368                                                (m_buffer[5] << 8)  |
369                                                (m_buffer[6] << 16) |
370                                                (m_buffer[7] << 24)
371                                                );
372                         return (((ulong) ret_high) << 32) | ret_low;
373                 }
374
375                 /* Ensures that m_buffer is at least length bytes
376                  * long, growing it if necessary
377                  */
378                 private void CheckBuffer(int length)
379                 {
380                         if(m_buffer.Length <= length) {
381                                 byte[] new_buffer=new byte[length];
382                                 Array.Copy(m_buffer, new_buffer,
383                                            m_buffer.Length);
384                                 m_buffer=new_buffer;
385                         }
386                 }
387         }
388 }