BufferedOutputStream.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. //------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //------------------------------------------------------------
  4. namespace System.Runtime
  5. {
  6. using System;
  7. using System.Globalization;
  8. using System.IO;
  9. class BufferedOutputStream : Stream
  10. {
  11. [Fx.Tag.Cache(typeof(byte), Fx.Tag.CacheAttrition.None, Scope = Fx.Tag.Strings.ExternallyManaged,
  12. SizeLimit = Fx.Tag.Strings.ExternallyManaged)]
  13. InternalBufferManager bufferManager;
  14. [Fx.Tag.Queue(typeof(byte), SizeLimit = "BufferedOutputStream(maxSize)",
  15. StaleElementsRemovedImmediately = true, EnqueueThrowsIfFull = true)]
  16. byte[][] chunks;
  17. int chunkCount;
  18. byte[] currentChunk;
  19. int currentChunkSize;
  20. int maxSize;
  21. int maxSizeQuota;
  22. int totalSize;
  23. bool callerReturnsBuffer;
  24. bool bufferReturned;
  25. bool initialized;
  26. // requires an explicit call to Init() by the caller
  27. public BufferedOutputStream()
  28. {
  29. this.chunks = new byte[4][];
  30. }
  31. public BufferedOutputStream(int initialSize, int maxSize, InternalBufferManager bufferManager)
  32. : this()
  33. {
  34. Reinitialize(initialSize, maxSize, bufferManager);
  35. }
  36. public BufferedOutputStream(int maxSize)
  37. : this(0, maxSize, InternalBufferManager.Create(0, int.MaxValue))
  38. {
  39. }
  40. public override bool CanRead
  41. {
  42. get
  43. {
  44. return false;
  45. }
  46. }
  47. public override bool CanSeek
  48. {
  49. get
  50. {
  51. return false;
  52. }
  53. }
  54. public override bool CanWrite
  55. {
  56. get
  57. {
  58. return true;
  59. }
  60. }
  61. public override long Length
  62. {
  63. get
  64. {
  65. return this.totalSize;
  66. }
  67. }
  68. public override long Position
  69. {
  70. get
  71. {
  72. throw Fx.Exception.AsError(new NotSupportedException(InternalSR.SeekNotSupported));
  73. }
  74. set
  75. {
  76. throw Fx.Exception.AsError(new NotSupportedException(InternalSR.SeekNotSupported));
  77. }
  78. }
  79. public void Reinitialize(int initialSize, int maxSizeQuota, InternalBufferManager bufferManager)
  80. {
  81. Reinitialize(initialSize, maxSizeQuota, maxSizeQuota, bufferManager);
  82. }
  83. public void Reinitialize(int initialSize, int maxSizeQuota, int effectiveMaxSize, InternalBufferManager bufferManager)
  84. {
  85. Fx.Assert(!this.initialized, "Clear must be called before re-initializing stream");
  86. this.maxSizeQuota = maxSizeQuota;
  87. this.maxSize = effectiveMaxSize;
  88. this.bufferManager = bufferManager;
  89. this.currentChunk = bufferManager.TakeBuffer(initialSize);
  90. this.currentChunkSize = 0;
  91. this.totalSize = 0;
  92. this.chunkCount = 1;
  93. this.chunks[0] = this.currentChunk;
  94. this.initialized = true;
  95. }
  96. void AllocNextChunk(int minimumChunkSize)
  97. {
  98. int newChunkSize;
  99. if (this.currentChunk.Length > (int.MaxValue / 2))
  100. {
  101. newChunkSize = int.MaxValue;
  102. }
  103. else
  104. {
  105. newChunkSize = this.currentChunk.Length * 2;
  106. }
  107. if (minimumChunkSize > newChunkSize)
  108. {
  109. newChunkSize = minimumChunkSize;
  110. }
  111. byte[] newChunk = this.bufferManager.TakeBuffer(newChunkSize);
  112. if (this.chunkCount == this.chunks.Length)
  113. {
  114. byte[][] newChunks = new byte[this.chunks.Length * 2][];
  115. Array.Copy(this.chunks, newChunks, this.chunks.Length);
  116. this.chunks = newChunks;
  117. }
  118. this.chunks[this.chunkCount++] = newChunk;
  119. this.currentChunk = newChunk;
  120. this.currentChunkSize = 0;
  121. }
  122. public override IAsyncResult BeginRead(byte[] buffer, int offset, int size, AsyncCallback callback, object state)
  123. {
  124. throw Fx.Exception.AsError(new NotSupportedException(InternalSR.ReadNotSupported));
  125. }
  126. public override int EndRead(IAsyncResult result)
  127. {
  128. throw Fx.Exception.AsError(new NotSupportedException(InternalSR.ReadNotSupported));
  129. }
  130. public override IAsyncResult BeginWrite(byte[] buffer, int offset, int size, AsyncCallback callback, object state)
  131. {
  132. Write(buffer, offset, size);
  133. return new CompletedAsyncResult(callback, state);
  134. }
  135. public override void EndWrite(IAsyncResult result)
  136. {
  137. CompletedAsyncResult.End(result);
  138. }
  139. public void Clear()
  140. {
  141. if (!this.callerReturnsBuffer)
  142. {
  143. for (int i = 0; i < this.chunkCount; i++)
  144. {
  145. this.bufferManager.ReturnBuffer(this.chunks[i]);
  146. this.chunks[i] = null;
  147. }
  148. }
  149. this.callerReturnsBuffer = false;
  150. this.initialized = false;
  151. this.bufferReturned = false;
  152. this.chunkCount = 0;
  153. this.currentChunk = null;
  154. }
  155. public override void Close()
  156. {
  157. }
  158. public override void Flush()
  159. {
  160. }
  161. public override int Read(byte[] buffer, int offset, int size)
  162. {
  163. throw Fx.Exception.AsError(new NotSupportedException(InternalSR.ReadNotSupported));
  164. }
  165. public override int ReadByte()
  166. {
  167. throw Fx.Exception.AsError(new NotSupportedException(InternalSR.ReadNotSupported));
  168. }
  169. public override long Seek(long offset, SeekOrigin origin)
  170. {
  171. throw Fx.Exception.AsError(new NotSupportedException(InternalSR.SeekNotSupported));
  172. }
  173. public override void SetLength(long value)
  174. {
  175. throw Fx.Exception.AsError(new NotSupportedException(InternalSR.SeekNotSupported));
  176. }
  177. public MemoryStream ToMemoryStream()
  178. {
  179. int bufferSize;
  180. byte[] buffer = ToArray(out bufferSize);
  181. return new MemoryStream(buffer, 0, bufferSize);
  182. }
  183. public byte[] ToArray(out int bufferSize)
  184. {
  185. Fx.Assert(this.initialized, "No data to return from uninitialized stream");
  186. Fx.Assert(!this.bufferReturned, "ToArray cannot be called more than once");
  187. byte[] buffer;
  188. if (this.chunkCount == 1)
  189. {
  190. buffer = this.currentChunk;
  191. bufferSize = this.currentChunkSize;
  192. this.callerReturnsBuffer = true;
  193. }
  194. else
  195. {
  196. buffer = this.bufferManager.TakeBuffer(this.totalSize);
  197. int offset = 0;
  198. int count = this.chunkCount - 1;
  199. for (int i = 0; i < count; i++)
  200. {
  201. byte[] chunk = this.chunks[i];
  202. Buffer.BlockCopy(chunk, 0, buffer, offset, chunk.Length);
  203. offset += chunk.Length;
  204. }
  205. Buffer.BlockCopy(this.currentChunk, 0, buffer, offset, this.currentChunkSize);
  206. bufferSize = this.totalSize;
  207. }
  208. this.bufferReturned = true;
  209. return buffer;
  210. }
  211. public void Skip(int size)
  212. {
  213. WriteCore(null, 0, size);
  214. }
  215. public override void Write(byte[] buffer, int offset, int size)
  216. {
  217. WriteCore(buffer, offset, size);
  218. }
  219. protected virtual Exception CreateQuotaExceededException(int maxSizeQuota)
  220. {
  221. return new InvalidOperationException(InternalSR.BufferedOutputStreamQuotaExceeded(maxSizeQuota));
  222. }
  223. void WriteCore(byte[] buffer, int offset, int size)
  224. {
  225. Fx.Assert(this.initialized, "Cannot write to uninitialized stream");
  226. Fx.Assert(!this.bufferReturned, "Cannot write to stream once ToArray has been called.");
  227. if (size < 0)
  228. {
  229. throw Fx.Exception.ArgumentOutOfRange("size", size, InternalSR.ValueMustBeNonNegative);
  230. }
  231. if ((int.MaxValue - size) < this.totalSize)
  232. {
  233. throw Fx.Exception.AsError(CreateQuotaExceededException(this.maxSizeQuota));
  234. }
  235. int newTotalSize = this.totalSize + size;
  236. if (newTotalSize > this.maxSize)
  237. {
  238. throw Fx.Exception.AsError(CreateQuotaExceededException(this.maxSizeQuota));
  239. }
  240. int remainingSizeInChunk = this.currentChunk.Length - this.currentChunkSize;
  241. if (size > remainingSizeInChunk)
  242. {
  243. if (remainingSizeInChunk > 0)
  244. {
  245. if (buffer != null)
  246. {
  247. Buffer.BlockCopy(buffer, offset, this.currentChunk, this.currentChunkSize, remainingSizeInChunk);
  248. }
  249. this.currentChunkSize = this.currentChunk.Length;
  250. offset += remainingSizeInChunk;
  251. size -= remainingSizeInChunk;
  252. }
  253. AllocNextChunk(size);
  254. }
  255. if (buffer != null)
  256. {
  257. Buffer.BlockCopy(buffer, offset, this.currentChunk, this.currentChunkSize, size);
  258. }
  259. this.totalSize = newTotalSize;
  260. this.currentChunkSize += size;
  261. }
  262. public override void WriteByte(byte value)
  263. {
  264. Fx.Assert(this.initialized, "Cannot write to uninitialized stream");
  265. Fx.Assert(!this.bufferReturned, "Cannot write to stream once ToArray has been called.");
  266. if (this.totalSize == this.maxSize)
  267. {
  268. throw Fx.Exception.AsError(CreateQuotaExceededException(this.maxSize));
  269. }
  270. if (this.currentChunkSize == this.currentChunk.Length)
  271. {
  272. AllocNextChunk(1);
  273. }
  274. this.currentChunk[this.currentChunkSize++] = value;
  275. }
  276. }
  277. }