MemoryStream.cs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. //
  2. // System.IO.MemoryStream
  3. //
  4. // Authors: Marcin Szczepanski ([email protected])
  5. // Patrik Torstensson
  6. // Gonzalo Paniagua Javier ([email protected])
  7. //
  8. // (c) 2001,2002 Marcin Szczepanski, Patrik Torstensson
  9. // (c) 2003 Ximian, Inc. (http://www.ximian.com)
  10. //
  11. using System.Runtime.InteropServices;
  12. namespace System.IO
  13. {
  14. [Serializable]
  15. public class MemoryStream : Stream
  16. {
  17. bool canWrite;
  18. bool allowGetBuffer;
  19. int capacity;
  20. int length;
  21. byte [] internalBuffer;
  22. int initialIndex;
  23. bool expandable;
  24. bool streamClosed;
  25. int position;
  26. public MemoryStream () : this (0)
  27. {
  28. }
  29. public MemoryStream (int capacity)
  30. {
  31. if (capacity < 0)
  32. throw new ArgumentOutOfRangeException ("capacity");
  33. canWrite = true;
  34. this.capacity = capacity;
  35. internalBuffer = new byte [capacity];
  36. expandable = true;
  37. allowGetBuffer = true;
  38. }
  39. public MemoryStream (byte [] buffer)
  40. {
  41. if (buffer == null)
  42. throw new ArgumentNullException ("buffer");
  43. InternalConstructor (buffer, 0, buffer.Length, true, false);
  44. }
  45. public MemoryStream (byte [] buffer, bool writeable)
  46. {
  47. if (buffer == null)
  48. throw new ArgumentNullException ("buffer");
  49. InternalConstructor (buffer, 0, buffer.Length, writeable, false);
  50. }
  51. public MemoryStream (byte [] buffer, int index, int count)
  52. {
  53. InternalConstructor (buffer, index, count, true, false);
  54. }
  55. public MemoryStream (byte [] buffer, int index, int count, bool writeable)
  56. {
  57. InternalConstructor (buffer, index, count, writeable, false);
  58. }
  59. public MemoryStream (byte [] buffer, int index, int count, bool writeable, bool publicallyVisible)
  60. {
  61. InternalConstructor (buffer, index, count, writeable, publicallyVisible);
  62. }
  63. void InternalConstructor (byte [] buffer, int index, int count, bool writeable, bool publicallyVisible)
  64. {
  65. if (buffer == null)
  66. throw new ArgumentNullException ("buffer");
  67. if (index < 0 || count < 0)
  68. throw new ArgumentOutOfRangeException ("index or count is less than 0.");
  69. if (buffer.Length - index < count)
  70. throw new ArgumentException ("index+count",
  71. "The size of the buffer is less than index + count.");
  72. canWrite = writeable;
  73. internalBuffer = buffer;
  74. capacity = count + index;
  75. length = capacity;
  76. position = index;
  77. initialIndex = index;
  78. allowGetBuffer = publicallyVisible;
  79. expandable = false;
  80. }
  81. void CheckIfClosedThrowDisposed ()
  82. {
  83. if (streamClosed)
  84. throw new ObjectDisposedException ("MemoryStream");
  85. }
  86. void CheckIfClosedThrowIO ()
  87. {
  88. if (streamClosed)
  89. throw new IOException ("MemoryStream is closed");
  90. }
  91. public override bool CanRead {
  92. get { return !streamClosed; }
  93. }
  94. public override bool CanSeek {
  95. get { return !streamClosed; }
  96. }
  97. public override bool CanWrite {
  98. get { return (!streamClosed && canWrite); }
  99. }
  100. public virtual int Capacity {
  101. get {
  102. CheckIfClosedThrowDisposed ();
  103. return capacity - initialIndex;
  104. }
  105. set {
  106. CheckIfClosedThrowDisposed ();
  107. if (value == capacity)
  108. return; // LAMENESS: see MemoryStreamTest.ConstructorFive
  109. if (!expandable)
  110. throw new NotSupportedException ("Cannot expand this MemoryStream");
  111. if (value < 0 || value < length)
  112. throw new ArgumentOutOfRangeException ("value",
  113. "New capacity cannot be negative or less than the current capacity " + value + " " + capacity);
  114. byte [] newBuffer = null;
  115. if (value != 0) {
  116. newBuffer = new byte [value];
  117. Buffer.BlockCopyInternal (internalBuffer, 0, newBuffer, 0, length);
  118. }
  119. internalBuffer = newBuffer; // It's null when capacity is set to 0
  120. capacity = value;
  121. }
  122. }
  123. public override long Length {
  124. get {
  125. // LAMESPEC: The spec says to throw an IOException if the
  126. // stream is closed and an ObjectDisposedException if
  127. // "methods were called after the stream was closed". What
  128. // is the difference?
  129. CheckIfClosedThrowIO ();
  130. // This is ok for MemoryStreamTest.ConstructorFive
  131. return length - initialIndex;
  132. }
  133. }
  134. public override long Position {
  135. get {
  136. CheckIfClosedThrowIO ();
  137. return position - initialIndex;
  138. }
  139. set {
  140. CheckIfClosedThrowIO ();
  141. if (value < 0)
  142. throw new ArgumentOutOfRangeException ("value",
  143. "Position cannot be negative" );
  144. if (value > Int32.MaxValue)
  145. throw new ArgumentOutOfRangeException ("value",
  146. "Position must be non-negative and less than 2^31 - 1 - origin");
  147. position = initialIndex + (int) value;
  148. }
  149. }
  150. public override void Close ()
  151. {
  152. streamClosed = true;
  153. expandable = false;
  154. }
  155. public override void Flush ()
  156. {
  157. // Do nothing
  158. }
  159. public virtual byte [] GetBuffer ()
  160. {
  161. if (!allowGetBuffer)
  162. throw new UnauthorizedAccessException ();
  163. return internalBuffer;
  164. }
  165. public override int Read ([In,Out] byte [] buffer, int offset, int count)
  166. {
  167. CheckIfClosedThrowDisposed ();
  168. if (buffer == null)
  169. throw new ArgumentNullException ("buffer");
  170. if (offset < 0 || count < 0)
  171. throw new ArgumentOutOfRangeException ("offset or count less than zero.");
  172. if (buffer.Length - offset < count )
  173. throw new ArgumentException ("offset+count",
  174. "The size of the buffer is less than offset + count.");
  175. if (position >= length || count == 0)
  176. return 0;
  177. if (position > length - count)
  178. count = length - position;
  179. Buffer.BlockCopyInternal (internalBuffer, position, buffer, offset, count);
  180. position += count;
  181. return count;
  182. }
  183. public override int ReadByte ()
  184. {
  185. CheckIfClosedThrowDisposed ();
  186. if (position >= length)
  187. return -1;
  188. return internalBuffer [position++];
  189. }
  190. public override long Seek (long offset, SeekOrigin loc)
  191. {
  192. CheckIfClosedThrowDisposed ();
  193. // It's funny that they don't throw this exception for < Int32.MinValue
  194. if (offset > (long) Int32.MaxValue)
  195. throw new ArgumentOutOfRangeException ("Offset out of range. " + offset);
  196. int refPoint;
  197. switch (loc) {
  198. case SeekOrigin.Begin:
  199. if (offset < 0)
  200. throw new IOException ("Attempted to seek before start of MemoryStream.");
  201. refPoint = initialIndex;
  202. break;
  203. case SeekOrigin.Current:
  204. refPoint = position;
  205. break;
  206. case SeekOrigin.End:
  207. refPoint = length;
  208. break;
  209. default:
  210. throw new ArgumentException ("loc", "Invalid SeekOrigin");
  211. }
  212. // LAMESPEC: My goodness, how may LAMESPECs are there in this
  213. // class! :) In the spec for the Position property it's stated
  214. // "The position must not be more than one byte beyond the end of the stream."
  215. // In the spec for seek it says "Seeking to any location beyond the length of the
  216. // stream is supported." That's a contradiction i'd say.
  217. // I guess seek can go anywhere but if you use position it may get moved back.
  218. refPoint += (int) offset;
  219. if (refPoint < initialIndex)
  220. throw new IOException ("Attempted to seek before start of MemoryStream.");
  221. position = refPoint;
  222. return position;
  223. }
  224. int CalculateNewCapacity (int minimum)
  225. {
  226. if (minimum < 256)
  227. minimum = 256; // See GetBufferTwo test
  228. if (minimum < capacity * 2)
  229. minimum = capacity * 2;
  230. return minimum;
  231. }
  232. public override void SetLength (long value)
  233. {
  234. if (!expandable && value > capacity)
  235. throw new NotSupportedException ("Expanding this MemoryStream is not supported");
  236. CheckIfClosedThrowDisposed ();
  237. if (!canWrite)
  238. throw new IOException ("Cannot write to this MemoryStream");
  239. // LAMESPEC: AGAIN! It says to throw this exception if value is
  240. // greater than "the maximum length of the MemoryStream". I haven't
  241. // seen anywhere mention what the maximum length of a MemoryStream is and
  242. // since we're this far this memory stream is expandable.
  243. if (value < 0 || (value + initialIndex) > (long) Int32.MaxValue)
  244. throw new ArgumentOutOfRangeException ();
  245. int newSize = (int) value + initialIndex;
  246. if (newSize > capacity) {
  247. Capacity = CalculateNewCapacity (newSize);
  248. } else if (newSize > length) {
  249. for (int i = newSize; i < length; i++)
  250. Buffer.SetByte (internalBuffer, i, 0);
  251. }
  252. length = newSize;
  253. if (position > length)
  254. position = length;
  255. }
  256. public virtual byte [] ToArray ()
  257. {
  258. int l = length - initialIndex;
  259. byte[] outBuffer = new byte [l];
  260. Buffer.BlockCopyInternal (internalBuffer, initialIndex, outBuffer, 0, l);
  261. return outBuffer;
  262. }
  263. public override void Write (byte [] buffer, int offset, int count)
  264. {
  265. CheckIfClosedThrowDisposed ();
  266. if (!canWrite)
  267. throw new NotSupportedException ("Cannot write to this stream.");
  268. if (buffer == null)
  269. throw new ArgumentNullException ("buffer");
  270. if (offset < 0 || count < 0)
  271. throw new ArgumentOutOfRangeException ();
  272. if (buffer.Length - offset < count)
  273. throw new ArgumentException ("offset+count",
  274. "The size of the buffer is less than offset + count.");
  275. if (position + count > capacity)
  276. Capacity = CalculateNewCapacity (position + count);
  277. Buffer.BlockCopyInternal (buffer, offset, internalBuffer, position, count);
  278. position += count;
  279. if (position >= length)
  280. length = position;
  281. }
  282. public override void WriteByte (byte value)
  283. {
  284. CheckIfClosedThrowDisposed ();
  285. if (!canWrite)
  286. throw new NotSupportedException ("Cannot write to this stream.");
  287. if (position >= capacity)
  288. Capacity = CalculateNewCapacity (position + 1);
  289. if (position >= length)
  290. length = position + 1;
  291. internalBuffer [position++] = value;
  292. }
  293. public virtual void WriteTo (Stream stream)
  294. {
  295. CheckIfClosedThrowDisposed ();
  296. if (stream == null)
  297. throw new ArgumentNullException ("stream");
  298. stream.Write (internalBuffer, initialIndex, length - initialIndex);
  299. }
  300. }
  301. }