2003-11-14 Ben Maurer <bmaurer@users.sourceforge.net>
[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                 byte[] m_peekBuffer;
21                 int m_peekIndex = 0;
22                 int m_peekLimit = 0;
23                 
24                 private bool m_disposed = false;
25
26                 public BinaryReader(Stream input) : this(input, Encoding.UTF8Unmarked) {
27                 }
28
29                 public BinaryReader(Stream input, Encoding encoding) {
30                         if (input == null || encoding == null) 
31                                 throw new ArgumentNullException(Locale.GetText ("Input or Encoding is a null reference."));
32                         if (!input.CanRead)
33                                 throw new ArgumentException(Locale.GetText ("The stream doesn't support reading."));
34
35                         m_stream = input;
36                         m_encoding = encoding;
37                         m_encoding_max_byte = m_encoding.GetMaxByteCount(1);
38                         m_buffer = new byte [32];
39                 }
40
41                 public virtual Stream BaseStream {
42                         get {
43                                 return m_stream;
44                         }
45                 }
46
47                 public virtual void Close() {
48                         Dispose (true);
49                         m_disposed = true;
50                 }
51                 
52                 protected virtual void Dispose (bool disposing)
53                 {
54                         if (disposing && m_stream != null)
55                                 m_stream.Close ();
56
57                         m_disposed = true;
58                         m_buffer = null;
59                         m_encoding = null;
60                         m_stream = null;
61                 }
62
63                 void IDisposable.Dispose() 
64                 {
65                         Dispose (true);
66                 }
67
68                 protected virtual void FillBuffer(int bytes) {
69                         if(m_stream==null) {
70                                 throw new IOException("Stream is invalid");
71                         }
72                         
73                         CheckBuffer(bytes);
74
75                         /* Cope with partial reads */
76                         int pos=0;
77
78                         while (m_peekIndex < m_peekLimit)
79                                 m_buffer[pos++] = m_peekBuffer[m_peekIndex++];
80
81                         while(pos<bytes) {
82                                 int n=m_stream.Read(m_buffer, pos, bytes-pos);
83                                 if(n==0) {
84                                         throw new EndOfStreamException();
85                                 }
86
87                                 pos+=n;
88                         }
89                 }
90
91                 public virtual int PeekChar() {
92                         if(m_stream==null) {
93                                 
94                                 if (m_disposed)
95                                         throw new ObjectDisposedException ("BinaryReader", "Cannot read from a closed BinaryReader.");
96
97                                 throw new IOException("Stream is invalid");
98                         }
99
100                         char[] result = new char[1];
101                         byte[] bytes;
102                         int bcount;
103
104                         int ccount = ReadCharBytes (result, 0, 1, out bytes, out bcount);
105
106                         if (m_peekBuffer == null || m_peekBuffer.Length < bcount)
107                                 m_peekBuffer = new byte[bcount];
108
109                         Array.Copy (bytes, m_peekBuffer, bcount);
110                         m_peekIndex = 0;
111                         m_peekLimit = bcount;
112
113                         if (ccount == 0) return -1;
114                         else return result[0];
115                 }
116
117                 public virtual int Read() {
118                         char[] decode = new char[1];
119
120                         int count=Read(decode, 0, 1);
121                         if(count==0) {
122                                 /* No chars available */
123                                 return(-1);
124                         }
125                         
126                         return decode[0];
127                 }
128
129                 public virtual int Read(byte[] buffer, int index, int count) {
130                         if(m_stream==null) {
131
132                                 if (m_disposed)
133                                         throw new ObjectDisposedException ("BinaryReader", "Cannot read from a closed BinaryReader.");
134
135                                 throw new IOException("Stream is invalid");
136                         }
137                         
138                         if (buffer == null) {
139                                 throw new ArgumentNullException("buffer is null");
140                         }
141                         if (buffer.Length - index < count) {
142                                 throw new ArgumentException("buffer is too small");
143                         }
144                         if (index < 0) {
145                                 throw new ArgumentOutOfRangeException("index is less than 0");
146                         }
147                         if (count < 0) {
148                                 throw new ArgumentOutOfRangeException("count is less than 0");
149                         }
150
151                         int fromPeek = 0;
152                         while (m_peekIndex < m_peekLimit) {
153                                 buffer[index++] = m_peekBuffer[m_peekIndex++];
154                                 count--; 
155                                 fromPeek++;
156                         }
157
158                         int bytes_read=m_stream.Read(buffer, index, count);
159
160                         return(bytes_read + fromPeek);
161                 }
162
163                 public virtual int Read(char[] buffer, int index, int count) {
164
165                         if(m_stream==null) {
166
167                                 if (m_disposed)
168                                         throw new ObjectDisposedException ("BinaryReader", "Cannot read from a closed BinaryReader.");
169
170                                 throw new IOException("Stream is invalid");
171                         }
172                         
173                         if (buffer == null) {
174                                 throw new ArgumentNullException("buffer is null");
175                         }
176                         if (buffer.Length - index < count) {
177                                 throw new ArgumentException("buffer is too small");
178                         }
179                         if (index < 0) {
180                                 throw new ArgumentOutOfRangeException("index is less than 0");
181                         }
182                         if (count < 0) {
183                                 throw new ArgumentOutOfRangeException("count is less than 0");
184                         }
185
186                         int bytes_read;
187                         byte[] bytes;
188                         return ReadCharBytes (buffer, index, count, out bytes, out bytes_read);
189                 }
190
191                 private int ReadCharBytes(char[] buffer, int index, int count, out byte[] bytes, out int bytes_read) \r
192                 {
193                         int chars_read=0;
194                         bytes_read=0;
195                         
196                         while(chars_read < count) {
197                                 CheckBuffer(bytes_read + 1);
198
199                                 int read_byte = (m_peekIndex < m_peekLimit) ? m_peekBuffer[m_peekIndex++] : m_stream.ReadByte();
200                                 if(read_byte==-1) {
201                                         /* EOF */
202                                         bytes = m_buffer;
203                                         return(chars_read);
204                                 }
205
206                                 m_buffer[bytes_read]=(byte)read_byte;
207                                 bytes_read++;
208
209                                 chars_read=m_encoding.GetChars(m_buffer, 0,
210                                                                                  bytes_read,
211                                                                                  buffer, index);
212                                 
213                         }
214
215                         bytes = m_buffer;
216                         return(chars_read);
217                 }
218
219                 protected int Read7BitEncodedInt() {
220                         int ret = 0;
221                         int shift = 0;
222                         byte b;
223
224                         do {
225                                 b = ReadByte();
226                                 
227                                 ret = ret | ((b & 0x7f) << shift);
228                                 shift += 7;
229                         } while ((b & 0x80) == 0x80);
230
231                         return ret;
232                 }
233
234                 public virtual bool ReadBoolean() {
235                         FillBuffer(1);
236
237                         // Return value:
238                         //  true if the byte is non-zero; otherwise false.
239                         return(m_buffer[0] != 0);
240                 }
241
242                 public virtual byte ReadByte() {
243                         FillBuffer(1);
244
245                         return(m_buffer[0]);
246                 }
247
248                 public virtual byte[] ReadBytes(int count) {
249                         if(m_stream==null) {
250
251                                 if (m_disposed)
252                                         throw new ObjectDisposedException ("BinaryReader", "Cannot read from a closed BinaryReader.");
253
254                                 throw new IOException("Stream is invalid");
255                         }
256                         
257                         if (count < 0) {
258                                 throw new ArgumentOutOfRangeException("count is less than 0");
259                         }
260
261                         /* Can't use FillBuffer() here, because it's OK to
262                          * return fewer bytes than were requested
263                          */
264
265                         byte[] buf = new byte[count];
266                         int pos=0;
267                         
268                         while (m_peekIndex < m_peekLimit)
269                                 buf[pos++] = m_peekBuffer[m_peekIndex++];
270 \r
271                         while(pos < count) \r
272                         {
273                                 int n=m_stream.Read(buf, pos, count-pos);
274                                 if(n==0) {
275                                         /* EOF */
276                                         break;
277                                 }
278
279                                 pos+=n;
280                         }
281                                 
282                         if (pos!=count) {
283                                 byte[] new_buffer=new byte[pos];
284                                 Array.Copy(buf, new_buffer, pos);
285                                 return(new_buffer);
286                         }
287                         
288                         return(buf);
289                 }
290
291                 public virtual char ReadChar() {
292                         int ch=Read();
293
294                         if(ch==-1) {
295                                 throw new EndOfStreamException();
296                         }
297
298                         return((char)ch);
299                 }
300
301                 public virtual char[] ReadChars(int count) {
302                         if (count < 0) {
303                                 throw new ArgumentOutOfRangeException("count is less than 0");
304                         }
305
306                         char[] full = new char[count];
307                         int chars = Read(full, 0, count);
308                         
309                         if (chars == 0) {
310                                 throw new EndOfStreamException();
311                         } else if (chars != full.Length) {
312                                 char[] ret = new char[chars];
313                                 Array.Copy(full, 0, ret, 0, chars);
314                                 return ret;
315                         } else {
316                                 return full;
317                         }
318                 }
319
320                 unsafe public virtual decimal ReadDecimal() {
321                         FillBuffer(16);
322
323                         decimal ret;
324                         byte* ret_ptr = (byte *)&ret;
325                         for (int i = 0; i < 16; i++) {
326                           
327                                 /*
328                                  * internal representation of decimal is 
329                                  * ss32, hi32, lo32, mi32, 
330                                  * but in stream it is 
331                                  * lo32, mi32, hi32, ss32
332                                  * So we have to rerange this int32 values
333                                  */                       
334                           
335                                 if (i < 4) {
336                                         // lo 8 - 12                      
337                                         ret_ptr [i + 8] = m_buffer [i];
338                                 } else if (i < 8) {
339                                         // mid 12 - 16
340                                         ret_ptr [i + 8] = m_buffer [i];
341                                 } else if (i < 12) {
342                                         // hi 4 - 8
343                                         ret_ptr [i - 4] = m_buffer [i];
344                                 } else if (i < 16) {
345                                         // ss 0 - 4
346                                         ret_ptr [i - 12] = m_buffer [i];
347                                 }                               
348                         }
349
350                         return ret;
351                 }
352
353                 public virtual double ReadDouble() {
354                         FillBuffer(8);
355
356                         return(BitConverter.ToDouble(m_buffer, 0));
357                 }
358
359                 public virtual short ReadInt16() {
360                         FillBuffer(2);
361
362                         return((short) (m_buffer[0] | (m_buffer[1] << 8)));
363                 }
364
365                 public virtual int ReadInt32() {
366                         FillBuffer(4);
367
368                         return(m_buffer[0] | (m_buffer[1] << 8) |
369                                (m_buffer[2] << 16) | (m_buffer[3] << 24));
370                 }
371
372                 public virtual long ReadInt64() {
373                         FillBuffer(8);
374
375                         uint ret_low  = (uint) (m_buffer[0]            |
376                                                (m_buffer[1] << 8)  |
377                                                (m_buffer[2] << 16) |
378                                                (m_buffer[3] << 24)
379                                                );
380                         uint ret_high = (uint) (m_buffer[4]        |
381                                                (m_buffer[5] << 8)  |
382                                                (m_buffer[6] << 16) |
383                                                (m_buffer[7] << 24)
384                                                );
385                         return (long) ((((ulong) ret_high) << 32) | ret_low);
386                 }
387
388                 [CLSCompliant(false)]
389                 public virtual sbyte ReadSByte() {
390                         FillBuffer(1);
391
392                         return((sbyte)m_buffer[0]);
393                 }
394
395                 public virtual string ReadString() {
396                         /* Inspection of BinaryWriter-written files
397                          * shows that the length is given in bytes,
398                          * not chars
399                          */
400                         int len = Read7BitEncodedInt();
401
402                         FillBuffer(len);
403                         
404                         char[] str = m_encoding.GetChars(m_buffer, 0, len);
405
406                         return(new String(str));
407                 }
408
409                 public virtual float ReadSingle() {
410                         FillBuffer(4);
411
412                         return(BitConverter.ToSingle(m_buffer, 0));
413                 }
414
415                 [CLSCompliant(false)]
416                 public virtual ushort ReadUInt16() {
417                         FillBuffer(2);
418
419                         return((ushort) (m_buffer[0] | (m_buffer[1] << 8)));
420                 }
421
422                 [CLSCompliant(false)]
423                 public virtual uint ReadUInt32() {
424                         FillBuffer(4);
425                                 
426
427                         return((uint) (m_buffer[0] |
428                                        (m_buffer[1] << 8) |
429                                        (m_buffer[2] << 16) |
430                                        (m_buffer[3] << 24)));
431                 }
432
433                 [CLSCompliant(false)]
434                 public virtual ulong ReadUInt64() {
435                         FillBuffer(8);
436
437                         uint ret_low  = (uint) (m_buffer[0]            |
438                                                (m_buffer[1] << 8)  |
439                                                (m_buffer[2] << 16) |
440                                                (m_buffer[3] << 24)
441                                                );
442                         uint ret_high = (uint) (m_buffer[4]        |
443                                                (m_buffer[5] << 8)  |
444                                                (m_buffer[6] << 16) |
445                                                (m_buffer[7] << 24)
446                                                );
447                         return (((ulong) ret_high) << 32) | ret_low;
448                 }
449
450                 /* Ensures that m_buffer is at least length bytes
451                  * long, growing it if necessary
452                  */
453                 private void CheckBuffer(int length)
454                 {
455                         if(m_buffer.Length <= length) {
456                                 byte[] new_buffer=new byte[length];
457                                 Array.Copy(m_buffer, new_buffer,
458                                            m_buffer.Length);
459                                 m_buffer=new_buffer;
460                         }
461                 }
462         }
463 }