2 // System.IO/FileStream.cs
\r
5 // Dietmar Maurer (dietmar@ximian.com)
\r
6 // Dan Lewis (dihlewis@yahoo.co.uk)
\r
8 // (C) 2001 Ximian, Inc. http://www.ximian.com
\r
12 using System.Runtime.CompilerServices;
\r
14 // FIXME: emit the correct exceptions everywhere. add error handling.
\r
19 public class FileStream : Stream
\r
21 // construct from handle
\r
23 public FileStream (IntPtr handle, FileAccess access)
\r
24 : this (handle, access, true, DefaultBufferSize, false) {}
\r
26 public FileStream (IntPtr handle, FileAccess access, bool ownsHandle)
\r
27 : this (handle, access, ownsHandle, DefaultBufferSize, false) {}
\r
29 public FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize)
\r
30 : this (handle, access, ownsHandle, bufferSize, false) {}
\r
32 public FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync)
36 this.owner = ownsHandle;
41 if(MonoIO.GetFileType (handle, out error) ==
48 InitBuffer (bufferSize);
50 /* Can't set append mode */
51 this.append_startpos=0;
54 // construct from filename
\r
56 public FileStream (string name, FileMode mode)
\r
57 : this (name, mode, FileAccess.ReadWrite, FileShare.ReadWrite, DefaultBufferSize, false) { }
\r
59 public FileStream (string name, FileMode mode, FileAccess access)
\r
60 : this (name, mode, access, FileShare.ReadWrite, DefaultBufferSize, false) { }
\r
62 public FileStream (string name, FileMode mode, FileAccess access, FileShare share)
\r
63 : this (name, mode, access, share, DefaultBufferSize, false) { }
\r
65 public FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize)
\r
66 : this (name, mode, access, share, bufferSize, false) { }
\r
68 public FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool isAsync)
71 throw new ArgumentNullException ("Name is null");
75 throw new ArgumentException ("Name is empty");
78 if (name.IndexOfAny (Path.InvalidPathChars) != -1) {
79 throw new ArgumentException ("Name has invalid chars");
82 if (Directory.Exists (name)) {
83 throw new UnauthorizedAccessException ("Access to the path '" + Path.GetFullPath (name) + "' is denied.");
86 /* Append streams can't be read (see FileMode
89 if (mode==FileMode.Append &&
90 (access&FileAccess.Read)==FileAccess.Read) {
91 throw new ArgumentException("Append streams can not be read");
96 // TODO: demand permissions
100 this.handle = MonoIO.Open (name, mode, access, share,
102 if (handle == MonoIO.InvalidHandle) {
103 throw MonoIO.GetException (name, error);
106 this.access = access;
108 this.async = isAsync;
110 /* Can we open non-files by name? */
112 if (MonoIO.GetFileType (handle, out error) ==
116 this.canseek = false;
119 InitBuffer (bufferSize);
121 if (mode==FileMode.Append) {
122 this.Seek (0, SeekOrigin.End);
123 this.append_startpos=this.Position;
125 this.append_startpos=0;
131 public override bool CanRead {
\r
133 return access == FileAccess.Read ||
\r
134 access == FileAccess.ReadWrite;
\r
138 public override bool CanWrite {
\r
140 return access == FileAccess.Write ||
\r
141 access == FileAccess.ReadWrite;
\r
145 public override bool CanSeek {
151 public virtual bool IsAsync {
157 public string Name {
\r
163 public override long Length {
167 return MonoIO.GetLength (handle, out error);
171 public override long Position {
173 if(CanSeek == false) {
174 throw new NotSupportedException("The stream does not support seeking");
178 return(buf_start + buf_offset);
182 if(CanSeek == false) {
183 throw new NotSupportedException("The stream does not support seeking");
187 throw new ArgumentOutOfRangeException("Attempt to set the position to a negative value");
190 Seek (value, SeekOrigin.Begin);
194 public virtual IntPtr Handle {
202 public override int ReadByte ()
204 if (handle == MonoIO.InvalidHandle)
205 throw new ObjectDisposedException ("Stream has been closed");
208 if (buf_offset >= buf_length) {
211 if (buf_length == 0) {
216 return(buf [buf_offset ++]);
220 public override void WriteByte (byte value)
223 if (buf_offset == buf_size) {
227 buf [buf_offset ++] = value;
228 if (buf_offset > buf_length) {
229 buf_length = buf_offset;
236 public override int Read (byte[] dest, int dest_offset, int count)
238 if (handle == MonoIO.InvalidHandle)
239 throw new ObjectDisposedException ("Stream has been closed");
241 throw new ArgumentNullException ("destination array is null");
243 throw new NotSupportedException ("Stream does not support reading");
244 int len = dest.Length;
245 if (dest_offset < 0 || count < 0)
246 throw new ArgumentException ("dest or count is negative");
247 if (dest_offset > len)
248 throw new ArgumentException ("destination offset is beyond array size");
249 if ((dest_offset + count) > len)
250 throw new ArgumentException ("Reading would overrun buffer");
256 int n = ReadSegment (dest, dest_offset, count);
261 /* If there was already enough
262 * buffered, no need to read
263 * more from the file.
268 if (count > buf_size) {
269 /* Read as much as we can, up
273 n = ReadData (handle, dest,
277 /* Make the next buffer read
278 * start from the right place
283 n = ReadSegment (dest,
294 public override void Write (byte[] src, int src_offset, int count)
299 int n = WriteSegment (src, src_offset + copied, count);
309 if (count > buf_size) {
310 // shortcut for long writes
313 MonoIO.Write (handle, src, src_offset + copied, count, out error);
320 public override long Seek (long offset, SeekOrigin origin)
324 if (handle == MonoIO.InvalidHandle)
325 throw new ObjectDisposedException ("Stream has been closed");
329 if(CanSeek == false) {
330 throw new NotSupportedException("The stream does not support seeking");
336 pos = Length + offset;
339 case SeekOrigin.Current:
340 pos = Position + offset;
343 case SeekOrigin.Begin: default:
349 /* LAMESPEC: shouldn't this be
350 * ArgumentOutOfRangeException?
352 throw new ArgumentException("Attempted to Seek before the beginning of the stream");
355 if(pos < this.append_startpos) {
356 /* More undocumented crap */
357 throw new IOException("Can't seek back over pre-existing data in append mode");
360 if (pos >= buf_start &&
361 pos <= buf_start + buf_length) {
362 buf_offset = (int) (pos - buf_start);
370 buf_start = MonoIO.Seek (handle, pos,
378 public override void SetLength (long length)
380 if(CanSeek == false) {
381 throw new NotSupportedException("The stream does not support seeking");
384 if(CanWrite == false) {
385 throw new NotSupportedException("The stream does not support writing");
389 throw new ArgumentOutOfRangeException("Length is less than 0");
396 MonoIO.SetLength (handle, length, out error);
399 public override void Flush ()
405 // The flushing is not actually required, in
406 //the mono runtime we were mapping flush to
407 //`fsync' which is not the same.
409 //MonoIO.Flush (handle);
412 public override void Close ()
\r
415 GC.SuppressFinalize (this); // remove from finalize queue
\r
425 protected virtual void Dispose (bool disposing) {
426 if (owner && handle != MonoIO.InvalidHandle) {
433 MonoIO.Close (handle, out error);
435 handle = MonoIO.InvalidHandle;
445 // ReadSegment, WriteSegment, FlushBuffer,
446 // RefillBuffer and ReadData should only be called
447 // when the Monitor lock is held, but these methods
448 // grab it again just to be safe.
450 private int ReadSegment (byte [] dest, int dest_offset,
453 if (count > buf_length - buf_offset) {
454 count = buf_length - buf_offset;
458 Buffer.BlockCopy (buf, buf_offset,
467 private int WriteSegment (byte [] src, int src_offset,
470 if (count > buf_size - buf_offset) {
471 count = buf_size - buf_offset;
475 Buffer.BlockCopy (src, src_offset,
479 if (buf_offset > buf_length) {
480 buf_length = buf_offset;
489 private void FlushBuffer ()
494 if (CanSeek == true) {
495 MonoIO.Seek (handle, buf_start,
499 MonoIO.Write (handle, buf, 0,
500 buf_length, out error);
503 buf_start += buf_length;
504 buf_offset = buf_length = 0;
508 private void RefillBuffer ()
512 buf_length = ReadData (handle, buf, 0,
516 private int ReadData (IntPtr handle, byte[] buf, int offset,
521 int amount = MonoIO.Read (handle, buf, offset,
524 /* Check for read error */
526 /* Kludge around broken pipes */
527 if(error == MonoIOError.ERROR_BROKEN_PIPE) {
530 throw new IOException ();
538 private void InitBuffer (int size)
\r
541 throw new ArgumentOutOfRangeException ("Buffer size cannot be negative.");
\r
545 buf = new byte [size];
\r
548 buf_offset = buf_length = 0;
\r
554 private static int DefaultBufferSize = 8192;
\r
556 private FileAccess access;
\r
557 private bool owner;
\r
558 private bool async;
\r
559 private bool canseek;
560 private long append_startpos;
563 private byte [] buf; // the buffer
\r
564 private int buf_size; // capacity in bytes
\r
565 private int buf_length; // number of valid bytes in buffer
\r
566 private int buf_offset; // position of next byte
\r
567 private bool buf_dirty; // true if buffer has been written to
\r
568 private long buf_start; // location of buffer in file
\r
569 private string name = "[Unknown]"; // name of file.
\r
571 IntPtr handle; // handle to underlying file
\r