This commit is contained in:
2026-06-15 18:18:16 +08:00
parent 97c9fba14e
commit 2b9f134e5f
4164 changed files with 386922 additions and 79 deletions

View File

@@ -0,0 +1,488 @@
//
// System.IO.MemoryStream.cs
//
// Authors: Marcin Szczepanski (marcins@zipworld.com.au)
// Patrik Torstensson
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
//
// (c) 2001,2002 Marcin Szczepanski, Patrik Torstensson
// (c) 2003 Ximian, Inc. (http://www.ximian.com)
// Copyright (C) 2004 Novell (http://www.novell.com)
//
//
// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.IO;
using Best.HTTP.Shared.Logger;
using Best.HTTP.Shared.PlatformSupport.Memory;
namespace Best.HTTP.Shared.Streams
{
/// <summary>
/// This is a modified MemoryStream class to use VariableSizedBufferPool
/// </summary>
public sealed class BufferPoolMemoryStream : System.IO.Stream
{
bool canWrite;
bool allowGetBuffer;
int capacity;
int length;
byte[] internalBuffer;
int initialIndex;
bool expandable;
bool streamClosed;
int position;
int dirty_bytes;
bool releaseInternalBuffer;
public BufferPoolMemoryStream() : this(0)
{
}
public BufferPoolMemoryStream(int capacity)
{
if (capacity < 0)
throw new ArgumentOutOfRangeException("capacity");
canWrite = true;
//internalBuffer = capacity > 0 ? BufferPool.Get(capacity, true) : BufferPool.NoData;
//this.capacity = internalBuffer.Length;
//
//expandable = true;
//allowGetBuffer = true;
var buffer = capacity > 0 ? BufferPool.Get(capacity, true) : BufferPool.NoData;
InternalConstructor(buffer, 0, buffer.Length, true, true, true, true);
}
public BufferPoolMemoryStream(byte[] buffer)
{
if (buffer == null)
throw new ArgumentNullException("buffer");
InternalConstructor(buffer, 0, buffer.Length, true, false, true, false);
}
public BufferPoolMemoryStream(byte[] buffer, bool writable)
{
if (buffer == null)
throw new ArgumentNullException("buffer");
InternalConstructor(buffer, 0, buffer.Length, writable, false, true, false);
}
public BufferPoolMemoryStream(byte[] buffer, int index, int count)
{
InternalConstructor(buffer, index, count, true, false, true, false);
}
public BufferPoolMemoryStream(byte[] buffer, int index, int count, bool writable)
{
InternalConstructor(buffer, index, count, writable, false, true, false);
}
public BufferPoolMemoryStream(byte[] buffer, int index, int count, bool writable, bool publiclyVisible)
{
InternalConstructor(buffer, index, count, writable, publiclyVisible, true, false);
}
public BufferPoolMemoryStream(byte[] buffer, int index, int count, bool writable, bool publiclyVisible, bool releaseBuffer)
{
InternalConstructor(buffer, index, count, writable, publiclyVisible, releaseBuffer, false);
}
public BufferPoolMemoryStream(byte[] buffer, int index, int count, bool writable, bool publiclyVisible, bool releaseBuffer, bool canExpand)
{
InternalConstructor(buffer, index, count, writable, publiclyVisible, releaseBuffer, canExpand);
}
void InternalConstructor(byte[] buffer, int index, int count, bool writable, bool publicallyVisible, bool releaseBuffer, bool canExpand)
{
if (buffer == null)
throw new ArgumentNullException("buffer");
if (index < 0 || count < 0)
throw new ArgumentOutOfRangeException("index or count is less than 0.");
if (buffer.Length - index < count)
throw new ArgumentException("index+count",
"The size of the buffer is less than index + count.");
canWrite = writable;
internalBuffer = buffer;
capacity = count + index;
//length = capacity;
length = 0;
position = index;
initialIndex = index;
allowGetBuffer = publicallyVisible;
releaseInternalBuffer = releaseBuffer;
expandable = canExpand;
}
void CheckIfClosedThrowDisposed()
{
if (streamClosed)
throw new ObjectDisposedException("MemoryStream");
}
public override bool CanRead
{
get { return !streamClosed; }
}
public override bool CanSeek
{
get { return !streamClosed; }
}
public override bool CanWrite
{
get { return (!streamClosed && canWrite); }
}
public int Capacity
{
get
{
CheckIfClosedThrowDisposed();
return capacity - initialIndex;
}
set
{
CheckIfClosedThrowDisposed();
if (value == capacity)
return; // LAMENESS: see MemoryStreamTest.ConstructorFive
if (!expandable)
throw new NotSupportedException("Cannot expand this MemoryStream");
if (value < 0 || value < length)
throw new ArgumentOutOfRangeException("value",
"New capacity cannot be negative or less than the current capacity " + value + " " + capacity);
byte[] newBuffer = null;
if (value != 0)
{
newBuffer = BufferPool.Get(value, true);
Buffer.BlockCopy(internalBuffer, 0, newBuffer, 0, length);
}
dirty_bytes = 0; // discard any dirty area beyond previous length
BufferPool.Release(internalBuffer);
internalBuffer = newBuffer; // It's null when capacity is set to 0
capacity = internalBuffer != null ? internalBuffer.Length : 0;
}
}
public override long Length
{
get
{
// LAMESPEC: The spec says to throw an IOException if the
// stream is closed and an ObjectDisposedException if
// "methods were called after the stream was closed". What
// is the difference?
CheckIfClosedThrowDisposed();
// This is ok for MemoryStreamTest.ConstructorFive
return length - initialIndex;
}
}
public override long Position
{
get
{
CheckIfClosedThrowDisposed();
return position - initialIndex;
}
set
{
CheckIfClosedThrowDisposed();
if (value < 0)
throw new ArgumentOutOfRangeException("value",
"Position cannot be negative");
if (value > Int32.MaxValue)
throw new ArgumentOutOfRangeException("value",
"Position must be non-negative and less than 2^31 - 1 - origin");
position = initialIndex + (int)value;
}
}
protected override void Dispose (bool disposing)
{
streamClosed = true;
expandable = false;
if (disposing && internalBuffer != null && this.releaseInternalBuffer)
BufferPool.Release(internalBuffer);
internalBuffer = null;
}
public override void Flush()
{
// Do nothing
}
public byte[] GetBuffer()
{
if (!allowGetBuffer)
throw new UnauthorizedAccessException();
return internalBuffer;
}
public override int Read(byte[] buffer, int offset, int count)
{
CheckIfClosedThrowDisposed();
if (buffer == null)
throw new ArgumentNullException("buffer");
if (offset < 0 || count < 0)
throw new ArgumentOutOfRangeException("offset or count less than zero.");
if (buffer.Length - offset < count)
throw new ArgumentException("offset+count",
"The size of the buffer is less than offset + count.");
if (position >= length || count == 0)
return 0;
if (position > length - count)
count = length - position;
Buffer.BlockCopy(internalBuffer, position, buffer, offset, count);
position += count;
return count;
}
public override int ReadByte()
{
CheckIfClosedThrowDisposed();
if (position >= length)
return -1;
return internalBuffer[position++];
}
public override long Seek(long offset, SeekOrigin loc)
{
CheckIfClosedThrowDisposed();
// It's funny that they don't throw this exception for < Int32.MinValue
if (offset > (long)Int32.MaxValue)
throw new ArgumentOutOfRangeException("Offset out of range. " + offset);
int refPoint;
switch (loc)
{
case SeekOrigin.Begin:
if (offset < 0)
throw new IOException("Attempted to seek before start of MemoryStream.");
refPoint = initialIndex;
break;
case SeekOrigin.Current:
refPoint = position;
break;
case SeekOrigin.End:
refPoint = length;
break;
default:
throw new ArgumentException("loc", "Invalid SeekOrigin");
}
// LAMESPEC: My goodness, how may LAMESPECs are there in this
// class! :) In the spec for the Position property it's stated
// "The position must not be more than one byte beyond the end of the stream."
// In the spec for seek it says "Seeking to any location beyond the length of the
// stream is supported." That's a contradiction i'd say.
// I guess seek can go anywhere but if you use position it may get moved back.
refPoint += (int)offset;
if (refPoint < initialIndex)
throw new IOException("Attempted to seek before start of MemoryStream.");
position = refPoint;
return position;
}
int CalculateNewCapacity(int minimum)
{
if (minimum < 256)
minimum = 256; // See GetBufferTwo test
if (minimum < capacity * 2)
minimum = capacity * 2;
if (!UnityEngine.Mathf.IsPowerOfTwo(minimum))
minimum = UnityEngine.Mathf.NextPowerOfTwo(minimum);
return minimum;
}
void Expand(int newSize)
{
// We don't need to take into account the dirty bytes when incrementing the
// Capacity, as changing it will only preserve the valid clear region.
if (newSize > capacity)
Capacity = CalculateNewCapacity(newSize);
else if (dirty_bytes > 0)
{
Array.Clear(internalBuffer, length, dirty_bytes);
dirty_bytes = 0;
}
}
public override void SetLength(long value)
{
if (!expandable && value > capacity)
throw new NotSupportedException("Expanding this MemoryStream is not supported");
CheckIfClosedThrowDisposed();
if (!canWrite)
{
throw new NotSupportedException("Cannot write to this MemoryStream");
}
// LAMESPEC: AGAIN! It says to throw this exception if value is
// greater than "the maximum length of the MemoryStream". I haven't
// seen anywhere mention what the maximum length of a MemoryStream is and
// since we're this far this memory stream is expandable.
if (value < 0 || (value + initialIndex) > (long)Int32.MaxValue)
throw new ArgumentOutOfRangeException();
int newSize = (int)value + initialIndex;
if (newSize > length)
Expand(newSize);
else if (newSize < length) // Postpone the call to Array.Clear till expand time
dirty_bytes += length - newSize;
length = newSize;
if (position > length)
position = length;
}
public byte[] ToArray()
{
return ToArray(false, null);
}
public byte[] ToArray(bool canBeLarger, LoggingContext context)
{
int l = length - initialIndex;
byte[] outBuffer = null;
if (l > 0)
{
if (canBeLarger)
outBuffer = BufferPool.Get(l, true, context);
else
outBuffer = new byte[l];
}
else
{
outBuffer = BufferPool.NoData;
}
if (internalBuffer != null)
Buffer.BlockCopy(internalBuffer, initialIndex, outBuffer, 0, l);
return outBuffer;
}
public BufferSegment ToBufferSegment()
{
int l = length - initialIndex;
byte[] outBuffer = l > 0 ? BufferPool.Get(l, true) : BufferPool.NoData;
if (internalBuffer != null)
Buffer.BlockCopy(internalBuffer, initialIndex, outBuffer, 0, l);
return new BufferSegment(outBuffer, 0, l);
}
public override void Write(byte[] buffer, int offset, int count)
{
CheckIfClosedThrowDisposed();
if (!canWrite)
throw new NotSupportedException("Cannot write to this stream.");
if (buffer == null)
throw new ArgumentNullException("buffer");
if (offset < 0 || count < 0)
throw new ArgumentOutOfRangeException();
if (buffer.Length - offset < count)
throw new ArgumentException("offset+count",
"The size of the buffer is less than offset + count.");
// reordered to avoid possible integer overflow
if (position > length - count)
Expand(position + count);
Buffer.BlockCopy(buffer, offset, internalBuffer, position, count);
position += count;
if (position >= length)
length = position;
}
public override void WriteByte(byte value)
{
CheckIfClosedThrowDisposed();
if (!canWrite)
throw new NotSupportedException("Cannot write to this stream.");
if (position >= length)
{
Expand(position + 1);
length = position + 1;
}
internalBuffer[position++] = value;
}
public void WriteTo(Stream stream)
{
CheckIfClosedThrowDisposed();
if (stream == null)
throw new ArgumentNullException("stream");
stream.Write(internalBuffer, initialIndex, length - initialIndex);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9de7df31ed0f2ac4e9d5b3cd4e4230b4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using Best.HTTP.Shared.PlatformSupport.Memory;
namespace Best.HTTP.Shared.Streams
{
public class BufferSegmentStream : Stream
{
public override bool CanRead { get { return true; } }
public override bool CanSeek { get { return false; } }
public override bool CanWrite { get { return false; } }
public override long Length { get { return this._length; } }
protected long _length;
public override long Position { get { return 0; } set { } }
protected List<BufferSegment> bufferList = new List<BufferSegment>();
private byte[] _tempByteArray = new byte[1];
public override int ReadByte()
{
if (Read(this._tempByteArray, 0, 1) == 0)
return -1;
return this._tempByteArray[0];
}
public override int Read(byte[] buffer, int offset, int count)
{
int sumReadCount = 0;
while (count > 0 && bufferList.Count > 0)
{
BufferSegment buff = this.bufferList[0];
int readCount = Math.Min(count, buff.Count);
Array.Copy(buff.Data, buff.Offset, buffer, offset, readCount);
sumReadCount += readCount;
offset += readCount;
count -= readCount;
this.bufferList[0] = buff = buff.Slice(buff.Offset + readCount);
if (buff.Count == 0)
{
this.bufferList.RemoveAt(0);
BufferPool.Release(buff.Data);
}
}
Interlocked.Add(ref this._length, -sumReadCount);
return sumReadCount;
}
public override void Write(byte[] buffer, int offset, int count) => Write(new BufferSegment(buffer, offset, count));
public virtual void Write(BufferSegment bufferSegment)
{
if (bufferSegment.Count == 0)
return;
this.bufferList.Add(bufferSegment);
Interlocked.Add(ref this._length, bufferSegment.Count);
}
public virtual void Reset()
{
BufferPool.ReleaseBulk(this.bufferList);
this.bufferList.Clear();
Interlocked.Exchange(ref this._length, 0);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
Reset();
}
public override void Flush() { }
public override long Seek(long offset, SeekOrigin origin) => throw new NotImplementedException();
public override void SetLength(long value) => throw new NotImplementedException();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2d11dedd4dd2ea541a75567de5234f0c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,50 @@
using Best.HTTP.Shared.PlatformSupport.Network.Tcp;
namespace Best.HTTP.Shared.Streams
{
/// <summary>
/// A PeekableStream implementation that also implements the <see cref="IPeekableContentProvider"/> interface too.
/// </summary>
public abstract class PeekableContentProviderStream : PeekableStream, IPeekableContentProvider
{
public PeekableContentProviderStream Peekable => this;
public IContentConsumer Consumer { get; private set; }
public void SetTwoWayBinding(IContentConsumer consumer)
{
this.Consumer = consumer;
this.Consumer?.SetBinding(this);
}
/// <summary>
/// This will set Consumer to null.
/// </summary>
public void Unbind()
{
this.Consumer?.UnsetBinding();
this.Consumer = null;
}
/// <summary>
/// Set Consumer to null if the current one is the one passed in the parameter.
/// </summary>
public void UnbindIf(IContentConsumer consumer)
{
if (consumer == null || consumer == this.Consumer)
{
this.Consumer?.UnsetBinding();
this.Consumer = null;
}
}
public void SwitchIf(IContentConsumer from, IContentConsumer to)
{
if (from == null || from == this.Consumer)
{
this.Consumer?.UnsetBinding();
SetTwoWayBinding(to);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5f46be5dbbe57d1448b1a98145826359
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,32 @@
namespace Best.HTTP.Shared.Streams
{
public sealed class PeekableIncomingSegmentStream : PeekableStream
{
private int peek_listIdx;
private int peek_pos;
public override void BeginPeek()
{
peek_listIdx = 0;
peek_pos = base.bufferList.Count > 0 ? base.bufferList[0].Offset : 0;
}
public override int PeekByte()
{
if (base.bufferList.Count == 0)
return -1;
var segment = base.bufferList[this.peek_listIdx];
if (peek_pos >= segment.Offset + segment.Count)
{
if (base.bufferList.Count <= this.peek_listIdx + 1)
return -1;
segment = base.bufferList[++this.peek_listIdx];
this.peek_pos = segment.Offset;
}
return segment.Data[this.peek_pos++];
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 53712de99c0f6f749875bbb192baea22
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
namespace Best.HTTP.Shared.Streams
{
public abstract class PeekableStream : BufferSegmentStream
{
public abstract void BeginPeek();
public abstract int PeekByte();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 59512fef2c2575e4f98a99b053e5b8d9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,144 @@
using Best.HTTP.Shared.PlatformSupport.Memory;
using System;
using System.IO;
namespace Best.HTTP.Shared.Streams
{
public sealed class ReadOnlyBufferedStream : Stream
{
Stream stream;
public const int READBUFFER = 8192;
byte[] buf;
int available = 0;
int pos = 0;
public ReadOnlyBufferedStream(Stream nstream)
:this(nstream, READBUFFER)
{
}
public ReadOnlyBufferedStream(Stream nstream, int bufferSize)
{
stream = nstream;
buf = BufferPool.Get(bufferSize, true);
}
public override int Read(byte[] buffer, int offset, int size)
{
if (available > 0)
{
// copy & return
int copyCount = Math.Min(available, size);
Array.Copy(buf, pos, buffer, offset, copyCount);
pos += copyCount;
available -= copyCount;
return copyCount;
}
else
{
if (size >= buf.Length)
{
// read directly to buffer
return stream.Read(buffer, offset, size);
}
else
{
// read to buf and copy
pos = 0;
available = stream.Read(buf, 0, buf.Length);
if (available > 0)
return Read(buffer, offset, size);
else
return 0;
}
}
}
public override int ReadByte()
{
if (available > 0)
{
available -= 1;
pos += 1;
return buf[pos - 1];
}
else
{
try
{
available = stream.Read(buf, 0, buf.Length);
pos = 0;
}
catch
{
return -1;
}
if (available < 1)
{
return -1;
}
else
{
available -= 1;
pos += 1;
return buf[pos - 1];
}
}
}
protected override void Dispose(bool disposing)
{
if (disposing && buf != null)
BufferPool.Release(buf);
buf = null;
}
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { throw new NotImplementedException(); }
}
public override bool CanWrite
{
get { throw new NotImplementedException(); }
}
public override long Length
{
get { throw new NotImplementedException(); }
}
public override long Position
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public override void Flush()
{
throw new NotImplementedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9e8c252d73a95304c9b4639e86366b53
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,165 @@
using Best.HTTP.Shared.Extensions;
using Best.HTTP.Shared.PlatformSupport.Memory;
using System;
namespace Best.HTTP.Shared.Streams
{
/// <summary>
/// Wrapper of multiple streams. Writes and reads are both supported. Read goes trough all the streams.
/// </summary>
public sealed class StreamList : System.IO.Stream
{
private System.IO.Stream[] Streams;
private int CurrentIdx;
public StreamList(params System.IO.Stream[] streams)
{
this.Streams = streams;
this.CurrentIdx = 0;
}
public void AppendStream(System.IO.Stream stream)
{
Array.Resize(ref this.Streams, this.Streams.Length + 1);
this.Streams[this.Streams.Length - 1] = stream;
}
public override bool CanRead
{
get
{
if (CurrentIdx >= Streams.Length)
return false;
return Streams[CurrentIdx].CanRead;
}
}
public override bool CanSeek { get { return false; } }
public override bool CanWrite
{
get
{
if (CurrentIdx >= Streams.Length)
return false;
return Streams[CurrentIdx].CanWrite;
}
}
public override void Flush()
{
if (CurrentIdx >= Streams.Length)
return;
// We have to call the flush to all previous streams, as we may advanced the CurrentIdx
for (int i = 0; i <= CurrentIdx; ++i)
Streams[i].Flush();
}
public override long Length
{
get
{
if (CurrentIdx >= Streams.Length)
return 0;
long length = 0;
for (int i = 0; i < Streams.Length; ++i)
length += Streams[i].Length;
return length;
}
}
public override int Read(byte[] buffer, int offset, int count)
{
if (CurrentIdx >= Streams.Length)
return -1;
int readCount = Streams[CurrentIdx].Read(buffer, offset, count);
while (readCount < count && ++CurrentIdx < Streams.Length)
{
// Dispose previous stream
try
{
Streams[CurrentIdx - 1].Dispose();
Streams[CurrentIdx - 1] = null;
}
catch (Exception ex)
{
HTTPManager.Logger.Exception("StreamList", "Dispose", ex);
}
readCount += Streams[CurrentIdx].Read(buffer, offset + readCount, count - readCount);
}
return readCount;
}
public override void Write(byte[] buffer, int offset, int count)
{
if (CurrentIdx >= Streams.Length)
return;
Streams[CurrentIdx].Write(buffer, offset, count);
}
public void Write(string str)
{
var buffer = str.GetASCIIBytes();
try
{
this.Write(buffer.Data, buffer.Offset, buffer.Count);
}
finally
{
BufferPool.Release(buffer);
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
for (int i = 0; i < Streams.Length; ++i)
if (Streams[i] != null)
{
try
{
Streams[i].Dispose();
}
catch (Exception ex)
{
HTTPManager.Logger.Exception("StreamList", "Dispose", ex);
}
}
}
}
public override long Position
{
get
{
throw new NotImplementedException("Position get");
}
set
{
throw new NotImplementedException("Position set");
}
}
public override long Seek(long offset, System.IO.SeekOrigin origin)
{
if (CurrentIdx >= Streams.Length)
return 0;
return Streams[CurrentIdx].Seek(offset, origin);
}
public override void SetLength(long value)
{
throw new NotImplementedException("SetLength");
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 305ba47e83a516c45bd29164da055121
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,110 @@
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
{
/// <summary>
/// A custom buffer stream implementation that will not close the underlying stream.
/// </summary>
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;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8feaa035f81583347b7b28d29bbea34e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: