FileStream.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. //
  2. // System.IO/FileStream.cs
  3. //
  4. // Authors:
  5. // Dietmar Maurer ([email protected])
  6. // Dan Lewis ([email protected])
  7. //
  8. // (C) 2001 Ximian, Inc. http://www.ximian.com
  9. //
  10. using System;
  11. using System.Runtime.CompilerServices;
  12. // FIXME: emit the correct exceptions everywhere. add error handling.
  13. namespace System.IO
  14. {
  15. public class FileStream : Stream
  16. {
  17. // construct from handle
  18. public FileStream (IntPtr handle, FileAccess access)
  19. : this (handle, access, true, DefaultBufferSize, false) {}
  20. public FileStream (IntPtr handle, FileAccess access, bool ownsHandle)
  21. : this (handle, access, ownsHandle, DefaultBufferSize, false) {}
  22. public FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize)
  23. : this (handle, access, ownsHandle, bufferSize, false) {}
  24. public FileStream (IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync)
  25. {
  26. this.handle = handle;
  27. this.access = access;
  28. this.owner = ownsHandle;
  29. this.async = isAsync;
  30. MonoIOError error;
  31. if(MonoIO.GetFileType (handle, out error) ==
  32. MonoFileType.Disk) {
  33. this.canseek = true;
  34. } else {
  35. this.canseek = false;
  36. }
  37. InitBuffer (bufferSize);
  38. }
  39. // construct from filename
  40. public FileStream (string name, FileMode mode)
  41. : this (name, mode, FileAccess.ReadWrite, FileShare.ReadWrite, DefaultBufferSize, false) { }
  42. public FileStream (string name, FileMode mode, FileAccess access)
  43. : this (name, mode, access, FileShare.ReadWrite, DefaultBufferSize, false) { }
  44. public FileStream (string name, FileMode mode, FileAccess access, FileShare share)
  45. : this (name, mode, access, share, DefaultBufferSize, false) { }
  46. public FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize)
  47. : this (name, mode, access, share, bufferSize, false) { }
  48. public FileStream (string name, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool isAsync)
  49. {
  50. if (name == null) {
  51. throw new ArgumentNullException ("Name is null");
  52. }
  53. if (name == "") {
  54. throw new ArgumentException ("Name is empty");
  55. }
  56. if (name.IndexOfAny (Path.InvalidPathChars) != -1) {
  57. throw new ArgumentException ("Name has invalid chars");
  58. }
  59. if (Directory.Exists (name)) {
  60. throw new UnauthorizedAccessException ("Access to the path '" + Path.GetFullPath (name) + "' is denied.");
  61. }
  62. this.name = name;
  63. // TODO: demand permissions
  64. MonoIOError error;
  65. this.handle = MonoIO.Open (name, mode, access, share,
  66. out error);
  67. if (handle == MonoIO.InvalidHandle) {
  68. throw MonoIO.GetException (name, error);
  69. }
  70. this.access = access;
  71. this.owner = true;
  72. this.async = isAsync;
  73. /* Can we open non-files by name? */
  74. if (MonoIO.GetFileType (handle, out error) ==
  75. MonoFileType.Disk) {
  76. this.canseek = true;
  77. } else {
  78. this.canseek = false;
  79. }
  80. InitBuffer (bufferSize);
  81. }
  82. // properties
  83. public override bool CanRead {
  84. get {
  85. return access == FileAccess.Read ||
  86. access == FileAccess.ReadWrite;
  87. }
  88. }
  89. public override bool CanWrite {
  90. get {
  91. return access == FileAccess.Write ||
  92. access == FileAccess.ReadWrite;
  93. }
  94. }
  95. public override bool CanSeek {
  96. get {
  97. return(canseek);
  98. }
  99. }
  100. public string Name {
  101. get {
  102. return name;
  103. }
  104. }
  105. public override long Length {
  106. get {
  107. MonoIOError error;
  108. return MonoIO.GetLength (handle, out error);
  109. }
  110. }
  111. public override long Position {
  112. get {
  113. if(CanSeek == false) {
  114. throw new NotSupportedException("The stream does not support seeking");
  115. }
  116. return(buf_start + buf_offset);
  117. }
  118. set {
  119. if(CanSeek == false) {
  120. throw new NotSupportedException("The stream does not support seeking");
  121. }
  122. if(value < 0) {
  123. throw new ArgumentOutOfRangeException("Attempt to set the position to a negative value");
  124. }
  125. Seek (value, SeekOrigin.Begin);
  126. }
  127. }
  128. public virtual IntPtr Handle {
  129. get { return handle; }
  130. }
  131. // methods
  132. public override int ReadByte ()
  133. {
  134. if (buf_offset >= buf_length) {
  135. RefillBuffer ();
  136. if (buf_length == 0)
  137. return -1;
  138. }
  139. return buf [buf_offset ++];
  140. }
  141. public override void WriteByte (byte value)
  142. {
  143. if (buf_offset == buf_size)
  144. FlushBuffer ();
  145. buf [buf_offset ++] = value;
  146. if (buf_offset > buf_length)
  147. buf_length = buf_offset;
  148. buf_dirty = true;
  149. }
  150. public override int Read (byte[] dest, int dest_offset, int count)
  151. {
  152. int copied = 0;
  153. int n = ReadSegment (dest, dest_offset, count);
  154. copied += n;
  155. count -= n;
  156. if (count == 0) {
  157. /* If there was already enough
  158. * buffered, no need to read more from
  159. * the file.
  160. */
  161. return (copied);
  162. }
  163. if (count > buf_size) {
  164. /* Read as much as we can, up to count
  165. * bytes
  166. */
  167. FlushBuffer();
  168. n = ReadData (handle, dest,
  169. dest_offset+copied, count);
  170. /* Make the next buffer read start
  171. * from the right place
  172. */
  173. buf_start += n;
  174. } else {
  175. RefillBuffer ();
  176. n = ReadSegment (dest, dest_offset+copied,
  177. count);
  178. }
  179. copied += n;
  180. return(copied);
  181. }
  182. public override void Write (byte[] src, int src_offset, int count)
  183. {
  184. int copied = 0;
  185. while (count > 0) {
  186. int n = WriteSegment (src, src_offset + copied, count);
  187. copied += n;
  188. count -= n;
  189. if (count == 0)
  190. break;
  191. FlushBuffer ();
  192. if (count > buf_size) {
  193. // shortcut for long writes
  194. MonoIOError error;
  195. MonoIO.Write (handle, src, src_offset + copied, count, out error);
  196. buf_start += count;
  197. break;
  198. }
  199. }
  200. }
  201. public override long Seek (long offset, SeekOrigin origin)
  202. {
  203. long pos;
  204. // make absolute
  205. if(CanSeek == false) {
  206. throw new NotSupportedException("The stream does not support seeking");
  207. }
  208. switch (origin) {
  209. case SeekOrigin.End:
  210. pos = Length + offset;
  211. break;
  212. case SeekOrigin.Current:
  213. pos = Position + offset;
  214. break;
  215. case SeekOrigin.Begin: default:
  216. pos = offset;
  217. break;
  218. }
  219. if (pos < 0) {
  220. /* LAMESPEC: shouldn't this be
  221. * ArgumentOutOfRangeException?
  222. */
  223. throw new ArgumentException("Attempted to Seek before the beginning of the stream");
  224. }
  225. if (pos >= buf_start && pos <= buf_start + buf_length) {
  226. buf_offset = (int) (pos - buf_start);
  227. return pos;
  228. }
  229. FlushBuffer ();
  230. MonoIOError error;
  231. buf_start = MonoIO.Seek (handle, pos,
  232. SeekOrigin.Begin, out error);
  233. return buf_start;
  234. }
  235. public override void SetLength (long length)
  236. {
  237. if(CanSeek == false) {
  238. throw new NotSupportedException("The stream does not support seeking");
  239. }
  240. if(CanWrite == false) {
  241. throw new NotSupportedException("The stream does not support writing");
  242. }
  243. if(length < 0) {
  244. throw new ArgumentOutOfRangeException("Length is less than 0");
  245. }
  246. Flush ();
  247. MonoIOError error;
  248. MonoIO.SetLength (handle, length, out error);
  249. }
  250. public override void Flush ()
  251. {
  252. FlushBuffer ();
  253. //
  254. // The flushing is not actually required, in the mono runtime we were
  255. // mapping flush to `fsync' which is not the same.
  256. //
  257. //MonoIO.Flush (handle);
  258. }
  259. public override void Close ()
  260. {
  261. Dispose (true);
  262. GC.SuppressFinalize (this); // remove from finalize queue
  263. }
  264. // protected
  265. ~FileStream ()
  266. {
  267. Dispose (false);
  268. }
  269. protected virtual void Dispose (bool disposing) {
  270. if (handle != MonoIO.InvalidHandle) {
  271. FlushBuffer ();
  272. MonoIOError error;
  273. MonoIO.Close (handle, out error);
  274. handle = MonoIO.InvalidHandle;
  275. }
  276. if (disposing) {
  277. buf = null;
  278. }
  279. }
  280. // private
  281. private int ReadSegment (byte [] dest, int dest_offset, int count)
  282. {
  283. if (count > buf_length - buf_offset)
  284. count = buf_length - buf_offset;
  285. if (count > 0) {
  286. Buffer.BlockCopy (buf, buf_offset, dest, dest_offset, count);
  287. buf_offset += count;
  288. }
  289. return count;
  290. }
  291. private int WriteSegment (byte [] src, int src_offset, int count)
  292. {
  293. if (count > buf_size - buf_offset)
  294. count = buf_size - buf_offset;
  295. if (count > 0) {
  296. Buffer.BlockCopy (src, src_offset, buf, buf_offset, count);
  297. buf_offset += count;
  298. if (buf_offset > buf_length)
  299. buf_length = buf_offset;
  300. buf_dirty = true;
  301. }
  302. return count;
  303. }
  304. private void FlushBuffer ()
  305. {
  306. if (buf_dirty) {
  307. MonoIOError error;
  308. if (CanSeek == true) {
  309. MonoIO.Seek (handle, buf_start,
  310. SeekOrigin.Begin,
  311. out error);
  312. }
  313. MonoIO.Write (handle, buf, 0, buf_length,
  314. out error);
  315. }
  316. buf_start += buf_length;
  317. buf_offset = buf_length = 0;
  318. buf_dirty = false;
  319. }
  320. private void RefillBuffer ()
  321. {
  322. FlushBuffer();
  323. buf_length = ReadData (handle, buf, 0, buf_size);
  324. }
  325. private int ReadData (IntPtr handle, byte[] buf, int offset,
  326. int count)
  327. {
  328. MonoIOError error;
  329. int amount = MonoIO.Read (handle, buf, offset, count,
  330. out error);
  331. /* Check for read error */
  332. if(amount == -1) {
  333. /* Kludge around broken pipes */
  334. if(error == MonoIOError.ERROR_BROKEN_PIPE) {
  335. amount = 0;
  336. } else {
  337. throw new IOException ();
  338. }
  339. }
  340. return(amount);
  341. }
  342. private void InitBuffer (int size)
  343. {
  344. if (size < 0)
  345. throw new ArgumentOutOfRangeException ("Buffer size cannot be negative.");
  346. if (size < 8)
  347. size = 8;
  348. buf = new byte [size];
  349. buf_size = size;
  350. buf_start = 0;
  351. buf_offset = buf_length = 0;
  352. buf_dirty = false;
  353. }
  354. // fields
  355. private static int DefaultBufferSize = 8192;
  356. private FileAccess access;
  357. private bool owner;
  358. private bool async;
  359. private bool canseek;
  360. private byte [] buf; // the buffer
  361. private int buf_size; // capacity in bytes
  362. private int buf_length; // number of valid bytes in buffer
  363. private int buf_offset; // position of next byte
  364. private bool buf_dirty; // true if buffer has been written to
  365. private long buf_start; // location of buffer in file
  366. private string name = "[Unknown]"; // name of file.
  367. IntPtr handle; // handle to underlying file
  368. }
  369. }