Merge pull request #946 from akoeplinger/fix-mono-parallel
[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 //
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System.Globalization;
35 using System.Runtime.InteropServices;
36
37 namespace System.IO {
38         [ComVisible (true)]
39         public sealed class BufferedStream : Stream {
40                 Stream m_stream;
41                 byte[] m_buffer;
42                 int m_buffer_pos;
43                 int m_buffer_read_ahead;
44                 bool m_buffer_reading;
45                 private bool disposed = false;
46
47                 public BufferedStream (Stream stream) : this (stream, 4096) 
48                 {
49                 }
50
51                 public BufferedStream (Stream stream, int bufferSize) 
52                 {
53                         if (stream == null)
54                                 throw new ArgumentNullException ("stream");
55                         // LAMESPEC: documented as < 0
56                         if (bufferSize <= 0)
57                                 throw new ArgumentOutOfRangeException ("bufferSize", "<= 0");
58                         if (!stream.CanRead && !stream.CanWrite) {
59                                 throw new ObjectDisposedException (
60                                         Locale.GetText ("Cannot access a closed Stream."));
61                         }
62
63                         m_stream = stream;
64                         m_buffer = new byte [bufferSize];
65                 }
66
67                 public override bool CanRead {
68                         get {
69                                 return m_stream.CanRead;
70                         }
71                 }
72
73                 public override bool CanWrite {
74                         get {
75                                 return m_stream.CanWrite;
76                         }
77                 }
78
79                 public override bool CanSeek {
80                         get {
81                                 return m_stream.CanSeek;
82                         }
83                 }
84
85                 public override long Length {
86                         get {                           
87                                 Flush ();
88                                 return m_stream.Length;
89                         }
90                 }
91                 
92                 public override long Position {
93                         get {
94                                 CheckObjectDisposedException ();
95                                 return m_stream.Position - m_buffer_read_ahead + m_buffer_pos;
96                         }
97
98                         set {
99                                 if (value < Position && (Position - value <= m_buffer_pos) && m_buffer_reading) {
100                                         m_buffer_pos -= (int) (Position - value);
101                                 }
102                                 else if (value > Position && (value - Position < m_buffer_read_ahead - m_buffer_pos) && m_buffer_reading) {
103                                         m_buffer_pos += (int) (value - Position);
104                                 }
105                                 else {
106                                         Flush();
107                                         m_stream.Position = value;
108                                 }
109                         }
110                 }
111
112                 protected override void Dispose (bool disposing)
113                 {
114                         if (disposed)
115                                 return;
116                         if (m_buffer != null)
117                                 Flush();
118
119                         m_stream.Close();
120                         m_buffer = null;
121                         disposed = true;
122                 }
123
124                 public override void Flush ()
125                 {
126                         CheckObjectDisposedException ();
127
128                         if (m_buffer_reading) {
129                                 if (CanSeek)
130                                         m_stream.Position = Position;
131                         } else if (m_buffer_pos > 0) {
132                                 m_stream.Write(m_buffer, 0, m_buffer_pos);
133                         }
134
135                         m_buffer_read_ahead = 0;
136                         m_buffer_pos = 0;
137                 }
138
139                 public override long Seek (long offset, SeekOrigin origin)
140                 {
141                         CheckObjectDisposedException ();
142                         if (!CanSeek) {
143                                 throw new NotSupportedException (
144                                         Locale.GetText ("Non seekable stream."));
145                         }
146                         Flush ();
147                         return m_stream.Seek (offset, origin);
148                 }
149
150                 public override void SetLength (long value)
151                 {
152                         CheckObjectDisposedException ();
153
154                         if (value < 0)
155                                 throw new ArgumentOutOfRangeException ("value must be positive");
156
157                         if (!m_stream.CanWrite && !m_stream.CanSeek)
158                                 throw new NotSupportedException ("the stream cannot seek nor write.");
159
160                         if ((m_stream == null) || (!m_stream.CanRead && !m_stream.CanWrite))
161                                 throw new IOException ("the stream is not open");
162                         
163                         m_stream.SetLength(value);
164                         if (Position > value)
165                                 Position = value;
166                 }
167
168                 public override int ReadByte ()
169                 {
170                         CheckObjectDisposedException ();
171                         
172                         if (!m_stream.CanRead) {
173                                 throw new NotSupportedException (
174                                         Locale.GetText ("Cannot read from stream"));
175                         }
176
177                         if (!m_buffer_reading) {
178                                 Flush ();
179                                 m_buffer_reading = true;
180                         }
181
182                         if (1 <= m_buffer_read_ahead - m_buffer_pos) {
183                                 return m_buffer [m_buffer_pos++];
184                         }
185                         else
186                         {
187                                 if (m_buffer_pos >= m_buffer_read_ahead) {
188                                         m_buffer_pos = 0;
189                                         m_buffer_read_ahead = 0;
190                                 }
191
192                                 m_buffer_read_ahead = m_stream.Read (m_buffer, 0, m_buffer.Length);
193                                 if (1 <= m_buffer_read_ahead) {
194                                         return m_buffer [m_buffer_pos++];
195                                 } else {
196                                         return -1;
197                                 }
198                         }
199                 }
200
201                 public override void WriteByte (byte value) 
202                 {
203                         CheckObjectDisposedException ();
204                         if (!m_stream.CanWrite) {
205                                 throw new NotSupportedException (
206                                         Locale.GetText ("Cannot write to stream"));
207                         }
208
209                         if (m_buffer_reading) {
210                                 Flush ();
211                                 m_buffer_reading = false;
212                         } 
213                         else
214                         // reordered to avoid possible integer overflow
215                         if (m_buffer_pos >= m_buffer.Length - 1) {
216                                 Flush ();
217                         } 
218
219                         m_buffer [m_buffer_pos++] = value;
220                 }
221
222                 public override int Read ([In,Out] byte[] array, int offset, int count) 
223                 {
224                         if (array == null)
225                                 throw new ArgumentNullException ("array");
226                         CheckObjectDisposedException ();
227                         if (!m_stream.CanRead) {
228                                 throw new NotSupportedException (
229                                         Locale.GetText ("Cannot read from stream"));
230                         }
231                         if (offset < 0)
232                                 throw new ArgumentOutOfRangeException ("offset", "< 0");
233                         if (count < 0)
234                                 throw new ArgumentOutOfRangeException ("count", "< 0");
235                         // re-ordered to avoid possible integer overflow
236                         if (array.Length - offset < count)
237                                 throw new ArgumentException ("array.Length - offset < count");
238
239                         if (!m_buffer_reading) {
240                                 Flush();
241                                 m_buffer_reading = true;
242                         }
243
244                         if (count <= m_buffer_read_ahead - m_buffer_pos) {
245                                 Buffer.BlockCopyInternal (m_buffer, m_buffer_pos, array, offset, count);
246
247                                 m_buffer_pos += count;
248                                 if (m_buffer_pos == m_buffer_read_ahead) {
249                                         m_buffer_pos = 0;
250                                         m_buffer_read_ahead = 0;
251                                 }
252
253                                 return count;
254                         }
255
256                         int ret = m_buffer_read_ahead - m_buffer_pos;
257                         Buffer.BlockCopyInternal (m_buffer, m_buffer_pos, array, offset, ret);
258                         m_buffer_pos = 0;
259                         m_buffer_read_ahead = 0;
260                         offset += ret;
261                         count -= ret;
262
263                         if (count >= m_buffer.Length) {
264                                 ret += m_stream.Read (array, offset, count);
265                         } else {
266                                 m_buffer_read_ahead = m_stream.Read (m_buffer, 0, m_buffer.Length);
267                                 
268                                 if (count < m_buffer_read_ahead) {
269                                         Buffer.BlockCopyInternal (m_buffer, 0, array, offset, count);
270                                         m_buffer_pos = count;
271                                         ret += count;
272                                 } else {
273                                         Buffer.BlockCopyInternal (m_buffer, 0, array, offset, m_buffer_read_ahead);
274                                         ret += m_buffer_read_ahead;
275                                         m_buffer_read_ahead = 0;
276                                 }
277                         }
278
279                         return ret;
280                 }
281
282                 public override void Write (byte[] array, int offset, int count)
283                 {
284                         if (array == null)
285                                 throw new ArgumentNullException ("array");
286                         CheckObjectDisposedException ();
287                         if (!m_stream.CanWrite) {
288                                 throw new NotSupportedException (
289                                         Locale.GetText ("Cannot write to stream"));
290                         }
291                         if (offset < 0)
292                                 throw new ArgumentOutOfRangeException ("offset", "< 0");
293                         if (count < 0)
294                                 throw new ArgumentOutOfRangeException ("count", "< 0");
295                         // avoid possible integer overflow
296                         if (array.Length - offset < count)
297                                 throw new ArgumentException ("array.Length - offset < count");
298
299                         if (m_buffer_reading) {
300                                 Flush();
301                                 m_buffer_reading = false;
302                         }
303
304                         // reordered to avoid possible integer overflow
305                         if (m_buffer_pos >= m_buffer.Length - count) {
306                                 Flush ();
307                                 m_stream.Write (array, offset, count);
308                         } 
309                         else {
310                                 Buffer.BlockCopyInternal  (array, offset, m_buffer, m_buffer_pos, count);
311                                 m_buffer_pos += count;
312                         }
313                 }
314
315                 private void CheckObjectDisposedException () 
316                 {
317                         if (disposed) {
318                                 throw new ObjectDisposedException ("BufferedStream", 
319                                         Locale.GetText ("Stream is closed"));
320                         }
321                 }
322         }
323 }