MemoryStream.cs 9.4 KB

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