Skip to content

Commit e356c35

Browse files
authored
Merge branch 'master' into feature/memory-owner-dangerousgetarray
2 parents e6bce57 + d434f43 commit e356c35

File tree

315 files changed

+27402
-2423
lines changed

Some content is hidden

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

315 files changed

+27402
-2423
lines changed

.github/ISSUE_TEMPLATE/bug_report.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,7 @@ assignees: ''
77

88
---
99

10-
<!--
11-
PLEASE HELP US PROCESS GITHUB ISSUES FASTER BY PROVIDING THE FOLLOWING INFORMATION.
12-
ISSUES MISSING IMPORTANT INFORMATION MAY BE CLOSED WITHOUT INVESTIGATION.
13-
-->
10+
<!-- 🚨 Please Do Not skip any instructions and information mentioned below as they are all required and essential to investigate the issue. Issues with missing information may be closed without investigation 🚨 -->
1411

1512
## Describe the bug
1613
A clear and concise description of what the bug is.

.github/ISSUE_TEMPLATE/feature_request.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ assignees: ''
77

88
---
99

10+
<!-- 🚨 Please provide detailed information and Do Not skip any instructions as they are all required and essential to help us understand the feature 🚨 -->
11+
1012
## Describe the problem this feature would solve
1113
<!-- Please describe or link to any existing issues or discussions.
1214
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->

.github/ISSUE_TEMPLATE/question.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Hi!
1313
We try and keep our GitHub issue list for bugs and features.
1414
1515
Ideally, it'd be great to post your question on Stack Overflow using the 'windows-community-toolkit' tag here: https://stackoverflow.com/questions/tagged/windows-community-toolkit
16+
🚨 Please provide detailed information that includes examples, screenshots, and relevant issues if possible 🚨
1617
1718
If this is more about a scenario that you think is missing documentation, please file an issue instead at https://github.com/MicrosoftDocs/WindowsCommunityToolkitDocs/issues/new
1819

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
<!-- 🚨 Please Do Not skip any instructions and information mentioned below as they are all required and essential to evaluate and test the PR. By fulfilling all the required information you will be able to reduce the volume of questions and most likely help merge the PR faster 🚨 -->
2+
13
## Fixes #
24
<!-- Add the relevant issue number after the "#" mentioned above (for ex: Fixes #1234) which will automatically close the issue once the PR is merged. -->
35

@@ -38,7 +40,7 @@ Please check if your PR fulfills the following requirements:
3840
- [ ] Contains **NO** breaking changes
3941

4042
<!-- If this PR contains a breaking change, please describe the impact and migration path for existing applications below.
41-
Please note that breaking changes are likely to be rejected. -->
43+
Please note that breaking changes are likely to be rejected within minor release cycles or held until major versions. -->
4244

4345

4446
## Other information

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,4 +227,8 @@ msbuild.binlog
227227
!/build/tools/packages.config
228228

229229
# Generated file from .ttinclude
230-
**/Generated/TypeInfo.g.cs
230+
**/Generated/TypeInfo.g.cs
231+
232+
# TAEF Log output
233+
WexLogFileOutput
234+
*.wtl

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: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
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.Buffers;
9+
using System.Runtime.CompilerServices;
10+
using System.Runtime.InteropServices;
11+
using Microsoft.Toolkit.HighPerformance.Extensions;
12+
13+
namespace Microsoft.Toolkit.HighPerformance.Buffers.Internals
14+
{
15+
/// <summary>
16+
/// A custom <see cref="MemoryManager{T}"/> that can wrap arbitrary <see cref="object"/> instances.
17+
/// </summary>
18+
/// <typeparam name="T">The type of elements in the target memory area.</typeparam>
19+
internal sealed class RawObjectMemoryManager<T> : MemoryManager<T>
20+
{
21+
/// <summary>
22+
/// The target <see cref="object"/> instance.
23+
/// </summary>
24+
private readonly object instance;
25+
26+
/// <summary>
27+
/// The initial offset within <see cref="instance"/>.
28+
/// </summary>
29+
private readonly IntPtr offset;
30+
31+
/// <summary>
32+
/// The length of the target memory area.
33+
/// </summary>
34+
private readonly int length;
35+
36+
/// <summary>
37+
/// Initializes a new instance of the <see cref="RawObjectMemoryManager{T}"/> class.
38+
/// </summary>
39+
/// <param name="instance">The target <see cref="object"/> instance.</param>
40+
/// <param name="offset">The starting offset within <paramref name="instance"/>.</param>
41+
/// <param name="length">The usable length within <paramref name="instance"/>.</param>
42+
public RawObjectMemoryManager(object instance, IntPtr offset, int length)
43+
{
44+
this.instance = instance;
45+
this.offset = offset;
46+
this.length = length;
47+
}
48+
49+
/// <inheritdoc/>
50+
public override Span<T> GetSpan()
51+
{
52+
ref T r0 = ref this.instance.DangerousGetObjectDataReferenceAt<T>(this.offset);
53+
54+
return MemoryMarshal.CreateSpan(ref r0, this.length);
55+
}
56+
57+
/// <inheritdoc/>
58+
public override unsafe MemoryHandle Pin(int elementIndex = 0)
59+
{
60+
if ((uint)elementIndex >= (uint)this.length)
61+
{
62+
ThrowArgumentOutOfRangeExceptionForInvalidElementIndex();
63+
}
64+
65+
// Allocating a pinned handle for the array with fail and throw an exception
66+
// if the array contains non blittable data. This is the expected behavior and
67+
// the same happens when trying to pin a Memory<T> instance obtained through
68+
// traditional means (eg. via the implicit T[] array conversion), if T is a
69+
// reference type or a type containing some references.
70+
GCHandle handle = GCHandle.Alloc(this.instance, GCHandleType.Pinned);
71+
ref T r0 = ref this.instance.DangerousGetObjectDataReferenceAt<T>(this.offset);
72+
ref T r1 = ref Unsafe.Add(ref r0, (nint)(uint)elementIndex);
73+
void* p = Unsafe.AsPointer(ref r1);
74+
75+
return new MemoryHandle(p, handle);
76+
}
77+
78+
/// <inheritdoc/>
79+
public override void Unpin()
80+
{
81+
}
82+
83+
/// <inheritdoc/>
84+
protected override void Dispose(bool disposing)
85+
{
86+
}
87+
88+
/// <summary>
89+
/// Throws an <see cref="ArgumentOutOfRangeException"/> when the input index for <see cref="Pin"/> is not valid.
90+
/// </summary>
91+
private static void ThrowArgumentOutOfRangeExceptionForInvalidElementIndex()
92+
{
93+
throw new ArgumentOutOfRangeException("elementIndex", "The input element index was not in the valid range");
94+
}
95+
}
96+
}
97+
98+
#endif

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Diagnostics;
88
using System.Diagnostics.Contracts;
99
using System.Runtime.CompilerServices;
10+
using Microsoft.Toolkit.HighPerformance.Buffers.Views;
1011

1112
namespace Microsoft.Toolkit.HighPerformance.Buffers
1213
{
@@ -20,7 +21,7 @@ namespace Microsoft.Toolkit.HighPerformance.Buffers
2021
/// instances (or objects that can be converted to a <see cref="Memory{T}"/>), to ensure the data is written directly
2122
/// to the intended buffer, with no possibility of doing additional allocations or expanding the available capacity.
2223
/// </remarks>
23-
[DebuggerTypeProxy(typeof(MemoryBufferWriter<>))]
24+
[DebuggerTypeProxy(typeof(MemoryDebugView<>))]
2425
[DebuggerDisplay("{ToString(),raw}")]
2526
public sealed class MemoryBufferWriter<T> : IBuffer<T>
2627
{

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace Microsoft.Toolkit.HighPerformance.Buffers
1919
/// An <see cref="IMemoryOwner{T}"/> implementation with an embedded length and a fast <see cref="Span{T}"/> accessor.
2020
/// </summary>
2121
/// <typeparam name="T">The type of items to store in the current instance.</typeparam>
22-
[DebuggerTypeProxy(typeof(MemoryOwnerDebugView<>))]
22+
[DebuggerTypeProxy(typeof(MemoryDebugView<>))]
2323
[DebuggerDisplay("{ToString(),raw}")]
2424
public sealed class MemoryOwner<T> : IMemoryOwner<T>
2525
{
@@ -286,6 +286,11 @@ public MemoryOwner<T> Slice(int start, int length)
286286
ThrowInvalidLengthException();
287287
}
288288

289+
// We're transferring the ownership of the underlying array, so the current
290+
// instance no longer needs to be disposed. Because of this, we can manually
291+
// suppress the finalizer to reduce the overhead on the garbage collector.
292+
GC.SuppressFinalize(this);
293+
289294
return new MemoryOwner<T>(start, length, this.pool, array!);
290295
}
291296

0 commit comments

Comments
 (0)