Add license and copyright to all source files in corlib
[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         public sealed class BufferedStream : Stream {
39                 Stream m_stream;
40                 byte[] m_buffer;
41                 int m_buffer_pos;
42                 int m_buffer_read_ahead;
43                 bool m_buffer_reading;
44                 private bool disposed = false;
45
46                 public BufferedStream (Stream stream) : this (stream, 4096) 
47                 {
48                 }
49
50                 public BufferedStream (Stream stream, int buffer_size) 
51                 {
52                         if (stream == null)
53                                 throw new ArgumentNullException ("stream");
54                         // LAMESPEC: documented as < 0
55                         if (buffer_size <= 0)
56                                 throw new ArgumentOutOfRangeException ("buffer_size", "<= 0");
57                         if (!stream.CanRead && !stream.CanWrite) {
58                                 throw new ObjectDisposedException (
59                                         Locale.GetText ("Cannot access a closed Stream."));
60                         }
61
62                         m_stream = stream;
63                         m_buffer = new byte [buffer_size];
64                 }
65
66                 public override bool CanRead {
67                         get {
68                                 return m_stream.CanRead;
69                         }
70                 }
71
72                 public override bool CanWrite {
73                         get {
74                                 return m_stream.CanWrite;
75                         }
76                 }
77
78                 public override bool CanSeek {
79                         get {
80                                 return m_stream.CanSeek;
81                         }
82                 }
83
84                 public override long Length {
85                         get {                           
86                                 Flush ();
87                                 return m_stream.Length;
88                         }
89                 }
90                 
91                 public override long Position {
92                         get {
93                                 CheckObjectDisposedException ();
94                                 return m_stream.Position - m_buffer_read_ahead + m_buffer_pos;
95                         }
96
97                         set {
98                                 if (value < Position && (Position - value <= m_buffer_pos) && m_buffer_reading) {
99                                         m_buffer_pos -= (int) (Position - value);
100                                 }
101                                 else if (value > Position && (value - Position < m_buffer_read_ahead - m_buffer_pos) && m_buffer_reading) {
102                                         m_buffer_pos += (int) (value - Position);
103                                 }
104                                 else {
105                                         Flush();
106                                         m_stream.Position = value;
107                                 }
108                         }
109                 }
110
111                 public override void Close ()
112                 {
113                         if (m_buffer != null)
114                                 Flush();
115
116                         m_stream.Close();
117                         m_buffer = null;
118                         disposed = true;
119                 }
120
121                 public override void Flush ()
122                 {
123                         CheckObjectDisposedException ();
124
125                         if (m_buffer_reading) {
126                                 if (CanSeek)
127                                         m_stream.Position = Position;
128                         } else if (m_buffer_pos > 0) {
129                                 m_stream.Write(m_buffer, 0, m_buffer_pos);
130                         }
131
132                         m_buffer_read_ahead = 0;
133                         m_buffer_pos = 0;
134                 }
135
136                 public override long Seek (long offset, SeekOrigin origin)
137                 {
138                         CheckObjectDisposedException ();
139                         if (!CanSeek) {
140                                 throw new NotSupportedException (
141                                         Locale.GetText ("Non seekable stream."));
142                         }
143                         Flush ();
144                         return m_stream.Seek (offset, origin);
145                 }
146
147                 public override void SetLength (long value)
148                 {
149                         CheckObjectDisposedException ();
150
151                         if (value < 0)
152                                 throw new ArgumentOutOfRangeException ("value must be positive");
153
154                         if (!m_stream.CanWrite && !m_stream.CanSeek)
155                                 throw new NotSupportedException ("the stream cannot seek nor write.");
156
157                         if ((m_stream == null) || (!m_stream.CanRead && !m_stream.CanWrite))
158                                 throw new IOException ("the stream is not open");
159                         
160                         m_stream.SetLength(value);
161                         if (Position > value)
162                                 Position = value;
163                 }
164
165                 public override int ReadByte ()
166                 {
167                         CheckObjectDisposedException ();
168                         
169                         byte[] b = new byte[1];
170
171                         if (Read(b, 0, 1) == 1) {
172                                 return b[0];
173                         } else {
174                                 return -1;
175                         }
176                 }
177
178                 public override void WriteByte (byte value) 
179                 {
180                         CheckObjectDisposedException ();
181                         byte[] b = new byte[1];
182
183                         b[0] = value;
184                         Write(b, 0, 1);
185                 }
186
187                 public override int Read ([In,Out] byte[] array, int offset, int count) 
188                 {
189                         if (array == null)
190                                 throw new ArgumentNullException ("array");
191                         CheckObjectDisposedException ();
192                         if (!m_stream.CanRead) {
193                                 throw new NotSupportedException (
194                                         Locale.GetText ("Cannot read from stream"));
195                         }
196                         if (offset < 0)
197                                 throw new ArgumentOutOfRangeException ("offset", "< 0");
198                         if (count < 0)
199                                 throw new ArgumentOutOfRangeException ("count", "< 0");
200                         // re-ordered to avoid possible integer overflow
201                         if (array.Length - offset < count)
202                                 throw new ArgumentException ("array.Length - offset < count");
203
204                         if (!m_buffer_reading) {
205                                 Flush();
206                                 m_buffer_reading = true;
207                         }
208
209                         if (count <= m_buffer_read_ahead - m_buffer_pos) {
210                                 Array.Copy(m_buffer, m_buffer_pos, array, offset, count);
211
212                                 m_buffer_pos += count;
213                                 if (m_buffer_pos == m_buffer_read_ahead) {
214                                         m_buffer_pos = 0;
215                                         m_buffer_read_ahead = 0;
216                                 }
217
218                                 return count;
219                         }
220
221                         int ret = m_buffer_read_ahead - m_buffer_pos;
222                         Array.Copy(m_buffer, m_buffer_pos, array, offset, ret);
223                         m_buffer_pos = 0;
224                         m_buffer_read_ahead = 0;
225                         offset += ret;
226                         count -= ret;
227
228                         if (count >= m_buffer.Length) {
229                                 ret += m_stream.Read(array, offset, count);
230                         } else {
231                                 m_buffer_read_ahead = m_stream.Read(m_buffer, 0, m_buffer.Length);
232                                 
233                                 if (count < m_buffer_read_ahead) {
234                                         Array.Copy(m_buffer, 0, array, offset, count);
235                                         m_buffer_pos = count;
236                                         ret += count;
237                                 } else {
238                                         Array.Copy(m_buffer, 0, array, offset, m_buffer_read_ahead);
239                                         ret += m_buffer_read_ahead;
240                                         m_buffer_read_ahead = 0;
241                                 }
242                         }
243
244                         return ret;
245                 }
246
247                 public override void Write (byte[] array, int offset, int count)
248                 {
249                         if (array == null)
250                                 throw new ArgumentNullException ("array");
251                         CheckObjectDisposedException ();
252                         if (!m_stream.CanWrite) {
253                                 throw new NotSupportedException (
254                                         Locale.GetText ("Cannot write to stream"));
255                         }
256                         if (offset < 0)
257                                 throw new ArgumentOutOfRangeException ("offset", "< 0");
258                         if (count < 0)
259                                 throw new ArgumentOutOfRangeException ("count", "< 0");
260                         // avoid possible integer overflow
261                         if (array.Length - offset < count)
262                                 throw new ArgumentException ("array.Length - offset < count");
263
264                         if (m_buffer_reading) {
265                                 Flush();
266                                 m_buffer_reading = false;
267                         }
268
269                         // reordered to avoid possible integer overflow
270                         if (m_buffer_pos >= m_buffer.Length - count) {
271                                 Flush ();
272                                 m_stream.Write (array, offset, count);
273                         } 
274                         else {
275                                 Array.Copy (array, offset, m_buffer, m_buffer_pos, count);
276                                 m_buffer_pos += count;
277                         }
278                 }
279
280                 private void CheckObjectDisposedException () 
281                 {
282                         if (disposed) {
283                                 throw new ObjectDisposedException ("BufferedStream", 
284                                         Locale.GetText ("Stream is closed"));
285                         }
286                 }
287         }
288 }