Skip to content

Commit 1fc9589

Browse files
authored
Merge branch 'master' into 3415-flyout-stickyscrollheader
2 parents 2036aaa + a778ffc commit 1fc9589

14 files changed

+628
-38
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Diagnostics.Contracts;
6+
using System.IO;
7+
using System.Runtime.CompilerServices;
8+
using Microsoft.Toolkit.HighPerformance.Buffers;
9+
using Microsoft.Toolkit.HighPerformance.Streams;
10+
using Microsoft.Toolkit.HighPerformance.Streams.Sources;
11+
12+
namespace Microsoft.Toolkit.HighPerformance.Extensions
13+
{
14+
/// <summary>
15+
/// Helpers for working with the <see cref="ArrayPoolBufferWriter{T}"/> type.
16+
/// </summary>
17+
public static class ArrayPoolBufferWriterExtensions
18+
{
19+
/// <summary>
20+
/// Returns a <see cref="Stream"/> that can be used to write to a target an <see cref="ArrayPoolBufferWriter{T}"/> of <see cref="byte"/> instance.
21+
/// </summary>
22+
/// <param name="writer">The target <see cref="ArrayPoolBufferWriter{T}"/> instance.</param>
23+
/// <returns>A <see cref="Stream"/> wrapping <paramref name="writer"/> and writing data to its underlying buffer.</returns>
24+
/// <remarks>The returned <see cref="Stream"/> can only be written to and does not support seeking.</remarks>
25+
[Pure]
26+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
27+
public static Stream AsStream(this ArrayPoolBufferWriter<byte> writer)
28+
{
29+
return new IBufferWriterStream<ArrayBufferWriterOwner>(new ArrayBufferWriterOwner(writer));
30+
}
31+
}
32+
}

Microsoft.Toolkit.HighPerformance/Extensions/IBufferWriterExtensions.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@
44

55
using System;
66
using System.Buffers;
7+
using System.Diagnostics.Contracts;
8+
using System.IO;
79
using System.Runtime.CompilerServices;
810
using System.Runtime.InteropServices;
11+
using Microsoft.Toolkit.HighPerformance.Buffers;
12+
using Microsoft.Toolkit.HighPerformance.Streams;
13+
using Microsoft.Toolkit.HighPerformance.Streams.Sources;
914

1015
namespace Microsoft.Toolkit.HighPerformance.Extensions
1116
{
@@ -14,6 +19,28 @@ namespace Microsoft.Toolkit.HighPerformance.Extensions
1419
/// </summary>
1520
public static class IBufferWriterExtensions
1621
{
22+
/// <summary>
23+
/// Returns a <see cref="Stream"/> that can be used to write to a target an <see cref="IBufferWriter{T}"/> of <see cref="byte"/> instance.
24+
/// </summary>
25+
/// <param name="writer">The target <see cref="IBufferWriter{T}"/> instance.</param>
26+
/// <returns>A <see cref="Stream"/> wrapping <paramref name="writer"/> and writing data to its underlying buffer.</returns>
27+
/// <remarks>The returned <see cref="Stream"/> can only be written to and does not support seeking.</remarks>
28+
[Pure]
29+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
30+
public static Stream AsStream(this IBufferWriter<byte> writer)
31+
{
32+
if (writer.GetType() == typeof(ArrayPoolBufferWriter<byte>))
33+
{
34+
// If the input writer is of type ArrayPoolBufferWriter<byte>, we can use the type
35+
// specific buffer writer owner to let the JIT elide callvirts when accessing it.
36+
var internalWriter = Unsafe.As<ArrayPoolBufferWriter<byte>>(writer)!;
37+
38+
return new IBufferWriterStream<ArrayBufferWriterOwner>(new ArrayBufferWriterOwner(internalWriter));
39+
}
40+
41+
return new IBufferWriterStream<IBufferWriterOwner>(new IBufferWriterOwner(writer));
42+
}
43+
1744
/// <summary>
1845
/// Writes a value of a specified type into a target <see cref="IBufferWriter{T}"/> instance.
1946
/// </summary>

Microsoft.Toolkit.HighPerformance/Extensions/StreamExtensions.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -167,24 +167,24 @@ public static void Write(this Stream stream, ReadOnlySpan<byte> buffer)
167167
/// <returns>The <typeparamref name="T"/> value read from <paramref name="stream"/>.</returns>
168168
/// <exception cref="InvalidOperationException">Thrown if <paramref name="stream"/> reaches the end.</exception>
169169
#if SPAN_RUNTIME_SUPPORT
170-
// Avoid inlining as we're renting a stack buffer, which
171-
// cause issues if this method was called inside a loop
172-
[MethodImpl(MethodImplOptions.NoInlining)]
170+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
173171
#endif
174172
public static T Read<T>(this Stream stream)
175173
where T : unmanaged
176174
{
177175
#if SPAN_RUNTIME_SUPPORT
178-
Span<byte> span = stackalloc byte[Unsafe.SizeOf<T>()];
176+
T result = default;
177+
int length = Unsafe.SizeOf<T>();
179178

180-
if (stream.Read(span) != span.Length)
179+
unsafe
181180
{
182-
ThrowInvalidOperationExceptionForEndOfStream();
181+
if (stream.Read(new Span<byte>(&result, length)) != length)
182+
{
183+
ThrowInvalidOperationExceptionForEndOfStream();
184+
}
183185
}
184186

185-
ref byte r0 = ref MemoryMarshal.GetReference(span);
186-
187-
return Unsafe.ReadUnaligned<T>(ref r0);
187+
return result;
188188
#else
189189
int length = Unsafe.SizeOf<T>();
190190
byte[] buffer = ArrayPool<byte>.Shared.Rent(length);
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#if SPAN_RUNTIME_SUPPORT
6+
7+
using System;
8+
using System.IO;
9+
using System.Threading;
10+
using System.Threading.Tasks;
11+
12+
namespace Microsoft.Toolkit.HighPerformance.Streams
13+
{
14+
/// <inheritdoc cref="IBufferWriterStream{TWriter}"/>
15+
internal sealed partial class IBufferWriterStream<TWriter>
16+
{
17+
/// <inheritdoc/>
18+
public override void CopyTo(Stream destination, int bufferSize)
19+
{
20+
throw MemoryStream.GetNotSupportedException();
21+
}
22+
23+
/// <inheritdoc/>
24+
public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
25+
{
26+
throw MemoryStream.GetNotSupportedException();
27+
}
28+
29+
/// <inheritdoc/>
30+
public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
31+
{
32+
if (cancellationToken.IsCancellationRequested)
33+
{
34+
return new ValueTask(Task.FromCanceled(cancellationToken));
35+
}
36+
37+
try
38+
{
39+
Write(buffer.Span);
40+
41+
return default;
42+
}
43+
catch (OperationCanceledException e)
44+
{
45+
return new ValueTask(Task.FromCanceled(e.CancellationToken));
46+
}
47+
catch (Exception e)
48+
{
49+
return new ValueTask(Task.FromException(e));
50+
}
51+
}
52+
53+
/// <inheritdoc/>
54+
public override int Read(Span<byte> buffer)
55+
{
56+
throw MemoryStream.GetNotSupportedException();
57+
}
58+
59+
/// <inheritdoc/>
60+
public override void Write(ReadOnlySpan<byte> buffer)
61+
{
62+
MemoryStream.ValidateDisposed(this.disposed);
63+
64+
Span<byte> destination = this.bufferWriter.GetSpan(buffer.Length);
65+
66+
if (!buffer.TryCopyTo(destination))
67+
{
68+
MemoryStream.ThrowArgumentExceptionForEndOfStreamOnWrite();
69+
}
70+
71+
this.bufferWriter.Advance(buffer.Length);
72+
}
73+
}
74+
}
75+
76+
#endif
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Buffers;
7+
using System.IO;
8+
using System.Runtime.CompilerServices;
9+
using System.Threading;
10+
using System.Threading.Tasks;
11+
12+
namespace Microsoft.Toolkit.HighPerformance.Streams
13+
{
14+
/// <summary>
15+
/// A <see cref="Stream"/> implementation wrapping an <see cref="IBufferWriter{T}"/> instance.
16+
/// </summary>
17+
/// <typeparam name="TWriter">The type of buffer writer to use.</typeparam>
18+
internal sealed partial class IBufferWriterStream<TWriter> : Stream
19+
where TWriter : struct, IBufferWriter<byte>
20+
{
21+
/// <summary>
22+
/// The target <typeparamref name="TWriter"/> instance to use.
23+
/// </summary>
24+
private readonly TWriter bufferWriter;
25+
26+
/// <summary>
27+
/// Indicates whether or not the current instance has been disposed
28+
/// </summary>
29+
private bool disposed;
30+
31+
/// <summary>
32+
/// Initializes a new instance of the <see cref="IBufferWriterStream{TWriter}"/> class.
33+
/// </summary>
34+
/// <param name="bufferWriter">The target <see cref="IBufferWriter{T}"/> instance to use.</param>
35+
public IBufferWriterStream(TWriter bufferWriter)
36+
{
37+
this.bufferWriter = bufferWriter;
38+
}
39+
40+
/// <inheritdoc/>
41+
public override bool CanRead => false;
42+
43+
/// <inheritdoc/>
44+
public override bool CanSeek => false;
45+
46+
/// <inheritdoc/>
47+
public override bool CanWrite
48+
{
49+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
50+
get => !this.disposed;
51+
}
52+
53+
/// <inheritdoc/>
54+
public override long Length => throw MemoryStream.GetNotSupportedException();
55+
56+
/// <inheritdoc/>
57+
public override long Position
58+
{
59+
get => throw MemoryStream.GetNotSupportedException();
60+
set => throw MemoryStream.GetNotSupportedException();
61+
}
62+
63+
/// <inheritdoc/>
64+
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
65+
{
66+
throw MemoryStream.GetNotSupportedException();
67+
}
68+
69+
/// <inheritdoc/>
70+
public override void Flush()
71+
{
72+
}
73+
74+
/// <inheritdoc/>
75+
public override Task FlushAsync(CancellationToken cancellationToken)
76+
{
77+
if (cancellationToken.IsCancellationRequested)
78+
{
79+
return Task.FromCanceled(cancellationToken);
80+
}
81+
82+
return Task.CompletedTask;
83+
}
84+
85+
/// <inheritdoc/>
86+
public override Task<int> ReadAsync(byte[]? buffer, int offset, int count, CancellationToken cancellationToken)
87+
{
88+
throw MemoryStream.GetNotSupportedException();
89+
}
90+
91+
/// <inheritdoc/>
92+
public override Task WriteAsync(byte[]? buffer, int offset, int count, CancellationToken cancellationToken)
93+
{
94+
if (cancellationToken.IsCancellationRequested)
95+
{
96+
return Task.FromCanceled(cancellationToken);
97+
}
98+
99+
try
100+
{
101+
Write(buffer, offset, count);
102+
103+
return Task.CompletedTask;
104+
}
105+
catch (OperationCanceledException e)
106+
{
107+
return Task.FromCanceled(e.CancellationToken);
108+
}
109+
catch (Exception e)
110+
{
111+
return Task.FromException(e);
112+
}
113+
}
114+
115+
/// <inheritdoc/>
116+
public override long Seek(long offset, SeekOrigin origin)
117+
{
118+
throw MemoryStream.GetNotSupportedException();
119+
}
120+
121+
/// <inheritdoc/>
122+
public override void SetLength(long value)
123+
{
124+
throw MemoryStream.GetNotSupportedException();
125+
}
126+
127+
/// <inheritdoc/>
128+
public override int Read(byte[]? buffer, int offset, int count)
129+
{
130+
throw MemoryStream.GetNotSupportedException();
131+
}
132+
133+
/// <inheritdoc/>
134+
public override int ReadByte()
135+
{
136+
throw MemoryStream.GetNotSupportedException();
137+
}
138+
139+
/// <inheritdoc/>
140+
public override void Write(byte[]? buffer, int offset, int count)
141+
{
142+
MemoryStream.ValidateDisposed(this.disposed);
143+
MemoryStream.ValidateBuffer(buffer, offset, count);
144+
145+
Span<byte>
146+
source = buffer.AsSpan(offset, count),
147+
destination = this.bufferWriter.GetSpan(count);
148+
149+
if (!source.TryCopyTo(destination))
150+
{
151+
MemoryStream.ThrowArgumentExceptionForEndOfStreamOnWrite();
152+
}
153+
154+
this.bufferWriter.Advance(count);
155+
}
156+
157+
/// <inheritdoc/>
158+
public override void WriteByte(byte value)
159+
{
160+
MemoryStream.ValidateDisposed(this.disposed);
161+
162+
this.bufferWriter.GetSpan(1)[0] = value;
163+
164+
this.bufferWriter.Advance(1);
165+
}
166+
167+
/// <inheritdoc/>
168+
protected override void Dispose(bool disposing)
169+
{
170+
this.disposed = true;
171+
}
172+
}
173+
}

0 commit comments

Comments
 (0)