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