Adds MinimumReadThreshold to StreamPipeReader. (#4372)
This commit is contained in:
parent
12966c63a6
commit
226f2c0c2c
|
|
@ -20,6 +20,7 @@ namespace Microsoft.AspNetCore.Http
|
||||||
public class StreamPipeReader : PipeReader
|
public class StreamPipeReader : PipeReader
|
||||||
{
|
{
|
||||||
private readonly int _minimumSegmentSize;
|
private readonly int _minimumSegmentSize;
|
||||||
|
private readonly int _minimumReadThreshold;
|
||||||
private readonly Stream _readingStream;
|
private readonly Stream _readingStream;
|
||||||
private readonly MemoryPool<byte> _pool;
|
private readonly MemoryPool<byte> _pool;
|
||||||
|
|
||||||
|
|
@ -35,11 +36,51 @@ namespace Microsoft.AspNetCore.Http
|
||||||
private bool _examinedEverything;
|
private bool _examinedEverything;
|
||||||
private object _lock = new object();
|
private object _lock = new object();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new StreamPipeReader.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="readingStream">The stream to read from.</param>
|
||||||
|
public StreamPipeReader(Stream readingStream)
|
||||||
|
: this(readingStream, StreamPipeReaderOptions.DefaultOptions)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new StreamPipeReader.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="readingStream">The stream to read from.</param>
|
||||||
|
/// <param name="options">The options to use.</param>
|
||||||
|
public StreamPipeReader(Stream readingStream, StreamPipeReaderOptions options)
|
||||||
|
{
|
||||||
|
_readingStream = readingStream ?? throw new ArgumentNullException(nameof(readingStream));
|
||||||
|
|
||||||
|
if (options == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.MinimumReadThreshold <= 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(options.MinimumReadThreshold));
|
||||||
|
}
|
||||||
|
|
||||||
|
_minimumSegmentSize = options.MinimumSegmentSize;
|
||||||
|
_minimumReadThreshold = Math.Min(options.MinimumReadThreshold, options.MinimumSegmentSize);
|
||||||
|
_pool = options.MemoryPool;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void AdvanceTo(SequencePosition consumed)
|
||||||
|
{
|
||||||
|
AdvanceTo(consumed, consumed);
|
||||||
|
}
|
||||||
|
|
||||||
private CancellationTokenSource InternalTokenSource
|
private CancellationTokenSource InternalTokenSource
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
lock(_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
if (_internalTokenSource == null)
|
if (_internalTokenSource == null)
|
||||||
{
|
{
|
||||||
|
|
@ -52,34 +93,6 @@ namespace Microsoft.AspNetCore.Http
|
||||||
{
|
{
|
||||||
_internalTokenSource = value;
|
_internalTokenSource = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new StreamPipeReader.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="readingStream">The stream to read from.</param>
|
|
||||||
public StreamPipeReader(Stream readingStream) : this(readingStream, minimumSegmentSize: 4096)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new StreamPipeReader.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="readingStream">The stream to read from.</param>
|
|
||||||
/// <param name="minimumSegmentSize">The minimum segment size to return from ReadAsync.</param>
|
|
||||||
/// <param name="pool"></param>
|
|
||||||
public StreamPipeReader(Stream readingStream, int minimumSegmentSize, MemoryPool<byte> pool = null)
|
|
||||||
{
|
|
||||||
_minimumSegmentSize = minimumSegmentSize;
|
|
||||||
_readingStream = readingStream;
|
|
||||||
_pool = pool ?? MemoryPool<byte>.Shared;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void AdvanceTo(SequencePosition consumed)
|
|
||||||
{
|
|
||||||
AdvanceTo(consumed, consumed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -309,7 +322,7 @@ namespace Microsoft.AspNetCore.Http
|
||||||
_readHead.SetMemory(_pool.Rent(GetSegmentSize()));
|
_readHead.SetMemory(_pool.Rent(GetSegmentSize()));
|
||||||
_readTail = _readHead;
|
_readTail = _readHead;
|
||||||
}
|
}
|
||||||
else if (_readTail.WritableBytes == 0)
|
else if (_readTail.WritableBytes < _minimumReadThreshold)
|
||||||
{
|
{
|
||||||
CreateNewTailSegment();
|
CreateNewTailSegment();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Http
|
||||||
|
{
|
||||||
|
public class StreamPipeReaderOptions
|
||||||
|
{
|
||||||
|
public static StreamPipeReaderOptions DefaultOptions = new StreamPipeReaderOptions();
|
||||||
|
public const int DefaultMinimumSegmentSize = 4096;
|
||||||
|
public const int DefaultMinimumReadThreshold = 256;
|
||||||
|
|
||||||
|
public StreamPipeReaderOptions()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public StreamPipeReaderOptions(int minimumSegmentSize, int minimumReadThreshold, MemoryPool<byte> memoryPool)
|
||||||
|
{
|
||||||
|
MinimumSegmentSize = minimumSegmentSize;
|
||||||
|
MinimumReadThreshold = minimumReadThreshold;
|
||||||
|
MemoryPool = memoryPool;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int MinimumSegmentSize { get; set; } = DefaultMinimumSegmentSize;
|
||||||
|
|
||||||
|
public int MinimumReadThreshold { get; set; } = DefaultMinimumReadThreshold;
|
||||||
|
|
||||||
|
public MemoryPool<byte> MemoryPool { get; set; } = MemoryPool<byte>.Shared;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Http.Tests
|
||||||
{
|
{
|
||||||
MemoryStream = new MemoryStream();
|
MemoryStream = new MemoryStream();
|
||||||
Writer = new StreamPipeWriter(MemoryStream, MinimumSegmentSize, new TestMemoryPool());
|
Writer = new StreamPipeWriter(MemoryStream, MinimumSegmentSize, new TestMemoryPool());
|
||||||
Reader = new StreamPipeReader(MemoryStream, MinimumSegmentSize, new TestMemoryPool());
|
Reader = new StreamPipeReader(MemoryStream, new StreamPipeReaderOptions(MinimumSegmentSize, minimumReadThreshold: 256, new TestMemoryPool()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
@ -49,6 +49,13 @@ namespace Microsoft.AspNetCore.Http.Tests
|
||||||
MemoryStream.Write(data, 0, data.Length);
|
MemoryStream.Write(data, 0, data.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Append(byte[] data)
|
||||||
|
{
|
||||||
|
var originalPosition = MemoryStream.Position;
|
||||||
|
MemoryStream.Write(data, 0, data.Length);
|
||||||
|
MemoryStream.Position = originalPosition;
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] ReadWithoutFlush()
|
public byte[] ReadWithoutFlush()
|
||||||
{
|
{
|
||||||
MemoryStream.Position = 0;
|
MemoryStream.Position = 0;
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ namespace Microsoft.AspNetCore.Http.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ReadWithAdvance()
|
public async Task ReadWithAdvance()
|
||||||
{
|
{
|
||||||
Write(new byte[10000]);
|
WriteByteArray(9000);
|
||||||
|
|
||||||
var readResult = await Reader.ReadAsync();
|
var readResult = await Reader.ReadAsync();
|
||||||
Reader.AdvanceTo(readResult.Buffer.End);
|
Reader.AdvanceTo(readResult.Buffer.End);
|
||||||
|
|
@ -71,8 +71,9 @@ namespace Microsoft.AspNetCore.Http.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ReadWithAdvanceDifferentSegmentSize()
|
public async Task ReadWithAdvanceDifferentSegmentSize()
|
||||||
{
|
{
|
||||||
Reader = new StreamPipeReader(MemoryStream, 4095, new TestMemoryPool());
|
CreateReader(minimumSegmentSize: 4095);
|
||||||
Write(new byte[10000]);
|
|
||||||
|
WriteByteArray(9000);
|
||||||
|
|
||||||
var readResult = await Reader.ReadAsync();
|
var readResult = await Reader.ReadAsync();
|
||||||
Reader.AdvanceTo(readResult.Buffer.End);
|
Reader.AdvanceTo(readResult.Buffer.End);
|
||||||
|
|
@ -85,8 +86,9 @@ namespace Microsoft.AspNetCore.Http.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ReadWithAdvanceSmallSegments()
|
public async Task ReadWithAdvanceSmallSegments()
|
||||||
{
|
{
|
||||||
Reader = new StreamPipeReader(MemoryStream, 16, new TestMemoryPool());
|
CreateReader();
|
||||||
Write(new byte[128]);
|
|
||||||
|
WriteByteArray(128);
|
||||||
|
|
||||||
var readResult = await Reader.ReadAsync();
|
var readResult = await Reader.ReadAsync();
|
||||||
Reader.AdvanceTo(readResult.Buffer.End);
|
Reader.AdvanceTo(readResult.Buffer.End);
|
||||||
|
|
@ -251,8 +253,9 @@ namespace Microsoft.AspNetCore.Http.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task AdvanceMultipleSegments()
|
public async Task AdvanceMultipleSegments()
|
||||||
{
|
{
|
||||||
Reader = new StreamPipeReader(MemoryStream, 16, new TestMemoryPool());
|
CreateReader();
|
||||||
Write(new byte[128]);
|
|
||||||
|
WriteByteArray(128);
|
||||||
|
|
||||||
var result = await Reader.ReadAsync();
|
var result = await Reader.ReadAsync();
|
||||||
Assert.Equal(16, result.Buffer.Length);
|
Assert.Equal(16, result.Buffer.Length);
|
||||||
|
|
@ -269,8 +272,9 @@ namespace Microsoft.AspNetCore.Http.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task AdvanceMultipleSegmentsEdgeCase()
|
public async Task AdvanceMultipleSegmentsEdgeCase()
|
||||||
{
|
{
|
||||||
Reader = new StreamPipeReader(MemoryStream, 16, new TestMemoryPool());
|
CreateReader();
|
||||||
Write(new byte[128]);
|
|
||||||
|
WriteByteArray(128);
|
||||||
|
|
||||||
var result = await Reader.ReadAsync();
|
var result = await Reader.ReadAsync();
|
||||||
Reader.AdvanceTo(result.Buffer.Start, result.Buffer.End);
|
Reader.AdvanceTo(result.Buffer.Start, result.Buffer.End);
|
||||||
|
|
@ -288,7 +292,7 @@ namespace Microsoft.AspNetCore.Http.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CompleteReaderWithoutAdvanceDoesNotThrow()
|
public async Task CompleteReaderWithoutAdvanceDoesNotThrow()
|
||||||
{
|
{
|
||||||
Write(new byte[100]);
|
WriteByteArray(100);
|
||||||
await Reader.ReadAsync();
|
await Reader.ReadAsync();
|
||||||
Reader.Complete();
|
Reader.Complete();
|
||||||
}
|
}
|
||||||
|
|
@ -296,7 +300,7 @@ namespace Microsoft.AspNetCore.Http.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task AdvanceAfterCompleteThrows()
|
public async Task AdvanceAfterCompleteThrows()
|
||||||
{
|
{
|
||||||
Write(new byte[100]);
|
WriteByteArray(100);
|
||||||
var buffer = (await Reader.ReadAsync()).Buffer;
|
var buffer = (await Reader.ReadAsync()).Buffer;
|
||||||
|
|
||||||
Reader.Complete();
|
Reader.Complete();
|
||||||
|
|
@ -309,7 +313,7 @@ namespace Microsoft.AspNetCore.Http.Tests
|
||||||
public async Task ReadBetweenBlocks()
|
public async Task ReadBetweenBlocks()
|
||||||
{
|
{
|
||||||
var blockSize = 16;
|
var blockSize = 16;
|
||||||
Reader = new StreamPipeReader(MemoryStream, blockSize, new TestMemoryPool());
|
CreateReader();
|
||||||
|
|
||||||
WriteWithoutPosition(Enumerable.Repeat((byte)'a', blockSize - 5).ToArray());
|
WriteWithoutPosition(Enumerable.Repeat((byte)'a', blockSize - 5).ToArray());
|
||||||
Write(Encoding.ASCII.GetBytes("Hello World"));
|
Write(Encoding.ASCII.GetBytes("Hello World"));
|
||||||
|
|
@ -364,7 +368,7 @@ namespace Microsoft.AspNetCore.Http.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ReadAsyncWithDataReadyReturnsTaskWithValue()
|
public void ReadAsyncWithDataReadyReturnsTaskWithValue()
|
||||||
{
|
{
|
||||||
Write(new byte[20]);
|
WriteByteArray(20);
|
||||||
var task = Reader.ReadAsync();
|
var task = Reader.ReadAsync();
|
||||||
Assert.True(IsTaskWithResult(task));
|
Assert.True(IsTaskWithResult(task));
|
||||||
}
|
}
|
||||||
|
|
@ -380,8 +384,9 @@ namespace Microsoft.AspNetCore.Http.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task AdvancePastMinReadSizeReadAsyncReturnsMoreData()
|
public async Task AdvancePastMinReadSizeReadAsyncReturnsMoreData()
|
||||||
{
|
{
|
||||||
Reader = new StreamPipeReader(MemoryStream, 16, new TestMemoryPool());
|
CreateReader();
|
||||||
Write(new byte[32]);
|
|
||||||
|
WriteByteArray(32);
|
||||||
var result = await Reader.ReadAsync();
|
var result = await Reader.ReadAsync();
|
||||||
Assert.Equal(16, result.Buffer.Length);
|
Assert.Equal(16, result.Buffer.Length);
|
||||||
|
|
||||||
|
|
@ -393,7 +398,7 @@ namespace Microsoft.AspNetCore.Http.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ExamineEverythingResetsAfterSuccessfulRead()
|
public async Task ExamineEverythingResetsAfterSuccessfulRead()
|
||||||
{
|
{
|
||||||
Write(Encoding.ASCII.GetBytes(new string('a', 10000)));
|
WriteByteArray(10000);
|
||||||
|
|
||||||
var readResult = await Reader.ReadAsync();
|
var readResult = await Reader.ReadAsync();
|
||||||
Reader.AdvanceTo(readResult.Buffer.Start, readResult.Buffer.End);
|
Reader.AdvanceTo(readResult.Buffer.Start, readResult.Buffer.End);
|
||||||
|
|
@ -408,10 +413,10 @@ namespace Microsoft.AspNetCore.Http.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ReadMultipleTimesAdvanceFreesAppropriately()
|
public async Task ReadMultipleTimesAdvanceFreesAppropriately()
|
||||||
{
|
{
|
||||||
var blockSize = 16;
|
|
||||||
var pool = new TestMemoryPool();
|
var pool = new TestMemoryPool();
|
||||||
Reader = new StreamPipeReader(MemoryStream, blockSize, pool);
|
CreateReader(memoryPool: pool);
|
||||||
Write(Encoding.ASCII.GetBytes(new string('a', 10000)));
|
|
||||||
|
WriteByteArray(2000);
|
||||||
|
|
||||||
for (var i = 0; i < 99; i++)
|
for (var i = 0; i < 99; i++)
|
||||||
{
|
{
|
||||||
|
|
@ -428,8 +433,9 @@ namespace Microsoft.AspNetCore.Http.Tests
|
||||||
public async Task AsyncReadWorks()
|
public async Task AsyncReadWorks()
|
||||||
{
|
{
|
||||||
MemoryStream = new AsyncStream();
|
MemoryStream = new AsyncStream();
|
||||||
Reader = new StreamPipeReader(MemoryStream, 16, new TestMemoryPool());
|
CreateReader();
|
||||||
Write(Encoding.ASCII.GetBytes(new string('a', 10000)));
|
|
||||||
|
WriteByteArray(2000);
|
||||||
|
|
||||||
for (var i = 0; i < 99; i++)
|
for (var i = 0; i < 99; i++)
|
||||||
{
|
{
|
||||||
|
|
@ -445,7 +451,8 @@ namespace Microsoft.AspNetCore.Http.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ConsumePartialBufferWorks()
|
public async Task ConsumePartialBufferWorks()
|
||||||
{
|
{
|
||||||
Reader = new StreamPipeReader(MemoryStream, 16, new TestMemoryPool());
|
CreateReader();
|
||||||
|
|
||||||
Write(Encoding.ASCII.GetBytes(new string('a', 8)));
|
Write(Encoding.ASCII.GetBytes(new string('a', 8)));
|
||||||
var readResult = await Reader.ReadAsync();
|
var readResult = await Reader.ReadAsync();
|
||||||
Reader.AdvanceTo(readResult.Buffer.GetPosition(4), readResult.Buffer.End);
|
Reader.AdvanceTo(readResult.Buffer.GetPosition(4), readResult.Buffer.End);
|
||||||
|
|
@ -460,7 +467,8 @@ namespace Microsoft.AspNetCore.Http.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ConsumePartialBufferBetweenMultipleSegmentsWorks()
|
public async Task ConsumePartialBufferBetweenMultipleSegmentsWorks()
|
||||||
{
|
{
|
||||||
Reader = new StreamPipeReader(MemoryStream, 16, new TestMemoryPool());
|
CreateReader();
|
||||||
|
|
||||||
Write(Encoding.ASCII.GetBytes(new string('a', 8)));
|
Write(Encoding.ASCII.GetBytes(new string('a', 8)));
|
||||||
var readResult = await Reader.ReadAsync();
|
var readResult = await Reader.ReadAsync();
|
||||||
Reader.AdvanceTo(readResult.Buffer.GetPosition(4), readResult.Buffer.End);
|
Reader.AdvanceTo(readResult.Buffer.GetPosition(4), readResult.Buffer.End);
|
||||||
|
|
@ -477,11 +485,78 @@ namespace Microsoft.AspNetCore.Http.Tests
|
||||||
Reader.AdvanceTo(readResult.Buffer.End);
|
Reader.AdvanceTo(readResult.Buffer.End);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SetMinimumReadThresholdSegmentAdvancesCorrectly()
|
||||||
|
{
|
||||||
|
CreateReader(minimumReadThreshold: 8);
|
||||||
|
|
||||||
|
WriteByteArray(9);
|
||||||
|
var readResult = await Reader.ReadAsync();
|
||||||
|
Reader.AdvanceTo(readResult.Buffer.Start, readResult.Buffer.End);
|
||||||
|
|
||||||
|
AppendByteArray(9);
|
||||||
|
readResult = await Reader.ReadAsync();
|
||||||
|
|
||||||
|
foreach (var segment in readResult.Buffer)
|
||||||
|
{
|
||||||
|
Assert.Equal(9, segment.Length);
|
||||||
|
}
|
||||||
|
Assert.False(readResult.Buffer.IsSingleSegment);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SetMinimumReadThresholdToMiminumSegmentSizeOnlyGetNewBlockWhenDataIsWritten()
|
||||||
|
{
|
||||||
|
CreateReader(minimumReadThreshold: 16);
|
||||||
|
WriteByteArray(0);
|
||||||
|
|
||||||
|
var readResult = await Reader.ReadAsync();
|
||||||
|
Reader.AdvanceTo(readResult.Buffer.Start, readResult.Buffer.End);
|
||||||
|
|
||||||
|
WriteByteArray(16);
|
||||||
|
readResult = await Reader.ReadAsync();
|
||||||
|
|
||||||
|
Assert.Equal(16, readResult.Buffer.Length);
|
||||||
|
Assert.True(readResult.Buffer.IsSingleSegment);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetMinimumReadThresholdOfZeroThrows()
|
||||||
|
{
|
||||||
|
Assert.Throws<ArgumentOutOfRangeException>(() => new StreamPipeReader(MemoryStream,
|
||||||
|
new StreamPipeReaderOptions(minimumSegmentSize: 4096, minimumReadThreshold: 0, new TestMemoryPool())));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetOptionsToNullThrows()
|
||||||
|
{
|
||||||
|
Assert.Throws<ArgumentNullException>(() => new StreamPipeReader(MemoryStream, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateReader(int minimumSegmentSize = 16, int minimumReadThreshold = 4, MemoryPool<byte> memoryPool = null)
|
||||||
|
{
|
||||||
|
Reader = new StreamPipeReader(MemoryStream,
|
||||||
|
new StreamPipeReaderOptions(
|
||||||
|
minimumSegmentSize,
|
||||||
|
minimumReadThreshold,
|
||||||
|
memoryPool ?? new TestMemoryPool()));
|
||||||
|
}
|
||||||
|
|
||||||
private bool IsTaskWithResult<T>(ValueTask<T> task)
|
private bool IsTaskWithResult<T>(ValueTask<T> task)
|
||||||
{
|
{
|
||||||
return task == new ValueTask<T>(task.Result);
|
return task == new ValueTask<T>(task.Result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void WriteByteArray(int size)
|
||||||
|
{
|
||||||
|
Write(new byte[size]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AppendByteArray(int size)
|
||||||
|
{
|
||||||
|
Append(new byte[size]);
|
||||||
|
}
|
||||||
|
|
||||||
private class AsyncStream : MemoryStream
|
private class AsyncStream : MemoryStream
|
||||||
{
|
{
|
||||||
private static byte[] bytes = Encoding.ASCII.GetBytes("Hello World");
|
private static byte[] bytes = Encoding.ASCII.GetBytes("Hello World");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue