Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 42 additions & 42 deletions assemblySize.include.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,51 @@

| | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability |
|----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------|
| netstandard2.0 | 8.0KB | 347.5KB | +339.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB |
| netstandard2.1 | 8.5KB | 302.0KB | +293.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB |
| net461 | 8.5KB | 346.5KB | +338.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
| net462 | 7.0KB | 350.0KB | +343.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
| net47 | 7.0KB | 349.5KB | +342.5KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB |
| net471 | 8.5KB | 349.0KB | +340.5KB | +9.0KB | +6.0KB | +9.0KB | +13.5KB |
| net472 | 8.5KB | 347.5KB | +339.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
| net48 | 8.5KB | 347.5KB | +339.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
| net481 | 8.5KB | 347.5KB | +339.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
| netcoreapp2.0 | 9.0KB | 323.5KB | +314.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
| netcoreapp2.1 | 9.0KB | 304.5KB | +295.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
| netcoreapp2.2 | 9.0KB | 304.5KB | +295.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
| netcoreapp3.0 | 9.5KB | 297.0KB | +287.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB |
| netcoreapp3.1 | 9.5KB | 295.5KB | +286.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
| net5.0 | 9.5KB | 259.0KB | +249.5KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB |
| net6.0 | 10.0KB | 201.0KB | +191.0KB | +10.0KB | +6.5KB | +512bytes | +3.5KB |
| net7.0 | 10.0KB | 163.5KB | +153.5KB | +9.0KB | +5.5KB | +512bytes | +3.0KB |
| net8.0 | 9.5KB | 135.0KB | +125.5KB | +8.0KB | | +512bytes | +3.0KB |
| net9.0 | 9.5KB | 88.5KB | +79.0KB | +8.5KB | | +512bytes | +3.5KB |
| net10.0 | 10.0KB | 66.0KB | +56.0KB | +9.0KB | | +512bytes | +3.5KB |
| net11.0 | 10.0KB | 27.0KB | +17.0KB | +9.0KB | | +1.0KB | +3.5KB |
| netstandard2.0 | 8.0KB | 351.5KB | +343.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
| netstandard2.1 | 8.5KB | 306.0KB | +297.5KB | +8.5KB | +6.0KB | +9.0KB | +13.5KB |
| net461 | 8.5KB | 350.0KB | +341.5KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB |
| net462 | 7.0KB | 353.5KB | +346.5KB | +9.0KB | +6.5KB | +9.5KB | +13.5KB |
| net47 | 7.0KB | 353.5KB | +346.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
| net471 | 8.5KB | 352.5KB | +344.0KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB |
| net472 | 8.5KB | 351.5KB | +343.0KB | +9.0KB | +6.0KB | +9.0KB | +13.5KB |
| net48 | 8.5KB | 351.5KB | +343.0KB | +9.0KB | +6.0KB | +9.0KB | +13.5KB |
| net481 | 8.5KB | 351.5KB | +343.0KB | +9.0KB | +6.0KB | +9.0KB | +13.5KB |
| netcoreapp2.0 | 9.0KB | 327.5KB | +318.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
| netcoreapp2.1 | 9.0KB | 308.0KB | +299.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB |
| netcoreapp2.2 | 9.0KB | 308.0KB | +299.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB |
| netcoreapp3.0 | 9.5KB | 301.0KB | +291.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
| netcoreapp3.1 | 9.5KB | 299.0KB | +289.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB |
| net5.0 | 9.5KB | 263.0KB | +253.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB |
| net6.0 | 10.0KB | 205.0KB | +195.0KB | +9.5KB | +6.5KB | +512bytes | +3.0KB |
| net7.0 | 10.0KB | 167.0KB | +157.0KB | +9.5KB | +5.5KB | +1.0KB | +3.5KB |
| net8.0 | 9.5KB | 138.5KB | +129.0KB | +8.5KB | +512bytes | +512bytes | +3.5KB |
| net9.0 | 9.5KB | 92.5KB | +83.0KB | +8.5KB | | +512bytes | +3.5KB |
| net10.0 | 10.0KB | 70.0KB | +60.0KB | +9.0KB | | +512bytes | +3.5KB |
| net11.0 | 10.0KB | 31.5KB | +21.5KB | +9.0KB | | +512bytes | +3.5KB |


### Assembly Sizes with EmbedUntrackedSources

| | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability |
|----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------|
| netstandard2.0 | 8.0KB | 507.8KB | +499.8KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB |
| netstandard2.1 | 8.5KB | 436.6KB | +428.1KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB |
| net461 | 8.5KB | 507.8KB | +499.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
| net462 | 7.0KB | 511.3KB | +504.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
| net47 | 7.0KB | 510.6KB | +503.6KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB |
| net471 | 8.5KB | 509.7KB | +501.2KB | +16.7KB | +7.7KB | +13.9KB | +18.9KB |
| net472 | 8.5KB | 507.2KB | +498.7KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
| net48 | 8.5KB | 507.2KB | +498.7KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
| net481 | 8.5KB | 507.2KB | +498.7KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
| netcoreapp2.0 | 9.0KB | 473.2KB | +464.2KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
| netcoreapp2.1 | 9.0KB | 442.8KB | +433.8KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
| netcoreapp2.2 | 9.0KB | 442.8KB | +433.8KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
| netcoreapp3.0 | 9.5KB | 426.5KB | +417.0KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB |
| netcoreapp3.1 | 9.5KB | 425.0KB | +415.5KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
| net5.0 | 9.5KB | 370.3KB | +360.8KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB |
| net6.0 | 10.0KB | 292.3KB | +282.3KB | +17.7KB | +8.2KB | +1.1KB | +4.2KB |
| net7.0 | 10.0KB | 236.2KB | +226.2KB | +16.6KB | +6.9KB | +1.1KB | +3.7KB |
| net8.0 | 9.5KB | 192.7KB | +183.2KB | +15.5KB | +299bytes | +1.1KB | +3.7KB |
| net9.0 | 9.5KB | 125.2KB | +115.7KB | +16.0KB | | +1.1KB | +4.2KB |
| net10.0 | 10.0KB | 94.4KB | +84.4KB | +16.5KB | | +1.1KB | +4.2KB |
| net11.0 | 10.0KB | 41.3KB | +31.3KB | +16.5KB | | +1.6KB | +4.2KB |
| netstandard2.0 | 8.0KB | 513.0KB | +505.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
| netstandard2.1 | 8.5KB | 441.7KB | +433.2KB | +16.2KB | +7.7KB | +13.9KB | +18.9KB |
| net461 | 8.5KB | 512.5KB | +504.0KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB |
| net462 | 7.0KB | 516.0KB | +509.0KB | +16.7KB | +8.2KB | +14.4KB | +18.9KB |
| net47 | 7.0KB | 515.8KB | +508.8KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
| net471 | 8.5KB | 514.4KB | +505.9KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB |
| net472 | 8.5KB | 512.4KB | +503.9KB | +16.7KB | +7.7KB | +13.9KB | +18.9KB |
| net48 | 8.5KB | 512.4KB | +503.9KB | +16.7KB | +7.7KB | +13.9KB | +18.9KB |
| net481 | 8.5KB | 512.4KB | +503.9KB | +16.7KB | +7.7KB | +13.9KB | +18.9KB |
| netcoreapp2.0 | 9.0KB | 478.4KB | +469.4KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
| netcoreapp2.1 | 9.0KB | 447.4KB | +438.4KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB |
| netcoreapp2.2 | 9.0KB | 447.4KB | +438.4KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB |
| netcoreapp3.0 | 9.5KB | 431.6KB | +422.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
| netcoreapp3.1 | 9.5KB | 429.6KB | +420.1KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB |
| net5.0 | 9.5KB | 375.4KB | +365.9KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB |
| net6.0 | 10.0KB | 297.4KB | +287.4KB | +17.2KB | +8.2KB | +1.1KB | +3.7KB |
| net7.0 | 10.0KB | 240.8KB | +230.8KB | +17.1KB | +6.9KB | +1.6KB | +4.2KB |
| net8.0 | 9.5KB | 197.3KB | +187.8KB | +16.0KB | +811bytes | +1.1KB | +4.2KB |
| net9.0 | 9.5KB | 130.3KB | +120.8KB | +16.0KB | | +1.1KB | +4.2KB |
| net10.0 | 10.0KB | 99.5KB | +89.5KB | +16.5KB | | +1.1KB | +4.2KB |
| net11.0 | 10.0KB | 46.9KB | +36.9KB | +16.5KB | | +1.1KB | +4.2KB |
2 changes: 1 addition & 1 deletion src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<Project>
<PropertyGroup>
<NoWarn>CS1591;NETSDK1138;NU1901;NU1902;NU1903;CA1822;CA1847;CA1861;NU1510;NU1608;NU1109</NoWarn>
<Version>10.11.0</Version>
<Version>10.11.1</Version>
<AssemblyVersion>1.0.0</AssemblyVersion>
<PackageTags>Polyfill</PackageTags>
<DisableImplicitNamespaceImports>true</DisableImplicitNamespaceImports>
Expand Down
162 changes: 149 additions & 13 deletions src/Polyfill/ReadOnlySequenceStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ namespace System.Buffers;
using System.IO;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

Expand All @@ -46,15 +47,22 @@ sealed class ReadOnlySequenceStream :
Stream
{
ReadOnlySequence<byte> sequence;
// Incremental cursor into the sequence's segments, kept in sync with the absolute position.
// Advancing from this cursor avoids re-walking the segment list from the start on every read.
SequencePosition cursor;
long position;
bool disposed;

/// <summary>
/// Initializes a new instance of the <see cref="ReadOnlySequenceStream"/> class over the specified <see cref="ReadOnlySequence{Byte}"/>.
/// </summary>
//Link: https://learn.microsoft.com/en-us/dotnet/api/system.buffers.readonlysequencestream.-ctor?view=net-11.0
public ReadOnlySequenceStream(ReadOnlySequence<byte> source) =>
public ReadOnlySequenceStream(ReadOnlySequence<byte> source)
{
sequence = source;
cursor = source.Start;
position = 0;
}

/// <inheritdoc/>
public override bool CanRead => !disposed;
Expand Down Expand Up @@ -91,7 +99,7 @@ public override long Position
throw new ArgumentOutOfRangeException(nameof(value));
}

position = value;
MoveTo(value);
}
}

Expand All @@ -106,14 +114,15 @@ public override int Read(byte[] buffer, int offset, int count)
return 0;
}

var remaining = sequence.Slice(position);
var remaining = sequence.Slice(cursor);
var toRead = (int)Math.Min(remaining.Length, count);
if (toRead <= 0)
{
return 0;
}

remaining.Slice(0, toRead).CopyTo(buffer.AsSpan(offset, toRead));
cursor = sequence.GetPosition(toRead, cursor);
position += toRead;
return toRead;
}
Expand All @@ -128,7 +137,8 @@ public override int ReadByte()
return -1;
}

var result = sequence.Slice(position, 1).First.Span[0];
var result = sequence.Slice(cursor, 1).First.Span[0];
cursor = sequence.GetPosition(1, cursor);
position++;
return result;
}
Expand All @@ -137,20 +147,14 @@ public override int ReadByte()
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
GuardRange(buffer, offset, count);
ThrowIfDisposed();

if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled<int>(cancellationToken);
}

try
{
return Task.FromResult(Read(buffer, offset, count));
}
catch (Exception exception)
{
return Task.FromException<int>(exception);
}
return Task.FromResult(Read(buffer, offset, count));
}

/// <inheritdoc/>
Expand All @@ -166,16 +170,147 @@ public override long Seek(long offset, SeekOrigin origin)
_ => throw new ArgumentException("Invalid seek origin.", nameof(origin))
};

if (offset > long.MaxValue - basePosition)
{
throw new ArgumentOutOfRangeException(nameof(offset));
}

var newPosition = basePosition + offset;
if (newPosition < 0)
{
throw new IOException("An attempt was made to move the position before the beginning of the stream.");
}

position = newPosition;
MoveTo(newPosition);
return position;
}

// Repositions the segment cursor to the given absolute position, advancing forward from the
// current cursor when possible and only walking from the start for backward jumps.
void MoveTo(long value)
{
if (value >= sequence.Length)
{
cursor = sequence.End;
}
else if (value >= position)
{
cursor = sequence.GetPosition(value - position, cursor);
}
else
{
cursor = sequence.GetPosition(value, sequence.Start);
}

position = value;
}

#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
// Stream.CopyTo(Stream, int) only became virtual in netcoreapp2.1/netstandard2.1. On older
// targets it cannot be overridden, so the base implementation (which routes through the
// cursor-based Read above) is used instead.
/// <inheritdoc/>
public override void CopyTo(Stream destination, int bufferSize)
{
GuardCopyTo(destination, bufferSize);
ThrowIfDisposed();

if (position >= sequence.Length)
{
return;
}

foreach (var segment in sequence.Slice(cursor))
{
destination.Write(segment.Span);
}

cursor = sequence.End;
position = sequence.Length;
}
#endif

/// <inheritdoc/>
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
{
GuardCopyTo(destination, bufferSize);
ThrowIfDisposed();

if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled(cancellationToken);
}

if (position >= sequence.Length)
{
return Task.CompletedTask;
}

return CopyToAsyncCore(destination, cancellationToken);
}

async Task CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
{
foreach (var segment in sequence.Slice(cursor))
{
await WriteSegmentAsync(destination, segment, cancellationToken).ConfigureAwait(false);
}

cursor = sequence.End;
position = sequence.Length;
}

static Task WriteSegmentAsync(Stream destination, ReadOnlyMemory<byte> segment, CancellationToken cancellationToken)
{
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
return destination.WriteAsync(segment, cancellationToken).AsTask();
#else
var array = GetSegmentArray(segment, out var offset, out var count);
return destination.WriteAsync(array, offset, count, cancellationToken);
#endif
}

#if !(NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER)
static byte[] GetSegmentArray(ReadOnlyMemory<byte> segment, out int offset, out int count)
{
if (MemoryMarshal.TryGetArray(segment, out var arraySegment) &&
arraySegment.Array != null)
{
offset = arraySegment.Offset;
count = arraySegment.Count;
return arraySegment.Array;
}

var array = segment.ToArray();
offset = 0;
count = array.Length;
return array;
}
#endif

static void GuardCopyTo(Stream destination, int bufferSize)
{
if (destination == null)
{
throw new ArgumentNullException(nameof(destination));
}

if (bufferSize <= 0)
{
throw new ArgumentOutOfRangeException(nameof(bufferSize));
}

if (!destination.CanWrite)
{
if (destination.CanRead)
{
throw new NotSupportedException("Stream does not support writing.");
}

throw new ObjectDisposedException(null, "Cannot access a closed stream.");
}
}

/// <inheritdoc/>
public override void Flush()
{
Expand All @@ -198,6 +333,7 @@ protected override void Dispose(bool disposing)
{
disposed = true;
sequence = default;
cursor = default;
base.Dispose(disposing);
}

Expand Down
Loading
Loading