Skip to content

Commit d3a5ade

Browse files
authored
Merge branch 'master' into jamesmcroft/3517-bladeview-automation
2 parents 2e3e9c2 + 82f86e8 commit d3a5ade

File tree

284 files changed

+27565
-2023
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

284 files changed

+27565
-2023
lines changed

Microsoft.Toolkit.HighPerformance/Box{T}.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public static Box<T> GetFrom(object obj)
7878
ThrowInvalidCastExceptionForGetFrom();
7979
}
8080

81-
return Unsafe.As<Box<T>>(obj);
81+
return Unsafe.As<Box<T>>(obj)!;
8282
}
8383

8484
/// <summary>
@@ -94,7 +94,7 @@ public static Box<T> GetFrom(object obj)
9494
[MethodImpl(MethodImplOptions.AggressiveInlining)]
9595
public static Box<T> DangerousGetFrom(object obj)
9696
{
97-
return Unsafe.As<Box<T>>(obj);
97+
return Unsafe.As<Box<T>>(obj)!;
9898
}
9999

100100
/// <summary>
@@ -108,7 +108,7 @@ public static bool TryGetFrom(object obj, [NotNullWhen(true)] out Box<T>? box)
108108
{
109109
if (obj.GetType() == typeof(T))
110110
{
111-
box = Unsafe.As<Box<T>>(obj);
111+
box = Unsafe.As<Box<T>>(obj)!;
112112

113113
return true;
114114
}
@@ -145,7 +145,7 @@ public static implicit operator Box<T>(T value)
145145
// manually be implemented in the Box<T> type. For instance, boxing a float
146146
// and calling ToString() on it directly, on its boxed object or on a Box<T>
147147
// reference retrieved from it will produce the same result in all cases.
148-
return Unsafe.As<Box<T>>(value);
148+
return Unsafe.As<Box<T>>(value)!;
149149
}
150150

151151
/// <inheritdoc/>

Microsoft.Toolkit.HighPerformance/Buffers/ArrayPoolBufferWriter{T}.cs

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using System.Runtime.InteropServices;
1111
using Microsoft.Toolkit.HighPerformance.Buffers.Views;
1212
using Microsoft.Toolkit.HighPerformance.Extensions;
13+
using Microsoft.Toolkit.HighPerformance.Helpers.Internals;
1314

1415
namespace Microsoft.Toolkit.HighPerformance.Buffers
1516
{
@@ -24,7 +25,7 @@ namespace Microsoft.Toolkit.HighPerformance.Buffers
2425
/// the arrays in use are rented from the shared <see cref="ArrayPool{T}"/> instance,
2526
/// and that <see cref="ArrayPoolBufferWriter{T}"/> is also available on .NET Standard 2.0.
2627
/// </remarks>
27-
[DebuggerTypeProxy(typeof(ArrayPoolBufferWriterDebugView<>))]
28+
[DebuggerTypeProxy(typeof(MemoryDebugView<>))]
2829
[DebuggerDisplay("{ToString(),raw}")]
2930
public sealed class ArrayPoolBufferWriter<T> : IBuffer<T>, IMemoryOwner<T>
3031
{
@@ -233,15 +234,15 @@ public void Advance(int count)
233234
/// <inheritdoc/>
234235
public Memory<T> GetMemory(int sizeHint = 0)
235236
{
236-
CheckAndResizeBuffer(sizeHint);
237+
CheckBufferAndEnsureCapacity(sizeHint);
237238

238239
return this.array.AsMemory(this.index);
239240
}
240241

241242
/// <inheritdoc/>
242243
public Span<T> GetSpan(int sizeHint = 0)
243244
{
244-
CheckAndResizeBuffer(sizeHint);
245+
CheckBufferAndEnsureCapacity(sizeHint);
245246

246247
return this.array.AsSpan(this.index);
247248
}
@@ -251,9 +252,11 @@ public Span<T> GetSpan(int sizeHint = 0)
251252
/// </summary>
252253
/// <param name="sizeHint">The minimum number of items to ensure space for in <see cref="array"/>.</param>
253254
[MethodImpl(MethodImplOptions.AggressiveInlining)]
254-
private void CheckAndResizeBuffer(int sizeHint)
255+
private void CheckBufferAndEnsureCapacity(int sizeHint)
255256
{
256-
if (this.array is null)
257+
T[]? array = this.array;
258+
259+
if (array is null)
257260
{
258261
ThrowObjectDisposedException();
259262
}
@@ -268,12 +271,32 @@ private void CheckAndResizeBuffer(int sizeHint)
268271
sizeHint = 1;
269272
}
270273

271-
if (sizeHint > FreeCapacity)
274+
if (sizeHint > array!.Length - this.index)
272275
{
273-
int minimumSize = this.index + sizeHint;
276+
ResizeBuffer(sizeHint);
277+
}
278+
}
274279

275-
this.pool.Resize(ref this.array, minimumSize);
280+
/// <summary>
281+
/// Resizes <see cref="array"/> to ensure it can fit the specified number of new items.
282+
/// </summary>
283+
/// <param name="sizeHint">The minimum number of items to ensure space for in <see cref="array"/>.</param>
284+
[MethodImpl(MethodImplOptions.NoInlining)]
285+
private void ResizeBuffer(int sizeHint)
286+
{
287+
int minimumSize = this.index + sizeHint;
288+
289+
// The ArrayPool<T> class has a maximum threshold of 1024 * 1024 for the maximum length of
290+
// pooled arrays, and once this is exceeded it will just allocate a new array every time
291+
// of exactly the requested size. In that case, we manually round up the requested size to
292+
// the nearest power of two, to ensure that repeated consecutive writes when the array in
293+
// use is bigger than that threshold don't end up causing a resize every single time.
294+
if (minimumSize > 1024 * 1024)
295+
{
296+
minimumSize = BitOperations.RoundUpPowerOfTwo(minimumSize);
276297
}
298+
299+
this.pool.Resize(ref this.array, minimumSize);
277300
}
278301

279302
/// <inheritdoc/>
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
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.Runtime.CompilerServices;
8+
using System.Runtime.InteropServices;
9+
using Microsoft.Toolkit.HighPerformance.Buffers.Internals.Interfaces;
10+
using Microsoft.Toolkit.HighPerformance.Extensions;
11+
using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers;
12+
13+
namespace Microsoft.Toolkit.HighPerformance.Buffers.Internals
14+
{
15+
/// <summary>
16+
/// A custom <see cref="MemoryManager{T}"/> that casts data from a <typeparamref name="TFrom"/> array, to <typeparamref name="TTo"/> values.
17+
/// </summary>
18+
/// <typeparam name="TFrom">The source type of items to read.</typeparam>
19+
/// <typeparam name="TTo">The target type to cast the source items to.</typeparam>
20+
internal sealed class ArrayMemoryManager<TFrom, TTo> : MemoryManager<TTo>, IMemoryManager
21+
where TFrom : unmanaged
22+
where TTo : unmanaged
23+
{
24+
/// <summary>
25+
/// The source <typeparamref name="TFrom"/> array to read data from.
26+
/// </summary>
27+
private readonly TFrom[] array;
28+
29+
/// <summary>
30+
/// The starting offset within <see name="array"/>.
31+
/// </summary>
32+
private readonly int offset;
33+
34+
/// <summary>
35+
/// The original used length for <see name="array"/>.
36+
/// </summary>
37+
private readonly int length;
38+
39+
/// <summary>
40+
/// Initializes a new instance of the <see cref="ArrayMemoryManager{TFrom, TTo}"/> class.
41+
/// </summary>
42+
/// <param name="array">The source <typeparamref name="TFrom"/> array to read data from.</param>
43+
/// <param name="offset">The starting offset within <paramref name="array"/>.</param>
44+
/// <param name="length">The original used length for <paramref name="array"/>.</param>
45+
public ArrayMemoryManager(TFrom[] array, int offset, int length)
46+
{
47+
this.array = array;
48+
this.offset = offset;
49+
this.length = length;
50+
}
51+
52+
/// <inheritdoc/>
53+
public override Span<TTo> GetSpan()
54+
{
55+
#if SPAN_RUNTIME_SUPPORT
56+
ref TFrom r0 = ref this.array.DangerousGetReferenceAt(this.offset);
57+
ref TTo r1 = ref Unsafe.As<TFrom, TTo>(ref r0);
58+
int length = RuntimeHelpers.ConvertLength<TFrom, TTo>(this.length);
59+
60+
return MemoryMarshal.CreateSpan(ref r1, length);
61+
#else
62+
Span<TFrom> span = this.array.AsSpan(this.offset, this.length);
63+
64+
// We rely on MemoryMarshal.Cast here to deal with calculating the effective
65+
// size of the new span to return. This will also make the behavior consistent
66+
// for users that are both using this type as well as casting spans directly.
67+
return MemoryMarshal.Cast<TFrom, TTo>(span);
68+
#endif
69+
}
70+
71+
/// <inheritdoc/>
72+
public override unsafe MemoryHandle Pin(int elementIndex = 0)
73+
{
74+
if ((uint)elementIndex >= (uint)(this.length * Unsafe.SizeOf<TFrom>() / Unsafe.SizeOf<TTo>()))
75+
{
76+
ThrowArgumentOutOfRangeExceptionForInvalidIndex();
77+
}
78+
79+
int
80+
bytePrefix = this.offset * Unsafe.SizeOf<TFrom>(),
81+
byteSuffix = elementIndex * Unsafe.SizeOf<TTo>(),
82+
byteOffset = bytePrefix + byteSuffix;
83+
84+
GCHandle handle = GCHandle.Alloc(this.array, GCHandleType.Pinned);
85+
86+
ref TFrom r0 = ref this.array.DangerousGetReference();
87+
ref byte r1 = ref Unsafe.As<TFrom, byte>(ref r0);
88+
ref byte r2 = ref Unsafe.Add(ref r1, byteOffset);
89+
void* pi = Unsafe.AsPointer(ref r2);
90+
91+
return new MemoryHandle(pi, handle);
92+
}
93+
94+
/// <inheritdoc/>
95+
public override void Unpin()
96+
{
97+
}
98+
99+
/// <inheritdoc/>
100+
protected override void Dispose(bool disposing)
101+
{
102+
}
103+
104+
/// <inheritdoc/>
105+
public Memory<T> GetMemory<T>(int offset, int length)
106+
where T : unmanaged
107+
{
108+
// We need to calculate the right offset and length of the new Memory<T>. The local offset
109+
// is the original offset into the wrapped TFrom[] array, while the input offset is the one
110+
// with respect to TTo items in the Memory<TTo> instance that is currently being cast.
111+
int
112+
absoluteOffset = this.offset + RuntimeHelpers.ConvertLength<TTo, TFrom>(offset),
113+
absoluteLength = RuntimeHelpers.ConvertLength<TTo, TFrom>(length);
114+
115+
// We have a special handling in cases where the user is circling back to the original type
116+
// of the wrapped array. In this case we can just return a memory wrapping that array directly,
117+
// with offset and length being adjusted, without the memory manager indirection.
118+
if (typeof(T) == typeof(TFrom))
119+
{
120+
return (Memory<T>)(object)this.array.AsMemory(absoluteOffset, absoluteLength);
121+
}
122+
123+
return new ArrayMemoryManager<TFrom, T>(this.array, absoluteOffset, absoluteLength).Memory;
124+
}
125+
126+
/// <summary>
127+
/// Throws an <see cref="ArgumentOutOfRangeException"/> when the target index for <see cref="Pin"/> is invalid.
128+
/// </summary>
129+
private static void ThrowArgumentOutOfRangeExceptionForInvalidIndex()
130+
{
131+
throw new ArgumentOutOfRangeException("elementIndex", "The input index is not in the valid range");
132+
}
133+
}
134+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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+
8+
namespace Microsoft.Toolkit.HighPerformance.Buffers.Internals.Interfaces
9+
{
10+
/// <summary>
11+
/// An interface for a <see cref="MemoryManager{T}"/> instance that can reinterpret its underlying data.
12+
/// </summary>
13+
internal interface IMemoryManager
14+
{
15+
/// <summary>
16+
/// Creates a new <see cref="Memory{T}"/> that reinterprets the underlying data for the current instance.
17+
/// </summary>
18+
/// <typeparam name="T">The target type to cast the items to.</typeparam>
19+
/// <param name="offset">The starting offset within the data store.</param>
20+
/// <param name="length">The original used length for the data store.</param>
21+
/// <returns>A new <see cref="Memory{T}"/> instance of the specified type, reinterpreting the current items.</returns>
22+
Memory<T> GetMemory<T>(int offset, int length)
23+
where T : unmanaged;
24+
}
25+
}

0 commit comments

Comments
 (0)