Skip to content

Commit fdee563

Browse files
Merge pull request #3353 from Sergio0694/feature/array3d-apis
2D System.Memory-like primitives (Span2D<T>, Memory2D<T>)
2 parents 06eed0d + 7e67a9c commit fdee563

File tree

68 files changed

+11240
-979
lines changed

Some content is hidden

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

68 files changed

+11240
-979
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ namespace Microsoft.Toolkit.HighPerformance.Buffers
2424
/// the arrays in use are rented from the shared <see cref="ArrayPool{T}"/> instance,
2525
/// and that <see cref="ArrayPoolBufferWriter{T}"/> is also available on .NET Standard 2.0.
2626
/// </remarks>
27-
[DebuggerTypeProxy(typeof(ArrayPoolBufferWriterDebugView<>))]
27+
[DebuggerTypeProxy(typeof(MemoryDebugView<>))]
2828
[DebuggerDisplay("{ToString(),raw}")]
2929
public sealed class ArrayPoolBufferWriter<T> : IBuffer<T>, IMemoryOwner<T>
3030
{
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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ namespace Microsoft.Toolkit.HighPerformance.Buffers
1616
/// An <see cref="IMemoryOwner{T}"/> implementation with an embedded length and a fast <see cref="Span{T}"/> accessor.
1717
/// </summary>
1818
/// <typeparam name="T">The type of items to store in the current instance.</typeparam>
19-
[DebuggerTypeProxy(typeof(MemoryOwnerDebugView<>))]
19+
[DebuggerTypeProxy(typeof(MemoryDebugView<>))]
2020
[DebuggerDisplay("{ToString(),raw}")]
2121
public sealed class MemoryOwner<T> : IMemoryOwner<T>
2222
{

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ namespace Microsoft.Toolkit.HighPerformance.Buffers
3131
/// Not doing so will cause the underlying buffer not to be returned to the shared pool.
3232
/// </summary>
3333
/// <typeparam name="T">The type of items to store in the current instance.</typeparam>
34-
[DebuggerTypeProxy(typeof(SpanOwnerDebugView<>))]
34+
[DebuggerTypeProxy(typeof(MemoryDebugView<>))]
3535
[DebuggerDisplay("{ToString(),raw}")]
3636
public readonly ref struct SpanOwner<T>
3737
{

Microsoft.Toolkit.HighPerformance/Buffers/StringPool.cs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,7 @@ private unsafe ref string TryGet(ReadOnlySpan<char> span, int hashcode)
536536
(uint)i < (uint)length;
537537
i = entry.NextIndex)
538538
{
539-
entry = ref Unsafe.Add(ref mapEntriesRef, (IntPtr)(void*)(uint)i);
539+
entry = ref Unsafe.Add(ref mapEntriesRef, (nint)(uint)i);
540540

541541
if (entry.HashCode == hashcode &&
542542
entry.Value!.AsSpan().SequenceEqual(span))
@@ -556,7 +556,7 @@ private unsafe ref string TryGet(ReadOnlySpan<char> span, int hashcode)
556556
/// <param name="value">The new <see cref="string"/> instance to store.</param>
557557
/// <param name="hashcode">The precomputed hashcode for <paramref name="value"/>.</param>
558558
[MethodImpl(MethodImplOptions.NoInlining)]
559-
private unsafe void Insert(string value, int hashcode)
559+
private void Insert(string value, int hashcode)
560560
{
561561
ref int bucketsRef = ref this.buckets.DangerousGetReference();
562562
ref MapEntry mapEntriesRef = ref this.mapEntries.DangerousGetReference();
@@ -571,7 +571,7 @@ private unsafe void Insert(string value, int hashcode)
571571
entryIndex = heapEntriesRef.MapIndex;
572572
heapIndex = 0;
573573

574-
ref MapEntry removedEntry = ref Unsafe.Add(ref mapEntriesRef, (IntPtr)(void*)(uint)entryIndex);
574+
ref MapEntry removedEntry = ref Unsafe.Add(ref mapEntriesRef, (nint)(uint)entryIndex);
575575

576576
// The removal logic can be extremely optimized in this case, as we
577577
// can retrieve the precomputed hashcode for the target entry by doing
@@ -588,9 +588,9 @@ private unsafe void Insert(string value, int hashcode)
588588
}
589589

590590
int bucketIndex = hashcode & (this.buckets.Length - 1);
591-
ref int targetBucket = ref Unsafe.Add(ref bucketsRef, (IntPtr)(void*)(uint)bucketIndex);
592-
ref MapEntry targetMapEntry = ref Unsafe.Add(ref mapEntriesRef, (IntPtr)(void*)(uint)entryIndex);
593-
ref HeapEntry targetHeapEntry = ref Unsafe.Add(ref heapEntriesRef, (IntPtr)(void*)(uint)heapIndex);
591+
ref int targetBucket = ref Unsafe.Add(ref bucketsRef, (nint)(uint)bucketIndex);
592+
ref MapEntry targetMapEntry = ref Unsafe.Add(ref mapEntriesRef, (nint)(uint)entryIndex);
593+
ref HeapEntry targetHeapEntry = ref Unsafe.Add(ref heapEntriesRef, (nint)(uint)heapIndex);
594594

595595
// Assign the values in the new map entry
596596
targetMapEntry.HashCode = hashcode;
@@ -616,7 +616,7 @@ private unsafe void Insert(string value, int hashcode)
616616
/// <param name="mapIndex">The index of the target map node to remove.</param>
617617
/// <remarks>The input <see cref="string"/> instance needs to already exist in the map.</remarks>
618618
[MethodImpl(MethodImplOptions.NoInlining)]
619-
private unsafe void Remove(int hashcode, int mapIndex)
619+
private void Remove(int hashcode, int mapIndex)
620620
{
621621
ref MapEntry mapEntriesRef = ref this.mapEntries.DangerousGetReference();
622622
int
@@ -628,15 +628,15 @@ private unsafe void Remove(int hashcode, int mapIndex)
628628
// value we're looking for is guaranteed to be present
629629
while (true)
630630
{
631-
ref MapEntry candidate = ref Unsafe.Add(ref mapEntriesRef, (IntPtr)(void*)(uint)entryIndex);
631+
ref MapEntry candidate = ref Unsafe.Add(ref mapEntriesRef, (nint)(uint)entryIndex);
632632

633633
// Check the current value for a match
634634
if (entryIndex == mapIndex)
635635
{
636636
// If this was not the first list node, update the parent as well
637637
if (lastIndex != EndOfList)
638638
{
639-
ref MapEntry lastEntry = ref Unsafe.Add(ref mapEntriesRef, (IntPtr)(void*)(uint)lastIndex);
639+
ref MapEntry lastEntry = ref Unsafe.Add(ref mapEntriesRef, (nint)(uint)lastIndex);
640640

641641
lastEntry.NextIndex = candidate.NextIndex;
642642
}
@@ -662,14 +662,14 @@ private unsafe void Remove(int hashcode, int mapIndex)
662662
/// </summary>
663663
/// <param name="heapIndex">The index of the target heap node to update.</param>
664664
[MethodImpl(MethodImplOptions.NoInlining)]
665-
private unsafe void UpdateTimestamp(ref int heapIndex)
665+
private void UpdateTimestamp(ref int heapIndex)
666666
{
667667
int
668668
currentIndex = heapIndex,
669669
count = this.count;
670670
ref MapEntry mapEntriesRef = ref this.mapEntries.DangerousGetReference();
671671
ref HeapEntry heapEntriesRef = ref this.heapEntries.DangerousGetReference();
672-
ref HeapEntry root = ref Unsafe.Add(ref heapEntriesRef, (IntPtr)(void*)(uint)currentIndex);
672+
ref HeapEntry root = ref Unsafe.Add(ref heapEntriesRef, (nint)(uint)currentIndex);
673673
uint timestamp = this.timestamp;
674674

675675
// Check if incrementing the current timestamp for the heap node to update
@@ -721,7 +721,7 @@ private unsafe void UpdateTimestamp(ref int heapIndex)
721721
// Check and update the left child, if necessary
722722
if (left < count)
723723
{
724-
ref HeapEntry child = ref Unsafe.Add(ref heapEntriesRef, (IntPtr)(void*)(uint)left);
724+
ref HeapEntry child = ref Unsafe.Add(ref heapEntriesRef, (nint)(uint)left);
725725

726726
if (child.Timestamp < minimum.Timestamp)
727727
{
@@ -733,7 +733,7 @@ private unsafe void UpdateTimestamp(ref int heapIndex)
733733
// Same check as above for the right child
734734
if (right < count)
735735
{
736-
ref HeapEntry child = ref Unsafe.Add(ref heapEntriesRef, (IntPtr)(void*)(uint)right);
736+
ref HeapEntry child = ref Unsafe.Add(ref heapEntriesRef, (nint)(uint)right);
737737

738738
if (child.Timestamp < minimum.Timestamp)
739739
{
@@ -752,8 +752,8 @@ private unsafe void UpdateTimestamp(ref int heapIndex)
752752
}
753753

754754
// Update the indices in the respective map entries (accounting for the swap)
755-
Unsafe.Add(ref mapEntriesRef, (IntPtr)(void*)(uint)root.MapIndex).HeapIndex = targetIndex;
756-
Unsafe.Add(ref mapEntriesRef, (IntPtr)(void*)(uint)minimum.MapIndex).HeapIndex = currentIndex;
755+
Unsafe.Add(ref mapEntriesRef, (nint)(uint)root.MapIndex).HeapIndex = targetIndex;
756+
Unsafe.Add(ref mapEntriesRef, (nint)(uint)minimum.MapIndex).HeapIndex = currentIndex;
757757

758758
currentIndex = targetIndex;
759759

@@ -764,7 +764,7 @@ private unsafe void UpdateTimestamp(ref int heapIndex)
764764
minimum = temp;
765765

766766
// Update the reference to the root node
767-
root = ref Unsafe.Add(ref heapEntriesRef, (IntPtr)(void*)(uint)currentIndex);
767+
root = ref Unsafe.Add(ref heapEntriesRef, (nint)(uint)currentIndex);
768768
}
769769

770770
Fallback:
@@ -787,14 +787,14 @@ private unsafe void UpdateTimestamp(ref int heapIndex)
787787
/// a given number of nodes, those are all contiguous from the start of the array.
788788
/// </summary>
789789
[MethodImpl(MethodImplOptions.NoInlining)]
790-
private unsafe void UpdateAllTimestamps()
790+
private void UpdateAllTimestamps()
791791
{
792792
int count = this.count;
793793
ref HeapEntry heapEntriesRef = ref this.heapEntries.DangerousGetReference();
794794

795795
for (int i = 0; i < count; i++)
796796
{
797-
Unsafe.Add(ref heapEntriesRef, (IntPtr)(void*)(uint)i).Timestamp = (uint)i;
797+
Unsafe.Add(ref heapEntriesRef, (nint)(uint)i).Timestamp = (uint)i;
798798
}
799799
}
800800
}

Microsoft.Toolkit.HighPerformance/Buffers/Views/ArrayPoolBufferWriterDebugView{T}.cs

Lines changed: 0 additions & 30 deletions
This file was deleted.

Microsoft.Toolkit.HighPerformance/Buffers/Views/MemoryBufferWriterDebugView{T}.cs

Lines changed: 0 additions & 30 deletions
This file was deleted.

0 commit comments

Comments
 (0)