MemoryStream.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. //
  2. // System.IO.MemoryStream.cs
  3. //
  4. // Authors: Marcin Szczepanski ([email protected])
  5. // Patrik Torstensson
  6. // Gonzalo Paniagua Javier ([email protected])
  7. // Marek Safar ([email protected])
  8. //
  9. // (c) 2001,2002 Marcin Szczepanski, Patrik Torstensson
  10. // (c) 2003 Ximian, Inc. (http://www.ximian.com)
  11. // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
  12. // Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
  13. //
  14. // Permission is hereby granted, free of charge, to any person obtaining
  15. // a copy of this software and associated documentation files (the
  16. // "Software"), to deal in the Software without restriction, including
  17. // without limitation the rights to use, copy, modify, merge, publish,
  18. // distribute, sublicense, and/or sell copies of the Software, and to
  19. // permit persons to whom the Software is furnished to do so, subject to
  20. // the following conditions:
  21. //
  22. // The above copyright notice and this permission notice shall be
  23. // included in all copies or substantial portions of the Software.
  24. //
  25. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  26. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  27. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  28. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  29. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  30. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  31. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  32. //
  33. using System.Globalization;
  34. using System.Runtime.InteropServices;
  35. using System.Threading;
  36. #if NET_4_5
  37. using System.Threading.Tasks;
  38. #endif
  39. namespace System.IO
  40. {
  41. [Serializable]
  42. [ComVisible (true)]
  43. [MonoLimitation ("Serialization format not compatible with .NET")]
  44. public class MemoryStream : Stream
  45. {
  46. bool canWrite;
  47. bool allowGetBuffer;
  48. int capacity;
  49. int length;
  50. byte [] internalBuffer;
  51. int initialIndex;
  52. bool expandable;
  53. bool streamClosed;
  54. int position;
  55. int dirty_bytes;
  56. #if NET_4_5
  57. [NonSerialized]
  58. Task<int> read_task;
  59. #endif
  60. public MemoryStream () : this (0)
  61. {
  62. }
  63. public MemoryStream (int capacity)
  64. {
  65. if (capacity < 0)
  66. throw new ArgumentOutOfRangeException ("capacity");
  67. canWrite = true;
  68. this.capacity = capacity;
  69. internalBuffer = new byte [capacity];
  70. expandable = true;
  71. allowGetBuffer = true;
  72. }
  73. public MemoryStream (byte [] buffer)
  74. {
  75. if (buffer == null)
  76. throw new ArgumentNullException ("buffer");
  77. InternalConstructor (buffer, 0, buffer.Length, true, false);
  78. }
  79. public MemoryStream (byte [] buffer, bool writable)
  80. {
  81. if (buffer == null)
  82. throw new ArgumentNullException ("buffer");
  83. InternalConstructor (buffer, 0, buffer.Length, writable, false);
  84. }
  85. public MemoryStream (byte [] buffer, int index, int count)
  86. {
  87. InternalConstructor (buffer, index, count, true, false);
  88. }
  89. public MemoryStream (byte [] buffer, int index, int count, bool writable)
  90. {
  91. InternalConstructor (buffer, index, count, writable, false);
  92. }
  93. public MemoryStream (byte [] buffer, int index, int count, bool writable, bool publiclyVisible)
  94. {
  95. InternalConstructor (buffer, index, count, writable, publiclyVisible);
  96. }
  97. void InternalConstructor (byte [] buffer, int index, int count, bool writable, bool publicallyVisible)
  98. {
  99. if (buffer == null)
  100. throw new ArgumentNullException ("buffer");
  101. if (index < 0 || count < 0)
  102. throw new ArgumentOutOfRangeException ("index or count is less than 0.");
  103. if (buffer.Length - index < count)
  104. throw new ArgumentException ("index+count",
  105. "The size of the buffer is less than index + count.");
  106. canWrite = writable;
  107. internalBuffer = buffer;
  108. capacity = count + index;
  109. length = capacity;
  110. position = index;
  111. initialIndex = index;
  112. allowGetBuffer = publicallyVisible;
  113. expandable = false;
  114. }
  115. void CheckIfClosedThrowDisposed ()
  116. {
  117. if (streamClosed)
  118. throw new ObjectDisposedException ("MemoryStream");
  119. }
  120. public override bool CanRead {
  121. get { return !streamClosed; }
  122. }
  123. public override bool CanSeek {
  124. get { return !streamClosed; }
  125. }
  126. public override bool CanWrite {
  127. get { return (!streamClosed && canWrite); }
  128. }
  129. public virtual int Capacity {
  130. get {
  131. CheckIfClosedThrowDisposed ();
  132. return capacity - initialIndex;
  133. }
  134. set {
  135. CheckIfClosedThrowDisposed ();
  136. if (!expandable)
  137. throw new NotSupportedException ("Cannot expand this MemoryStream");
  138. if (value < 0 || value < length)
  139. throw new ArgumentOutOfRangeException ("value",
  140. "New capacity cannot be negative or less than the current capacity " + value + " " + capacity);
  141. if (internalBuffer != null && value == internalBuffer.Length)
  142. return;
  143. byte [] newBuffer = null;
  144. if (value != 0) {
  145. newBuffer = new byte [value];
  146. if (internalBuffer != null)
  147. Buffer.BlockCopy (internalBuffer, 0, newBuffer, 0, length);
  148. }
  149. dirty_bytes = 0; // discard any dirty area beyond previous length
  150. internalBuffer = newBuffer; // It's null when capacity is set to 0
  151. capacity = value;
  152. }
  153. }
  154. public override long Length {
  155. get {
  156. // LAMESPEC: The spec says to throw an IOException if the
  157. // stream is closed and an ObjectDisposedException if
  158. // "methods were called after the stream was closed". What
  159. // is the difference?
  160. CheckIfClosedThrowDisposed ();
  161. // This is ok for MemoryStreamTest.ConstructorFive
  162. return length - initialIndex;
  163. }
  164. }
  165. public override long Position {
  166. get {
  167. CheckIfClosedThrowDisposed ();
  168. return position - initialIndex;
  169. }
  170. set {
  171. CheckIfClosedThrowDisposed ();
  172. if (value < 0)
  173. throw new ArgumentOutOfRangeException ("value",
  174. "Position cannot be negative" );
  175. if (value > Int32.MaxValue)
  176. throw new ArgumentOutOfRangeException ("value",
  177. "Position must be non-negative and less than 2^31 - 1 - origin");
  178. position = initialIndex + (int) value;
  179. }
  180. }
  181. protected override void Dispose (bool disposing)
  182. {
  183. streamClosed = true;
  184. expandable = false;
  185. }
  186. public override void Flush ()
  187. {
  188. // Do nothing
  189. }
  190. public virtual byte [] GetBuffer ()
  191. {
  192. if (!allowGetBuffer)
  193. throw new UnauthorizedAccessException ();
  194. return internalBuffer;
  195. }
  196. public override int Read ([In,Out] byte [] buffer, int offset, int count)
  197. {
  198. if (buffer == null)
  199. throw new ArgumentNullException ("buffer");
  200. if (offset < 0 || count < 0)
  201. throw new ArgumentOutOfRangeException ("offset or count less than zero.");
  202. if (buffer.Length - offset < count )
  203. throw new ArgumentException ("offset+count",
  204. "The size of the buffer is less than offset + count.");
  205. CheckIfClosedThrowDisposed ();
  206. if (position >= length || count == 0)
  207. return 0;
  208. if (position > length - count)
  209. count = length - position;
  210. Buffer.BlockCopy (internalBuffer, position, buffer, offset, count);
  211. position += count;
  212. return count;
  213. }
  214. public override int ReadByte ()
  215. {
  216. CheckIfClosedThrowDisposed ();
  217. if (position >= length)
  218. return -1;
  219. return internalBuffer [position++];
  220. }
  221. public override long Seek (long offset, SeekOrigin loc)
  222. {
  223. CheckIfClosedThrowDisposed ();
  224. // It's funny that they don't throw this exception for < Int32.MinValue
  225. if (offset > (long) Int32.MaxValue)
  226. throw new ArgumentOutOfRangeException ("Offset out of range. " + offset);
  227. int refPoint;
  228. switch (loc) {
  229. case SeekOrigin.Begin:
  230. if (offset < 0)
  231. throw new IOException ("Attempted to seek before start of MemoryStream.");
  232. refPoint = initialIndex;
  233. break;
  234. case SeekOrigin.Current:
  235. refPoint = position;
  236. break;
  237. case SeekOrigin.End:
  238. refPoint = length;
  239. break;
  240. default:
  241. throw new ArgumentException ("loc", "Invalid SeekOrigin");
  242. }
  243. // LAMESPEC: My goodness, how may LAMESPECs are there in this
  244. // class! :) In the spec for the Position property it's stated
  245. // "The position must not be more than one byte beyond the end of the stream."
  246. // In the spec for seek it says "Seeking to any location beyond the length of the
  247. // stream is supported." That's a contradiction i'd say.
  248. // I guess seek can go anywhere but if you use position it may get moved back.
  249. refPoint += (int) offset;
  250. if (refPoint < initialIndex)
  251. throw new IOException ("Attempted to seek before start of MemoryStream.");
  252. position = refPoint;
  253. return position;
  254. }
  255. int CalculateNewCapacity (int minimum)
  256. {
  257. if (minimum < 256)
  258. minimum = 256; // See GetBufferTwo test
  259. if (minimum < capacity * 2)
  260. minimum = capacity * 2;
  261. return minimum;
  262. }
  263. void Expand (int newSize)
  264. {
  265. // We don't need to take into account the dirty bytes when incrementing the
  266. // Capacity, as changing it will only preserve the valid clear region.
  267. if (newSize > capacity)
  268. Capacity = CalculateNewCapacity (newSize);
  269. else if (dirty_bytes > 0) {
  270. Array.Clear (internalBuffer, length, dirty_bytes);
  271. dirty_bytes = 0;
  272. }
  273. }
  274. public override void SetLength (long value)
  275. {
  276. if (!expandable && value > capacity)
  277. throw new NotSupportedException ("Expanding this MemoryStream is not supported");
  278. CheckIfClosedThrowDisposed ();
  279. if (!canWrite) {
  280. throw new NotSupportedException (Locale.GetText
  281. ("Cannot write to this MemoryStream"));
  282. }
  283. // LAMESPEC: AGAIN! It says to throw this exception if value is
  284. // greater than "the maximum length of the MemoryStream". I haven't
  285. // seen anywhere mention what the maximum length of a MemoryStream is and
  286. // since we're this far this memory stream is expandable.
  287. if (value < 0 || (value + initialIndex) > (long) Int32.MaxValue)
  288. throw new ArgumentOutOfRangeException ();
  289. int newSize = (int) value + initialIndex;
  290. if (newSize > length)
  291. Expand (newSize);
  292. else if (newSize < length) // Postpone the call to Array.Clear till expand time
  293. dirty_bytes += length - newSize;
  294. length = newSize;
  295. if (position > length)
  296. position = length;
  297. }
  298. public virtual byte [] ToArray ()
  299. {
  300. int l = length - initialIndex;
  301. byte[] outBuffer = new byte [l];
  302. if (internalBuffer != null)
  303. Buffer.BlockCopy (internalBuffer, initialIndex, outBuffer, 0, l);
  304. return outBuffer;
  305. }
  306. public override void Write (byte [] buffer, int offset, int count)
  307. {
  308. if (buffer == null)
  309. throw new ArgumentNullException ("buffer");
  310. if (offset < 0 || count < 0)
  311. throw new ArgumentOutOfRangeException ();
  312. if (buffer.Length - offset < count)
  313. throw new ArgumentException ("offset+count",
  314. "The size of the buffer is less than offset + count.");
  315. CheckIfClosedThrowDisposed ();
  316. if (!CanWrite)
  317. throw new NotSupportedException ("Cannot write to this stream.");
  318. // reordered to avoid possible integer overflow
  319. if (position > length - count)
  320. Expand (position + count);
  321. Buffer.BlockCopy (buffer, offset, internalBuffer, position, count);
  322. position += count;
  323. if (position >= length)
  324. length = position;
  325. }
  326. public override void WriteByte (byte value)
  327. {
  328. CheckIfClosedThrowDisposed ();
  329. if (!canWrite)
  330. throw new NotSupportedException ("Cannot write to this stream.");
  331. if (position >= length) {
  332. Expand (position + 1);
  333. length = position + 1;
  334. }
  335. internalBuffer [position++] = value;
  336. }
  337. public virtual void WriteTo (Stream stream)
  338. {
  339. CheckIfClosedThrowDisposed ();
  340. if (stream == null)
  341. throw new ArgumentNullException ("stream");
  342. stream.Write (internalBuffer, initialIndex, length - initialIndex);
  343. }
  344. #if NET_4_5
  345. public override Task CopyToAsync (Stream destination, int bufferSize, CancellationToken cancellationToken)
  346. {
  347. // TODO: Specialization but what for?
  348. return base.CopyToAsync (destination, bufferSize, cancellationToken);
  349. }
  350. public override Task FlushAsync (CancellationToken cancellationToken)
  351. {
  352. if (cancellationToken.IsCancellationRequested)
  353. return TaskConstants.Canceled;
  354. try {
  355. Flush ();
  356. return TaskConstants.Finished;
  357. } catch (Exception ex) {
  358. return Task<object>.FromException (ex);
  359. }
  360. }
  361. public override Task<int> ReadAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken)
  362. {
  363. if (buffer == null)
  364. throw new ArgumentNullException ("buffer");
  365. if (offset < 0 || count < 0)
  366. throw new ArgumentOutOfRangeException ("offset or count less than zero.");
  367. if (buffer.Length - offset < count )
  368. throw new ArgumentException ("offset+count",
  369. "The size of the buffer is less than offset + count.");
  370. if (cancellationToken.IsCancellationRequested)
  371. return TaskConstants<int>.Canceled;
  372. try {
  373. count = Read (buffer, offset, count);
  374. // Try not to allocate a new task for every buffer read
  375. if (read_task == null || read_task.Result != count)
  376. read_task = Task<int>.FromResult (count);
  377. return read_task;
  378. } catch (Exception ex) {
  379. return Task<int>.FromException (ex);
  380. }
  381. }
  382. public override Task WriteAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken)
  383. {
  384. if (buffer == null)
  385. throw new ArgumentNullException ("buffer");
  386. if (offset < 0 || count < 0)
  387. throw new ArgumentOutOfRangeException ();
  388. if (buffer.Length - offset < count)
  389. throw new ArgumentException ("offset+count",
  390. "The size of the buffer is less than offset + count.");
  391. if (cancellationToken.IsCancellationRequested)
  392. return TaskConstants.Canceled;
  393. try {
  394. Write (buffer, offset, count);
  395. return TaskConstants.Finished;
  396. } catch (Exception ex) {
  397. return Task<object>.FromException (ex);
  398. }
  399. }
  400. #endif
  401. }
  402. }