| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- //------------------------------------------------------------
- // Copyright (c) Microsoft Corporation. All rights reserved.
- //------------------------------------------------------------
- namespace System.Runtime
- {
- using System;
- using System.Globalization;
- using System.IO;
- class BufferedOutputStream : Stream
- {
- [Fx.Tag.Cache(typeof(byte), Fx.Tag.CacheAttrition.None, Scope = Fx.Tag.Strings.ExternallyManaged,
- SizeLimit = Fx.Tag.Strings.ExternallyManaged)]
- InternalBufferManager bufferManager;
- [Fx.Tag.Queue(typeof(byte), SizeLimit = "BufferedOutputStream(maxSize)",
- StaleElementsRemovedImmediately = true, EnqueueThrowsIfFull = true)]
- byte[][] chunks;
- int chunkCount;
- byte[] currentChunk;
- int currentChunkSize;
- int maxSize;
- int maxSizeQuota;
- int totalSize;
- bool callerReturnsBuffer;
- bool bufferReturned;
- bool initialized;
- // requires an explicit call to Init() by the caller
- public BufferedOutputStream()
- {
- this.chunks = new byte[4][];
- }
- public BufferedOutputStream(int initialSize, int maxSize, InternalBufferManager bufferManager)
- : this()
- {
- Reinitialize(initialSize, maxSize, bufferManager);
- }
- public BufferedOutputStream(int maxSize)
- : this(0, maxSize, InternalBufferManager.Create(0, int.MaxValue))
- {
- }
- public override bool CanRead
- {
- get
- {
- return false;
- }
- }
- public override bool CanSeek
- {
- get
- {
- return false;
- }
- }
- public override bool CanWrite
- {
- get
- {
- return true;
- }
- }
- public override long Length
- {
- get
- {
- return this.totalSize;
- }
- }
- public override long Position
- {
- get
- {
- throw Fx.Exception.AsError(new NotSupportedException(InternalSR.SeekNotSupported));
- }
- set
- {
- throw Fx.Exception.AsError(new NotSupportedException(InternalSR.SeekNotSupported));
- }
- }
- public void Reinitialize(int initialSize, int maxSizeQuota, InternalBufferManager bufferManager)
- {
- Reinitialize(initialSize, maxSizeQuota, maxSizeQuota, bufferManager);
- }
- public void Reinitialize(int initialSize, int maxSizeQuota, int effectiveMaxSize, InternalBufferManager bufferManager)
- {
- Fx.Assert(!this.initialized, "Clear must be called before re-initializing stream");
- this.maxSizeQuota = maxSizeQuota;
- this.maxSize = effectiveMaxSize;
- this.bufferManager = bufferManager;
- this.currentChunk = bufferManager.TakeBuffer(initialSize);
- this.currentChunkSize = 0;
- this.totalSize = 0;
- this.chunkCount = 1;
- this.chunks[0] = this.currentChunk;
- this.initialized = true;
- }
- void AllocNextChunk(int minimumChunkSize)
- {
- int newChunkSize;
- if (this.currentChunk.Length > (int.MaxValue / 2))
- {
- newChunkSize = int.MaxValue;
- }
- else
- {
- newChunkSize = this.currentChunk.Length * 2;
- }
- if (minimumChunkSize > newChunkSize)
- {
- newChunkSize = minimumChunkSize;
- }
- byte[] newChunk = this.bufferManager.TakeBuffer(newChunkSize);
- if (this.chunkCount == this.chunks.Length)
- {
- byte[][] newChunks = new byte[this.chunks.Length * 2][];
- Array.Copy(this.chunks, newChunks, this.chunks.Length);
- this.chunks = newChunks;
- }
- this.chunks[this.chunkCount++] = newChunk;
- this.currentChunk = newChunk;
- this.currentChunkSize = 0;
- }
- public override IAsyncResult BeginRead(byte[] buffer, int offset, int size, AsyncCallback callback, object state)
- {
- throw Fx.Exception.AsError(new NotSupportedException(InternalSR.ReadNotSupported));
- }
- public override int EndRead(IAsyncResult result)
- {
- throw Fx.Exception.AsError(new NotSupportedException(InternalSR.ReadNotSupported));
- }
- public override IAsyncResult BeginWrite(byte[] buffer, int offset, int size, AsyncCallback callback, object state)
- {
- Write(buffer, offset, size);
- return new CompletedAsyncResult(callback, state);
- }
- public override void EndWrite(IAsyncResult result)
- {
- CompletedAsyncResult.End(result);
- }
- public void Clear()
- {
- if (!this.callerReturnsBuffer)
- {
- for (int i = 0; i < this.chunkCount; i++)
- {
- this.bufferManager.ReturnBuffer(this.chunks[i]);
- this.chunks[i] = null;
- }
- }
- this.callerReturnsBuffer = false;
- this.initialized = false;
- this.bufferReturned = false;
- this.chunkCount = 0;
- this.currentChunk = null;
- }
- public override void Close()
- {
- }
- public override void Flush()
- {
- }
- public override int Read(byte[] buffer, int offset, int size)
- {
- throw Fx.Exception.AsError(new NotSupportedException(InternalSR.ReadNotSupported));
- }
- public override int ReadByte()
- {
- throw Fx.Exception.AsError(new NotSupportedException(InternalSR.ReadNotSupported));
- }
- public override long Seek(long offset, SeekOrigin origin)
- {
- throw Fx.Exception.AsError(new NotSupportedException(InternalSR.SeekNotSupported));
- }
- public override void SetLength(long value)
- {
- throw Fx.Exception.AsError(new NotSupportedException(InternalSR.SeekNotSupported));
- }
- public MemoryStream ToMemoryStream()
- {
- int bufferSize;
- byte[] buffer = ToArray(out bufferSize);
- return new MemoryStream(buffer, 0, bufferSize);
- }
- public byte[] ToArray(out int bufferSize)
- {
- Fx.Assert(this.initialized, "No data to return from uninitialized stream");
- Fx.Assert(!this.bufferReturned, "ToArray cannot be called more than once");
- byte[] buffer;
- if (this.chunkCount == 1)
- {
- buffer = this.currentChunk;
- bufferSize = this.currentChunkSize;
- this.callerReturnsBuffer = true;
- }
- else
- {
- buffer = this.bufferManager.TakeBuffer(this.totalSize);
- int offset = 0;
- int count = this.chunkCount - 1;
- for (int i = 0; i < count; i++)
- {
- byte[] chunk = this.chunks[i];
- Buffer.BlockCopy(chunk, 0, buffer, offset, chunk.Length);
- offset += chunk.Length;
- }
- Buffer.BlockCopy(this.currentChunk, 0, buffer, offset, this.currentChunkSize);
- bufferSize = this.totalSize;
- }
- this.bufferReturned = true;
- return buffer;
- }
- public void Skip(int size)
- {
- WriteCore(null, 0, size);
- }
- public override void Write(byte[] buffer, int offset, int size)
- {
- WriteCore(buffer, offset, size);
- }
- protected virtual Exception CreateQuotaExceededException(int maxSizeQuota)
- {
- return new InvalidOperationException(InternalSR.BufferedOutputStreamQuotaExceeded(maxSizeQuota));
- }
- void WriteCore(byte[] buffer, int offset, int size)
- {
- Fx.Assert(this.initialized, "Cannot write to uninitialized stream");
- Fx.Assert(!this.bufferReturned, "Cannot write to stream once ToArray has been called.");
- if (size < 0)
- {
- throw Fx.Exception.ArgumentOutOfRange("size", size, InternalSR.ValueMustBeNonNegative);
- }
- if ((int.MaxValue - size) < this.totalSize)
- {
- throw Fx.Exception.AsError(CreateQuotaExceededException(this.maxSizeQuota));
- }
- int newTotalSize = this.totalSize + size;
- if (newTotalSize > this.maxSize)
- {
- throw Fx.Exception.AsError(CreateQuotaExceededException(this.maxSizeQuota));
- }
- int remainingSizeInChunk = this.currentChunk.Length - this.currentChunkSize;
- if (size > remainingSizeInChunk)
- {
- if (remainingSizeInChunk > 0)
- {
- if (buffer != null)
- {
- Buffer.BlockCopy(buffer, offset, this.currentChunk, this.currentChunkSize, remainingSizeInChunk);
- }
- this.currentChunkSize = this.currentChunk.Length;
- offset += remainingSizeInChunk;
- size -= remainingSizeInChunk;
- }
- AllocNextChunk(size);
- }
- if (buffer != null)
- {
- Buffer.BlockCopy(buffer, offset, this.currentChunk, this.currentChunkSize, size);
- }
- this.totalSize = newTotalSize;
- this.currentChunkSize += size;
- }
- public override void WriteByte(byte value)
- {
- Fx.Assert(this.initialized, "Cannot write to uninitialized stream");
- Fx.Assert(!this.bufferReturned, "Cannot write to stream once ToArray has been called.");
- if (this.totalSize == this.maxSize)
- {
- throw Fx.Exception.AsError(CreateQuotaExceededException(this.maxSize));
- }
- if (this.currentChunkSize == this.currentChunk.Length)
- {
- AllocNextChunk(1);
- }
- this.currentChunk[this.currentChunkSize++] = value;
- }
- }
- }
|