* BufferedStream.cs (SetLength): Add checks and throw the
[mono.git] / mcs / class / corlib / System.IO / BufferedStream.cs
1 //
2 // System.IO.BufferedStream
3 //
4 // Author:
5 //   Matt Kimball (matt@kimball.net)
6 //   Ville Palo <vi64pa@kolumbus.fi>
7 //
8 // Copyright (C) 2004 Novell (http://www.novell.com)
9 //
10
11 using System.Globalization;
12 using System.Runtime.InteropServices;
13
14 namespace System.IO {
15         public sealed class BufferedStream : Stream {
16                 Stream m_stream;
17                 byte[] m_buffer;
18                 int m_buffer_pos;
19                 int m_buffer_read_ahead;
20                 bool m_buffer_reading;
21                 private bool disposed = false;
22
23                 public BufferedStream (Stream stream) : this (stream, 4096) 
24                 {
25                 }
26
27                 public BufferedStream (Stream stream, int buffer_size) 
28                 {
29                         if (stream == null)
30                                 throw new ArgumentNullException ("stream");
31                         // LAMESPEC: documented as < 0
32                         if (buffer_size <= 0)
33                                 throw new ArgumentOutOfRangeException ("buffer_size", "<= 0");
34                         if (!stream.CanRead && !stream.CanWrite) {
35                                 throw new ObjectDisposedException (
36                                         Locale.GetText ("Cannot access a closed Stream."));
37                         }
38
39                         m_stream = stream;
40                         m_buffer = new byte [buffer_size];
41                 }
42
43                 public override bool CanRead {
44                         get {
45                                 return m_stream.CanRead;
46                         }
47                 }
48
49                 public override bool CanWrite {
50                         get {
51                                 return m_stream.CanWrite;
52                         }
53                 }
54
55                 public override bool CanSeek {
56                         get {
57                                 return m_stream.CanSeek;
58                         }
59                 }
60
61                 public override long Length {
62                         get {                           
63                                 Flush ();
64                                 return m_stream.Length;
65                         }
66                 }
67                 
68                 public override long Position {
69                         get {
70                                 CheckObjectDisposedException ();
71                                 return m_stream.Position - m_buffer_read_ahead + m_buffer_pos;
72                         }
73
74                         set {
75                                 if (value < Position && (Position - value <= m_buffer_pos) && m_buffer_reading) {
76                                         m_buffer_pos -= (int) (Position - value);
77                                 }
78                                 else if (value > Position && (value - Position < m_buffer_read_ahead - m_buffer_pos) && m_buffer_reading) {
79                                         m_buffer_pos += (int) (value - Position);
80                                 }
81                                 else {
82                                         Flush();
83                                         m_stream.Position = value;
84                                 }
85                         }
86                 }
87
88                 public override void Close ()
89                 {
90                         if (m_buffer != null)
91                                 Flush();
92
93                         m_stream.Close();
94                         m_buffer = null;
95                         disposed = true;
96                 }
97
98                 public override void Flush ()
99                 {
100                         CheckObjectDisposedException ();
101
102                         if (m_buffer_reading) {
103                                 if (CanSeek)
104                                         m_stream.Position = Position;
105                         } else if (m_buffer_pos > 0) {
106                                 m_stream.Write(m_buffer, 0, m_buffer_pos);
107                         }
108
109                         m_buffer_read_ahead = 0;
110                         m_buffer_pos = 0;
111                 }
112
113                 public override long Seek (long offset, SeekOrigin origin)
114                 {
115                         CheckObjectDisposedException ();
116                         if (!CanSeek) {
117                                 throw new NotSupportedException (
118                                         Locale.GetText ("Non seekable stream."));
119                         }
120                         Flush ();
121                         return m_stream.Seek (offset, origin);
122                 }
123
124                 public override void SetLength (long value)
125                 {
126                         CheckObjectDisposedException ();
127
128                         if (value < 0)
129                                 throw new ArgumentOutOfRangeException ("value must be positive");
130
131                         if (!m_stream.CanWrite && !m_stream.CanSeek)
132                                 throw new NotSupportedException ("the stream cannot seek nor write.");
133
134                         if ((m_stream == null) || (!m_stream.CanRead && !m_stream.CanWrite))
135                                 throw new IOException ("the stream is not open");
136                         
137                         m_stream.SetLength(value);
138                         if (Position > value)
139                                 Position = value;
140                 }
141
142                 public override int ReadByte ()
143                 {
144                         CheckObjectDisposedException ();
145                         
146                         byte[] b = new byte[1];
147
148                         if (Read(b, 0, 1) == 1) {
149                                 return b[0];
150                         } else {
151                                 return -1;
152                         }
153                 }
154
155                 public override void WriteByte (byte value) 
156                 {
157                         CheckObjectDisposedException ();
158                         byte[] b = new byte[1];
159
160                         b[0] = value;
161                         Write(b, 0, 1);
162                 }
163
164                 public override int Read ([In,Out] byte[] array, int offset, int count) 
165                 {
166                         if (array == null)
167                                 throw new ArgumentNullException ("array");
168                         CheckObjectDisposedException ();
169                         if (!m_stream.CanRead) {
170                                 throw new NotSupportedException (
171                                         Locale.GetText ("Cannot read from stream"));
172                         }
173                         if (offset < 0)
174                                 throw new ArgumentOutOfRangeException ("offset", "< 0");
175                         if (count < 0)
176                                 throw new ArgumentOutOfRangeException ("count", "< 0");
177                         // re-ordered to avoid possible integer overflow
178                         if (array.Length - offset < count)
179                                 throw new ArgumentException ("array.Length - offset < count");
180
181                         if (!m_buffer_reading) {
182                                 Flush();
183                                 m_buffer_reading = true;
184                         }
185
186                         if (count <= m_buffer_read_ahead - m_buffer_pos) {
187                                 Array.Copy(m_buffer, m_buffer_pos, array, offset, count);
188
189                                 m_buffer_pos += count;
190                                 if (m_buffer_pos == m_buffer_read_ahead) {
191                                         m_buffer_pos = 0;
192                                         m_buffer_read_ahead = 0;
193                                 }
194
195                                 return count;
196                         }
197
198                         int ret = m_buffer_read_ahead - m_buffer_pos;
199                         Array.Copy(m_buffer, m_buffer_pos, array, offset, ret);
200                         m_buffer_pos = 0;
201                         m_buffer_read_ahead = 0;
202                         offset += ret;
203                         count -= ret;
204
205                         if (count >= m_buffer.Length) {
206                                 ret += m_stream.Read(array, offset, count);
207                         } else {
208                                 m_buffer_read_ahead = m_stream.Read(m_buffer, 0, m_buffer.Length);
209                                 
210                                 if (count < m_buffer_read_ahead) {
211                                         Array.Copy(m_buffer, 0, array, offset, count);
212                                         m_buffer_pos = count;
213                                         ret += count;
214                                 } else {
215                                         Array.Copy(m_buffer, 0, array, offset, m_buffer_read_ahead);
216                                         ret += m_buffer_read_ahead;
217                                         m_buffer_read_ahead = 0;
218                                 }
219                         }
220
221                         return ret;
222                 }
223
224                 public override void Write (byte[] array, int offset, int count)
225                 {
226                         if (array == null)
227                                 throw new ArgumentNullException ("array");
228                         CheckObjectDisposedException ();
229                         if (!m_stream.CanWrite) {
230                                 throw new NotSupportedException (
231                                         Locale.GetText ("Cannot write to stream"));
232                         }
233                         if (offset < 0)
234                                 throw new ArgumentOutOfRangeException ("offset", "< 0");
235                         if (count < 0)
236                                 throw new ArgumentOutOfRangeException ("count", "< 0");
237                         // avoid possible integer overflow
238                         if (array.Length - offset < count)
239                                 throw new ArgumentException ("array.Length - offset < count");
240
241                         if (m_buffer_reading) {
242                                 Flush();
243                                 m_buffer_reading = false;
244                         }
245
246                         // reordered to avoid possible integer overflow
247                         if (m_buffer_pos >= m_buffer.Length - count) {
248                                 Flush ();
249                                 m_stream.Write (array, offset, count);
250                         } 
251                         else {
252                                 Array.Copy (array, offset, m_buffer, m_buffer_pos, count);
253                                 m_buffer_pos += count;
254                         }
255                 }
256
257                 private void CheckObjectDisposedException () 
258                 {
259                         if (disposed) {
260                                 throw new ObjectDisposedException ("BufferedStream", 
261                                         Locale.GetText ("Stream is closed"));
262                         }
263                 }
264         }
265 }