Merge pull request #681 from tritao/dll-api
[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 #if NET_4_0
49                 SafeBuffer safebuffer;
50 #endif
51                 
52                 internal event EventHandler Closed;
53                 
54 #region Constructor
55                 protected UnmanagedMemoryStream()
56                 {
57                         closed = true;
58                 }
59
60                 [CLSCompliantAttribute(false)]
61                 public unsafe UnmanagedMemoryStream (byte *pointer, long length) :
62                         this (pointer, length, length, FileAccess.Read)
63                 {
64                 }
65                 
66                 [CLSCompliantAttribute(false)]
67                 public unsafe UnmanagedMemoryStream (byte *pointer, long length, long capacity, FileAccess access)
68                 {
69                         closed = true;
70                         Initialize (pointer, length, capacity, access);
71                 }
72
73 #if NET_4_0
74                 public UnmanagedMemoryStream (SafeBuffer buffer, long offset, long length) :
75                         this (buffer, offset, length, FileAccess.Read)
76                 {
77                 }
78
79                 public UnmanagedMemoryStream (SafeBuffer buffer, long offset, long length, FileAccess access)
80                 {
81                         closed = true;
82                         Initialize (buffer, offset, length, access);
83                 }
84 #endif
85 #endregion
86         
87 #region Properties
88                 public override bool CanRead {
89                         get {
90                                 return (!closed && (fileaccess != FileAccess.Write));
91                         }
92                 }
93
94                 public override bool CanSeek {
95                         get {
96                                 return !closed;
97                         }
98                 }
99                 
100                 public override bool CanWrite {
101                         get {
102                                 return (!closed && (fileaccess != FileAccess.Read));
103                         }
104                 }
105                 public long Capacity {
106                         get {
107                                 if (closed)
108                                         throw new ObjectDisposedException("The stream is closed");
109                                 else
110                                         return (capacity);
111                         }
112                 }
113                 public override long Length {
114                         get {
115                                 if (closed)
116                                         throw new ObjectDisposedException("The stream is closed");
117                                 else
118                                         return (length);
119                         }
120                 }
121
122                 public override long Position {
123                         get {
124                                 if (closed)
125                                         throw new ObjectDisposedException("The stream is closed");
126                                 return (current_position);
127                         }
128                         set {
129                                 if (closed)
130                                         throw new ObjectDisposedException("The stream is closed");
131                                 if (value < 0)
132                                         throw new ArgumentOutOfRangeException("value", "Non-negative number required.");
133                                 if (value > (long)Int32.MaxValue)
134                                         throw new ArgumentOutOfRangeException("value", "The position is larger than Int32.MaxValue.");
135                                 current_position = value;
136                         }
137                 }
138
139                 [CLSCompliantAttribute (false)]
140                 public unsafe byte* PositionPointer {
141                         get {
142 #if NET_4_0
143                                 if (safebuffer != null)
144                                         throw new NotSupportedException ("Not supported when using SafeBuffer");
145 #endif
146                                 if (closed)
147                                         throw new ObjectDisposedException("The stream is closed");
148                                 if (current_position >= length)
149                                         throw new IndexOutOfRangeException ("value");
150
151                                 return (byte *) initial_pointer + current_position;
152                         }
153                         set {
154 #if NET_4_0
155                                 if (safebuffer != null)
156                                         throw new NotSupportedException ("Not supported when using SafeBuffer");
157 #endif
158                                 if (closed)
159                                         throw new ObjectDisposedException("The stream is closed");
160
161                                 if (value < (byte *)initial_pointer)
162                                         throw new IOException ("Address is below the inital address");
163
164                                 Position = value - (byte*) initial_pointer;
165                         }
166                 }
167 #endregion
168                 
169 #region Methods
170                 public override int Read ([InAttribute] [OutAttribute] byte[] buffer, int offset, int count)
171                 {
172                         if (closed)
173                                 throw new ObjectDisposedException("The stream is closed");
174                         
175                         if (buffer == null)
176                                 throw new ArgumentNullException("buffer");
177                         if (offset < 0)
178                                 throw new ArgumentOutOfRangeException("offset", "Non-negative number required.");
179                         if (count < 0)
180                                 throw new ArgumentOutOfRangeException("count", "Non-negative number required.");
181                         if ((buffer.Length - offset) < count)
182                                 throw new ArgumentException("The length of the buffer array minus the offset parameter is less than the count parameter");
183                         
184                         if (fileaccess == FileAccess.Write)
185                                 throw new NotSupportedException("Stream does not support reading");
186
187                         if (current_position >= length)
188                                 return 0;
189
190                         int progress = current_position + count < length ? count : (int) (length - current_position);
191 #if NET_4_0
192                         if (safebuffer != null) {
193                                 unsafe {
194                                         byte *ptr = null;
195                                         try {
196                                                 safebuffer.AcquirePointer (ref ptr);
197                                                 Marshal.Copy (new IntPtr (ptr + current_position), buffer, offset, progress);
198                                         } finally {
199                                                 if (ptr != null)
200                                                         safebuffer.ReleasePointer ();
201                                         }
202                                 }
203                         } else
204 #endif
205                         {
206                                 Marshal.Copy (new IntPtr (initial_pointer.ToInt64 () + current_position), buffer, offset, progress);
207                         }
208                         current_position += progress;
209                         return progress;
210                 }
211
212                 public override int ReadByte ()
213                 {
214                         if (closed)
215                                 throw new ObjectDisposedException("The stream is closed");
216                         
217                         if (fileaccess== FileAccess.Write)
218                                 throw new NotSupportedException("Stream does not support reading");
219
220                         if (current_position >= length)
221                                 return (-1);
222
223 #if NET_4_0
224                         if (safebuffer != null) {
225                                 unsafe {
226                                         byte *ptr = null;
227                                         try {
228                                                 safebuffer.AcquirePointer (ref ptr);
229                                                 return (int) Marshal.ReadByte (new IntPtr (ptr), (int) current_position++);
230                                         } finally {
231                                                 if (ptr != null)
232                                                         safebuffer.ReleasePointer ();
233                                         }
234                                 }
235                         } else
236 #endif
237                         {
238                                 return (int) Marshal.ReadByte(initial_pointer, (int) current_position++);
239                         }
240                 }
241                 
242                 public override long Seek (long offset, SeekOrigin loc)
243                 {
244                         if (closed)
245                                 throw new ObjectDisposedException("The stream is closed");
246
247                         long refpoint;
248                         switch(loc) {
249                         case SeekOrigin.Begin:
250                                 if (offset < 0)
251                                         throw new IOException("An attempt was made to seek before the beginning of the stream");
252                                 refpoint = initial_position;
253                                 break;
254                         case SeekOrigin.Current:
255                                 refpoint = current_position;
256                                 break;
257                         case SeekOrigin.End:
258                                 refpoint = length;
259                                 break;
260                         default:
261                                 throw new ArgumentException("Invalid SeekOrigin option");
262                         }
263                         refpoint += offset;
264                         if (refpoint < initial_position)
265                                 throw new IOException("An attempt was made to seek before the beginning of the stream");
266                         current_position = refpoint;
267                         return(current_position);
268                 }
269                  
270                 public override void SetLength (long value)
271                 {
272 #if NET_4_0
273                         if (safebuffer != null)
274                                 throw new NotSupportedException ("Not supported when using SafeBuffer");
275 #endif
276                         if (closed)
277                                 throw new ObjectDisposedException("The stream is closed");
278                         if (value < 0)
279                                 throw new ArgumentOutOfRangeException("length", "Non-negative number required.");
280                         if (value > capacity)
281                                 throw new IOException ("Unable to expand length of this stream beyond its capacity.");
282                         if (fileaccess == FileAccess.Read)
283                                 throw new NotSupportedException ("Stream does not support writing.");
284                         length = value;
285                         if (length < current_position)
286                                 current_position = length;
287                 }
288
289                 public override void Flush ()
290                 {
291                         if (closed)
292                                 throw new ObjectDisposedException("The stream is closed");
293                         //This method performs no action for this class
294                         //but is included as part of the Stream base class
295                 }
296                  
297                 protected override void Dispose (bool disposing)
298                 {
299                         if (closed)
300                                 return;
301                         closed = true;
302                         if (Closed != null)
303                                 Closed (this, null);
304                 }
305                  
306                 public override void Write (byte[] buffer, int offset, int count)
307                 {
308                         if (closed)
309                                 throw new ObjectDisposedException("The stream is closed");
310                         if (buffer == null)
311                                 throw new ArgumentNullException("The buffer parameter is a null reference");
312                         if (offset < 0)
313                                 throw new ArgumentOutOfRangeException("offset", "Non-negative number required.");
314                         if (count < 0)
315                                 throw new ArgumentOutOfRangeException("count", "Non-negative number required.");
316                         if ((buffer.Length - offset) < count)
317                                 throw new ArgumentException("The length of the buffer array minus the offset parameter is less than the count parameter");
318                         if (current_position > capacity - count)
319                                 throw new NotSupportedException ("Unable to expand length of this stream beyond its capacity.");
320                         if (fileaccess == FileAccess.Read)
321                                 throw new NotSupportedException ("Stream does not support writing.");
322
323 #if NET_4_0
324                         if (safebuffer != null) {
325                                 unsafe {
326                                         byte *dest = null;
327                                         try {
328                                                 safebuffer.AcquirePointer (ref dest);
329                                                 fixed (byte *src = buffer) {
330                                                         dest += current_position;
331                                                         String.memcpy (dest, src + offset, count);
332                                                 }
333                                         } finally {
334                                                 if (dest != null)
335                                                         safebuffer.ReleasePointer ();
336                                         }
337                                 }
338                         } else
339 #endif
340                         {
341                                 unsafe {
342                                         fixed (byte *src = buffer) {
343                                                 byte *dest = (byte *) initial_pointer + current_position;
344                                                 String.memcpy (dest, src + offset, count);
345                                         }
346                                 }
347                         }
348                         current_position += count;
349                         if (current_position > length)
350                                 length = current_position;
351                 }
352                 
353                 public override void WriteByte (byte value)
354                 {
355                         if (closed)
356                                 throw new ObjectDisposedException("The stream is closed");
357                         
358                         if (current_position == capacity)
359                                 throw new NotSupportedException("The current position is at the end of the capacity of the stream");
360                         if (fileaccess == FileAccess.Read)
361                                 throw new NotSupportedException("Stream does not support writing.");
362  
363 #if NET_4_0
364                         if (safebuffer != null) {
365                                 unsafe {
366                                         byte *dest = null;
367                                         try {
368                                                 safebuffer.AcquirePointer (ref dest);
369                                                 dest += current_position++;
370                                                 *dest = value;
371                                         } finally {
372                                                 if (dest != null)
373                                                         safebuffer.ReleasePointer ();
374                                         }
375                                 }
376                         } else
377 #endif
378                         {
379                                 unsafe {
380                                         byte *dest = (byte *) initial_pointer + (int) current_position++;
381                                         *dest = value;
382                                 }
383                         }
384                         if (current_position > length)
385                                 length = current_position;
386                 }
387
388                 [CLSCompliant (false)]
389                 protected unsafe void Initialize (byte* pointer, long length,
390                                                   long capacity,
391                                                   FileAccess access)
392                 {
393                         if (pointer == null)
394                                 throw new ArgumentNullException("pointer");
395                         if (length < 0)
396                                 throw new ArgumentOutOfRangeException("length", "Non-negative number required.");
397                         if (capacity < 0)
398                                 throw new ArgumentOutOfRangeException("capacity", "Non-negative number required.");
399                         if (length > capacity)
400                                 throw new ArgumentOutOfRangeException("length", "The length cannot be greater than the capacity.");
401                         if ((access < FileAccess.Read) || (access > FileAccess.ReadWrite))
402                                 throw new ArgumentOutOfRangeException ("access", "Enum value was out of legal range.");
403                         if (!closed)
404                                 throw new InvalidOperationException ("Called Initialize twice");
405                                 
406                         fileaccess = access;
407                         this.length = length;
408                         this.capacity = capacity;
409                         initial_position = 0;
410                         current_position = initial_position;
411                         initial_pointer = new IntPtr ((void*)pointer);
412                         closed = false;
413                 }
414
415 #if NET_4_0
416                 protected void Initialize (SafeBuffer buffer, long offset, long length, FileAccess access)
417                 {
418                         if (buffer == null)
419                                 throw new ArgumentNullException ("buffer");
420
421                         if (offset < 0)
422                                 throw new ArgumentOutOfRangeException ("offset");
423
424                         if (length < 0)
425                                 throw new ArgumentOutOfRangeException ("length");
426
427                         ulong blength = buffer.ByteLength;
428                         if ((blength - (ulong) length) < (ulong) offset)
429                                 throw new ArgumentException ("Invalid offset and/or length");
430
431                         if (access < FileAccess.Read || access > FileAccess.ReadWrite)
432                                 throw new ArgumentOutOfRangeException ("access");
433
434                         if (!closed)
435                                 throw new InvalidOperationException ("Called Initialize twice");
436
437                         this.length = length;
438                         this.capacity = length;
439                         this.fileaccess = access;
440                         this.safebuffer = buffer;
441                         initial_position = offset;
442                         current_position = offset;
443                         closed = false;
444                 }
445 #endif
446 #endregion
447         }
448 }
449