using System; using System.IO; using Best.HTTP.Shared.Extensions; using Best.HTTP.Shared.Logger; using Best.HTTP.Shared.PlatformSupport.Memory; namespace Best.HTTP.Shared.Streams { /// /// A custom buffer stream implementation that will not close the underlying stream. /// public sealed class WriteOnlyBufferedStream : Stream { 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.buffer.Length; } } public override long Position { get { return this._position; } set { throw new NotImplementedException("Position set"); } } private int _position; private byte[] buffer; private int _bufferSize; private Stream stream; private LoggingContext _context; public WriteOnlyBufferedStream(Stream stream, int bufferSize, LoggingContext context) { if (stream == null) throw new NullReferenceException(nameof(stream)); this.stream = stream; this._context = context; this._bufferSize = bufferSize; this.buffer = BufferPool.Get(this._bufferSize, true, context); this._position = 0; } public override void Flush() { if (this._position > 0) { #if !UNITY_WEBGL || UNITY_EDITOR // if the underlying stream is an ITCPStreamerContentConsumer, we can use an optimized path and avoid copying // the buffered bytes. var tcpStreamer = this.stream as Shared.PlatformSupport.Network.Tcp.ITCPStreamerContentConsumer; if (tcpStreamer != null) { // First swap the buffers because tcpStreamer.Write might cause an exception and both the streamer // and WriteOnlyBufferedStream would release the same buffer var buff = this.buffer.AsBuffer(this._position); this.buffer = BufferPool.Get(this._bufferSize, true, this._context); tcpStreamer.Write(buff); } else #endif { this.stream.Write(this.buffer, 0, this._position); this.stream.Flush(); } //if (HTTPManager.Logger.IsDiagnostic) // HTTPManager.Logger.Information("WriteOnlyBufferedStream", string.Format("Flushed {0:N0} bytes", this._position)); this._position = 0; } } public override void Write(byte[] bufferFrom, int offset, int count) { while (count > 0) { int writeCount = Math.Min(count, this.buffer.Length - this._position); Array.Copy(bufferFrom, offset, this.buffer, this._position, writeCount); this._position += writeCount; offset += writeCount; count -= writeCount; if (this._position == this.buffer.Length) this.Flush(); } } public override int Read(byte[] buffer, int offset, int count) { return 0; } public override long Seek(long offset, SeekOrigin origin) { return 0; } public override void SetLength(long value) { } protected override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing && this.buffer != null) BufferPool.Release(this.buffer); this.buffer = null; } } }