Merge pull request #1225 from strawd/bug22307
[mono.git] / mcs / class / System.Web / System.Web / IntPtrStream.cs
1 //
2 // System.IO.IntPtrStream: A stream that is backed up by unmanaged memory
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //
7 // Based on the code for MemoryStream.cs:
8 //
9 // Authors:     Marcin Szczepanski (marcins@zipworld.com.au)
10 //              Gonzalo Paniagua Javier (gonzalo@ximian.com)
11 //
12 // (c) 2001,2002 Marcin Szczepanski
13 // (c) 2003 Ximian, Inc. (http://www.ximian.com)
14 //
15
16 //
17 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
18 //
19 // Permission is hereby granted, free of charge, to any person obtaining
20 // a copy of this software and associated documentation files (the
21 // "Software"), to deal in the Software without restriction, including
22 // without limitation the rights to use, copy, modify, merge, publish,
23 // distribute, sublicense, and/or sell copies of the Software, and to
24 // permit persons to whom the Software is furnished to do so, subject to
25 // the following conditions:
26 // 
27 // The above copyright notice and this permission notice shall be
28 // included in all copies or substantial portions of the Software.
29 // 
30 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
31 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
32 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
33 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
34 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
35 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
36 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37 //
38
39 using System.Runtime.InteropServices;
40 using System.IO;
41
42 namespace System.Web {
43
44         internal class IntPtrStream : Stream {
45                 unsafe byte *base_address;
46                 int size;
47                 int position;
48                 bool owns;
49
50                 public IntPtrStream (IntPtr base_address, int size)
51                 {
52                         unsafe {
53                                 this.base_address = (byte*)((void *)base_address);
54                         }
55                         this.size = size;
56                         owns = true;
57                 }
58
59                 public IntPtrStream (Stream stream)
60                 {
61                         IntPtrStream st = (IntPtrStream) stream;
62                         this.size = st.size;
63                         unsafe {
64                                 this.base_address = st.base_address;
65                         }
66                 }
67
68                 protected IntPtr BaseAddress {
69                         get {
70                                 unsafe  { return (IntPtr) base_address; }
71                         }
72                 }
73
74                 protected int Size {
75                         get { return size; }
76                 }
77
78                 public override bool CanRead {
79                         get {
80                                 return true;
81                         }
82                 }
83
84                 public override bool CanSeek {
85                         get {
86                                 return true;
87                         }
88                 }
89
90                 public override bool CanWrite {
91                         get {
92                                 return false;
93                         }
94                 }
95
96                 public override long Position {
97                         get {
98                                 return position;
99                         }
100
101                         set {
102                                 if (position < 0)
103                                         throw new ArgumentOutOfRangeException ("Position", "Can not be negative");
104                                 if (position > size)
105                                         throw new ArgumentOutOfRangeException ("Position", "Pointer falls out of range");
106
107                                 position = (int) value;
108                         }
109                 }
110
111                 public override long Length {
112                         get {
113                                 return size;
114                         }
115                 }
116
117                 public override int Read (byte [] buffer, int offset, int count)
118                 {
119                         if (buffer == null)
120                                 throw new ArgumentNullException ("buffer");
121
122                         if (offset < 0 || count < 0)
123                                 throw new ArgumentOutOfRangeException ("offset or count less than zero.");
124
125                         if (buffer.Length - offset < count )
126                                 throw new ArgumentException ("offset+count",
127                                                               "The size of the buffer is less than offset + count.");
128
129                         unsafe {
130                                 if (base_address == null)
131                                         throw new ObjectDisposedException ("Stream has been closed");
132                         }
133
134                         if (position >= size || count == 0)
135                                 return 0;
136
137                         if (position > size - count)
138                                 count = size - position;
139
140                         unsafe {
141                                 Marshal.Copy ((IntPtr) (base_address + position), buffer, offset, count);
142                         }
143                         position += count;
144                         return count;
145                 }
146
147                 public override int ReadByte ()
148                 {
149                         if (position >= size)
150                                 return -1;
151
152                         unsafe {
153                                 if (base_address == null)
154                                         throw new ObjectDisposedException ("Stream has been closed");
155                         }
156
157                         unsafe {
158                                 return base_address [position++];
159                         }
160                 }
161
162                 public override long Seek (long offset, SeekOrigin loc)
163                 {
164                         // It's funny that they don't throw this exception for < Int32.MinValue
165                         if (offset > (long) Int32.MaxValue)
166                                 throw new ArgumentOutOfRangeException ("Offset out of range. " + offset);
167
168                         unsafe {
169                                 if (base_address == null)
170                                         throw new ObjectDisposedException ("Stream has been closed");
171                         }
172
173                         int ref_point;
174                         switch (loc) {
175                         case SeekOrigin.Begin:
176                                 if (offset < 0)
177                                         throw new IOException ("Attempted to seek before start of MemoryStream.");
178                                 ref_point = 0;
179                                 break;
180                         case SeekOrigin.Current:
181                                 ref_point = position;
182                                 break;
183                         case SeekOrigin.End:
184                                 ref_point = size;
185                                 break;
186                         default:
187                                 throw new ArgumentException ("loc", "Invalid SeekOrigin");
188                         }
189
190                         checked {
191                                 try {
192                                         ref_point += (int) offset;
193                                 } catch {
194                                         throw new ArgumentOutOfRangeException ("Too large seek destination");
195                                 }
196                                 
197                                 if (ref_point < 0)
198                                         throw new IOException ("Attempted to seek before start of MemoryStream.");
199                         }
200
201                         position = ref_point;
202                         return position;
203                 }
204                 
205                 public override void SetLength (long value)
206                 {
207                         throw new NotSupportedException ("This stream can not change its size");
208                 }
209
210                 public override void Write (byte [] buffer, int offset, int count)
211                 {
212                         throw new NotSupportedException ("This stream can not change its size");
213                 }
214
215                 public override void WriteByte (byte value)
216                 {
217                         throw new NotSupportedException ("This stream can not change its size");
218                 }
219                 
220                 public override void Flush ()
221                 {
222                 }
223
224                 public override void Close ()
225                 {
226                         if (owns) {
227                                 unsafe {
228                                         IntPtr ptr = (IntPtr) base_address;
229                                         if (ptr != IntPtr.Zero)
230                                                 Marshal.FreeHGlobal (ptr);
231                                         base_address = null;
232                                 }
233                         }
234                 }
235
236                 ~IntPtrStream ()
237                 {
238                         if (owns) {
239                                 unsafe {
240                                         IntPtr ptr = (IntPtr) base_address;
241                                         if (ptr != IntPtr.Zero)
242                                                 Marshal.FreeHGlobal (ptr);
243                                         base_address = null;
244                                 }
245                         }
246                 }
247         }
248 }
249