svn path=/trunk/mcs/; revision=145671
[mono.git] / mcs / class / corlib / System.IO / UnmanagedMemoryStream.cs
1 //
2 // System.IO.UnmanagedMemoryStream.cs
3 //
4 // Copyright (C) 2006 Sridhar Kulkarni, All Rights Reserved
5 //
6 // Authors:
7 //      Sridhar Kulkarni (sridharkulkarni@gmail.com)
8 //      Gert Driesen (drieseng@users.sourceforge.net)
9 //      Sebastien Pouliot  <sebastien@ximian.com>
10 //
11 // Copyright (C) 2005-2006, 2009 Novell, Inc (http://www.novell.com)
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 //
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 //
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System;
34 using System.IO;
35 using System.Runtime.InteropServices;
36
37 namespace System.IO
38 {
39         public class UnmanagedMemoryStream : Stream
40         {
41                 long length;
42                 bool closed;
43                 long capacity;
44                 FileAccess fileaccess;
45                 IntPtr initial_pointer;
46                 long initial_position;
47                 long current_position;
48                 
49                 internal event EventHandler Closed;
50                 
51 #region Constructor
52                 protected UnmanagedMemoryStream()
53                 {
54                         closed = true;
55                 }
56
57                 [CLSCompliantAttribute(false)]
58                 public unsafe UnmanagedMemoryStream (byte *pointer, long length)
59                 {
60                         Initialize (pointer, length, length, FileAccess.Read);
61                 }
62                 
63                 [CLSCompliantAttribute(false)]
64                 public unsafe UnmanagedMemoryStream (byte *pointer, long length, long capacity, FileAccess access)
65                 {
66                         Initialize (pointer, length, capacity, access);
67                 }
68                 
69 #endregion
70         
71 #region Properties
72                 public override bool CanRead {
73                         get {
74                                 return (!closed && (fileaccess != FileAccess.Write));
75                         }
76                 }
77
78                 public override bool CanSeek {
79                         get {
80                                 return !closed;
81                         }
82                 }
83                 
84                 public override bool CanWrite {
85                         get {
86                                 return (!closed && (fileaccess != FileAccess.Read));
87                         }
88                 }
89                 public long Capacity {
90                         get {
91                                 if (closed)
92                                         throw new ObjectDisposedException("The stream is closed");
93                                 else
94                                         return (capacity);
95                         }
96                 }
97                 public override long Length {
98                         get {
99                                 if (closed)
100                                         throw new ObjectDisposedException("The stream is closed");
101                                 else
102                                         return (length);
103                         }
104                 }
105
106                 [CLSCompliantAttribute(false)]
107                 public override long Position {
108                         get {
109                                 if (closed)
110                                         throw new ObjectDisposedException("The stream is closed");
111                                 return (current_position);
112                         }
113                         set {
114                                 if (closed)
115                                         throw new ObjectDisposedException("The stream is closed");
116                                 if (value < 0)
117                                         throw new ArgumentOutOfRangeException("value", "Non-negative number required.");
118                                 if (value > (long)Int32.MaxValue)
119                                         throw new ArgumentOutOfRangeException("value", "The position is larger than Int32.MaxValue.");
120                                 current_position = value;
121                         }
122                 }
123
124 #if NET_2_1
125                 [CLSCompliantAttribute(false)]
126 #endif
127                 public unsafe byte* PositionPointer {
128                         get {
129                                 if (closed)
130                                         throw new ObjectDisposedException("The stream is closed");
131                                 if (current_position >= length)
132                                         throw new IndexOutOfRangeException ("value");
133
134                                 return (byte *) initial_pointer + current_position;
135                         }
136                         set {
137                                 if (closed)
138                                         throw new ObjectDisposedException("The stream is closed");
139
140                                 if (value < (byte *)initial_pointer)
141                                         throw new IOException ("Address is below the inital address");
142
143                                 Position = value - (byte*) initial_pointer;
144                         }
145                 }
146 #endregion
147                 
148 #region Methods
149                 public override int Read ([InAttribute] [OutAttribute] byte[] buffer, int offset, int count)
150                 {
151                         if (closed)
152                                 throw new ObjectDisposedException("The stream is closed");
153                         
154                         if (buffer == null)
155                                 throw new ArgumentNullException("buffer");
156                         if (offset < 0)
157                                 throw new ArgumentOutOfRangeException("offset", "Non-negative number required.");
158                         if (count < 0)
159                                 throw new ArgumentOutOfRangeException("count", "Non-negative number required.");
160                         if ((buffer.Length - offset) < count)
161                                 throw new ArgumentException("The length of the buffer array minus the offset parameter is less than the count parameter");
162                         
163                         if (fileaccess == FileAccess.Write)
164                                 throw new NotSupportedException("Stream does not support reading");
165                         else {
166                                 if (current_position >= length)
167                                         return (0);
168                                 else {
169                                         int progress = current_position + count < length ? count : (int) (length - current_position);
170                                         for (int i = 0; i < progress; i++)
171                                                 buffer [offset + i] = Marshal.ReadByte (initial_pointer, (int) current_position++);
172                                         return progress;
173                                 }
174                         }
175                 }
176
177                 public override int ReadByte ()
178                 {
179                         if (closed)
180                                 throw new ObjectDisposedException("The stream is closed");
181                         
182                         if (fileaccess== FileAccess.Write)
183                                 throw new NotSupportedException("Stream does not support reading");
184                         else {
185                                 if (current_position >= length)
186                                         return (-1);
187                                 return (int) Marshal.ReadByte(initial_pointer, (int) current_position++);
188                         }
189                 }
190                 
191                 public override long Seek (long offset, SeekOrigin loc)
192                 {
193                         if (closed)
194                                 throw new ObjectDisposedException("The stream is closed");
195
196                         long refpoint;
197                         switch(loc) {
198                         case SeekOrigin.Begin:
199                                 if (offset < 0)
200                                         throw new IOException("An attempt was made to seek before the beginning of the stream");
201                                 refpoint = initial_position;
202                                 break;
203                         case SeekOrigin.Current:
204                                 refpoint = current_position;
205                                 break;
206                         case SeekOrigin.End:
207                                 refpoint = length;
208                                 break;
209                         default:
210                                 throw new ArgumentException("Invalid SeekOrigin option");
211                         }
212                         refpoint += offset;
213                         if (refpoint < initial_position)
214                                 throw new IOException("An attempt was made to seek before the beginning of the stream");
215                         current_position = refpoint;
216                         return(current_position);
217                 }
218                  
219                 public override void SetLength (long value)
220                 {
221                         if (closed)
222                                 throw new ObjectDisposedException("The stream is closed");
223                         if (value < 0)
224                                 throw new ArgumentOutOfRangeException("length", "Non-negative number required.");
225                         if (value > capacity)
226                                 throw new IOException ("Unable to expand length of this stream beyond its capacity.");
227                         if (fileaccess == FileAccess.Read)
228                                 throw new NotSupportedException ("Stream does not support writing.");
229                         length = value;
230                         if (length < current_position)
231                                 current_position = length;
232                 }
233
234                 public override void Flush ()
235                 {
236                         if (closed)
237                                 throw new ObjectDisposedException("The stream is closed");
238                         //This method performs no action for this class
239                         //but is included as part of the Stream base class
240                 }
241                  
242                 protected override void Dispose (bool disposing)
243                 {
244                         if (closed)
245                                 return;
246                         closed = true;
247                         if (Closed != null)
248                                 Closed (this, null);
249                 }
250                  
251                 public override void Write (byte[] buffer, int offset, int count)
252                 {
253                         if (closed)
254                                 throw new ObjectDisposedException("The stream is closed");
255                         if (buffer == null)
256                                 throw new ArgumentNullException("The buffer parameter is a null reference");
257                         if (offset < 0)
258                                 throw new ArgumentOutOfRangeException("offset", "Non-negative number required.");
259                         if (count < 0)
260                                 throw new ArgumentOutOfRangeException("count", "Non-negative number required.");
261                         if ((buffer.Length - offset) < count)
262                                 throw new ArgumentException("The length of the buffer array minus the offset parameter is less than the count parameter");
263                         if (current_position > capacity - count)
264                                 throw new NotSupportedException ("Unable to expand length of this stream beyond its capacity.");
265                         if (fileaccess == FileAccess.Read)
266                                 throw new NotSupportedException ("Stream does not support writing.");
267                         else {
268                                 unsafe {
269                                         // use Marshal.WriteByte since that allow us to start writing
270                                         // from the current position
271                                         for (int i = 0; i < count; i++)
272                                                 Marshal.WriteByte (initial_pointer, (int) current_position++, buffer [offset + i]);
273
274                                         if (current_position > length)
275                                                 length = current_position;
276                                 }
277                         }
278                 }
279                 
280                 public override void WriteByte (byte value)
281                 {
282                         if (closed)
283                                 throw new ObjectDisposedException("The stream is closed");
284                         
285                         if (current_position == capacity)
286                                 throw new NotSupportedException("The current position is at the end of the capacity of the stream");
287                         if (fileaccess == FileAccess.Read)
288                                 throw new NotSupportedException("Stream does not support writing.");
289                         else {
290                                 unsafe {
291                                         Marshal.WriteByte(initial_pointer, (int)current_position, value);
292                                         current_position++;
293                                         if (current_position > length)
294                                                 length = current_position;
295                                 }
296                         }
297                 }
298
299                 protected unsafe void Initialize (byte* pointer, long length,
300                                                   long capacity,
301                                                   FileAccess access)
302                 {
303                         if (pointer == null)
304                                 throw new ArgumentNullException("pointer");
305                         if (length < 0)
306                                 throw new ArgumentOutOfRangeException("length", "Non-negative number required.");
307                         if (capacity < 0)
308                                 throw new ArgumentOutOfRangeException("capacity", "Non-negative number required.");
309                         if (length > capacity)
310                                 throw new ArgumentOutOfRangeException("length", "The length cannot be greater than the capacity.");
311                         if ((access < FileAccess.Read) || (access > FileAccess.ReadWrite))
312                                 throw new ArgumentOutOfRangeException ("access", "Enum value was out of legal range.");
313                                 
314                         fileaccess = access;
315                         this.length = length;
316                         this.capacity = capacity;
317                         initial_position = 0;
318                         current_position = initial_position;
319                         initial_pointer = new IntPtr ((void*)pointer);
320                         closed = false;
321                 }
322 #endregion
323         }
324 }
325