diff --git a/src/CommunityToolkit.HighPerformance/Enumerables/ReadOnlyRefEnumerable{T}.cs b/src/CommunityToolkit.HighPerformance/Enumerables/ReadOnlyRefEnumerable{T}.cs index bd2175ee..dcc9f088 100644 --- a/src/CommunityToolkit.HighPerformance/Enumerables/ReadOnlyRefEnumerable{T}.cs +++ b/src/CommunityToolkit.HighPerformance/Enumerables/ReadOnlyRefEnumerable{T}.cs @@ -21,7 +21,17 @@ namespace CommunityToolkit.HighPerformance.Enumerables; /// The type of items to enumerate. public readonly ref struct ReadOnlyRefEnumerable { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + /// + /// The reference for the instance. + /// + private readonly ref readonly T reference; + + /// + /// The length of the current sequence. + /// + private readonly int length; +#elif NETSTANDARD2_1_OR_GREATER /// /// The instance pointing to the first item in the target memory area. /// @@ -51,6 +61,7 @@ public readonly ref struct ReadOnlyRefEnumerable private readonly int step; #if NETSTANDARD2_1_OR_GREATER +#if !NET7_0_OR_GREATER /// /// Initializes a new instance of the struct. /// @@ -59,9 +70,15 @@ public readonly ref struct ReadOnlyRefEnumerable [MethodImpl(MethodImplOptions.AggressiveInlining)] private ReadOnlyRefEnumerable(ReadOnlySpan span, int step) { +#if NET7_0_OR_GREATER + this.reference = ref MemoryMarshal.GetReference(span); + this.length = span.Length; +#else this.span = span; +#endif this.step = step; } +#endif /// /// Initializes a new instance of the struct. @@ -72,8 +89,14 @@ private ReadOnlyRefEnumerable(ReadOnlySpan span, int step) [MethodImpl(MethodImplOptions.AggressiveInlining)] internal ReadOnlyRefEnumerable(in T reference, int length, int step) { +#if NET7_0_OR_GREATER + this.reference = ref reference; + this.length = length; + this.step = step; +#else this.span = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(reference), length); this.step = step; +#endif } /// @@ -124,7 +147,9 @@ internal ReadOnlyRefEnumerable(object? instance, IntPtr offset, int length, int public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + get => this.length; +#elif NETSTANDARD2_1_OR_GREATER get => this.span.Length; #else get => this.length; @@ -149,7 +174,9 @@ public ref readonly T this[int index] ThrowHelper.ThrowIndexOutOfRangeException(); } -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + ref T r0 = ref Unsafe.AsRef(in this.reference); +#elif NETSTANDARD2_1_OR_GREATER ref T r0 = ref MemoryMarshal.GetReference(this.span); #else ref T r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.instance, this.offset); @@ -181,7 +208,9 @@ public ref readonly T this[Index index] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Enumerator GetEnumerator() { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + return new(in this.reference, this.length, this.step); +#elif NETSTANDARD2_1_OR_GREATER return new(this.span, this.step); #else return new(this.instance, this.offset, this.length, this.step); @@ -197,7 +226,26 @@ public Enumerator GetEnumerator() /// public void CopyTo(RefEnumerable destination) { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + if (this.step == 1) + { + destination.CopyFrom(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in this.reference), this.length)); + + return; + } + + if (destination.Step == 1) + { + CopyTo(MemoryMarshal.CreateSpan(ref destination.Reference, destination.Length)); + + return; + } + + ref T sourceRef = ref Unsafe.AsRef(in this.reference); + ref T destinationRef = ref destination.Reference; + int sourceLength = this.length; + int destinationLength = destination.Length; +#elif NETSTANDARD2_1_OR_GREATER if (this.step == 1) { destination.CopyFrom(this.span); @@ -238,7 +286,10 @@ public void CopyTo(RefEnumerable destination) /// Whether or not the operation was successful. public bool TryCopyTo(RefEnumerable destination) { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + int sourceLength = this.length; + int destinationLength = destination.Length; +#elif NETSTANDARD2_1_OR_GREATER int sourceLength = this.span.Length; int destinationLength = destination.Span.Length; #else @@ -265,7 +316,17 @@ public bool TryCopyTo(RefEnumerable destination) /// public void CopyTo(Span destination) { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + if (this.step == 1) + { + MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in this.reference), this.length).CopyTo(destination); + + return; + } + + ref T sourceRef = ref Unsafe.AsRef(in this.reference); + int length = this.length; +#elif NETSTANDARD2_1_OR_GREATER if (this.step == 1) { this.span.CopyTo(destination); @@ -296,7 +357,9 @@ public void CopyTo(Span destination) /// Whether or not the operation was successful. public bool TryCopyTo(Span destination) { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + int length = this.length; +#elif NETSTANDARD2_1_OR_GREATER int length = this.span.Length; #else int length = this.length; @@ -315,7 +378,9 @@ public bool TryCopyTo(Span destination) /// public T[] ToArray() { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + int length = this.length; +#elif NETSTANDARD2_1_OR_GREATER int length = this.span.Length; #else int length = this.length; @@ -341,7 +406,9 @@ public T[] ToArray() [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator ReadOnlyRefEnumerable(RefEnumerable enumerable) { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + return new(in enumerable.Reference, enumerable.Length, enumerable.Step); +#elif NETSTANDARD2_1_OR_GREATER return new(enumerable.Span, enumerable.Step); #else return new(enumerable.Instance, enumerable.Offset, enumerable.Length, enumerable.Step); @@ -353,7 +420,13 @@ public static implicit operator ReadOnlyRefEnumerable(RefEnumerable enumer /// public ref struct Enumerator { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + /// + private readonly ref readonly T reference; + + /// + private readonly int length; +#elif NETSTANDARD2_1_OR_GREATER /// private readonly ReadOnlySpan span; #else @@ -375,7 +448,22 @@ public ref struct Enumerator /// private int position; -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + /// + /// Initializes a new instance of the struct. + /// + /// The reference to the first item of the sequence. + /// The length of the sequence. + /// The distance between items in the sequence to enumerate. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Enumerator(in T reference, int length, int step) + { + this.reference = ref reference; + this.length = length; + this.step = step; + this.position = -1; + } +#elif NETSTANDARD2_1_OR_GREATER /// /// Initializes a new instance of the struct. /// @@ -411,7 +499,9 @@ internal Enumerator(object? instance, IntPtr offset, int length, int step) [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + return ++this.position < this.length; +#elif NETSTANDARD2_1_OR_GREATER return ++this.position < this.span.Length; #else return ++this.position < this.length; @@ -424,7 +514,9 @@ public readonly ref readonly T Current [MethodImpl(MethodImplOptions.AggressiveInlining)] get { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + ref T r0 = ref Unsafe.AsRef(in this.reference); +#elif NETSTANDARD2_1_OR_GREATER ref T r0 = ref this.span.DangerousGetReference(); #else ref T r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.instance, this.offset); diff --git a/src/CommunityToolkit.HighPerformance/Enumerables/ReadOnlySpanEnumerable{T}.cs b/src/CommunityToolkit.HighPerformance/Enumerables/ReadOnlySpanEnumerable{T}.cs index c2000b90..2f6b16a5 100644 --- a/src/CommunityToolkit.HighPerformance/Enumerables/ReadOnlySpanEnumerable{T}.cs +++ b/src/CommunityToolkit.HighPerformance/Enumerables/ReadOnlySpanEnumerable{T}.cs @@ -81,10 +81,22 @@ public readonly Item Current [EditorBrowsable(EditorBrowsableState.Never)] public readonly ref struct Item { +#if NET7_0_OR_GREATER + /// + /// The reference for the instance. + /// + private readonly ref readonly T reference; + + /// + /// The index of the current instance. + /// + private readonly int index; +#else /// /// The source instance. /// private readonly ReadOnlySpan span; +#endif #if NETSTANDARD2_1_OR_GREATER /// @@ -95,7 +107,12 @@ public readonly ref struct Item [MethodImpl(MethodImplOptions.AggressiveInlining)] public Item(ref T value, int index) { +#if NET7_0_OR_GREATER + this.reference = ref value; + this.index = index; +#else this.span = MemoryMarshal.CreateReadOnlySpan(ref value, index); +#endif } #else /// @@ -124,7 +141,9 @@ public ref readonly T Value [MethodImpl(MethodImplOptions.AggressiveInlining)] get { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + return ref this.reference; +#elif NETSTANDARD2_1_OR_GREATER return ref MemoryMarshal.GetReference(this.span); #else ref T r0 = ref MemoryMarshal.GetReference(this.span); @@ -143,7 +162,9 @@ public int Index [MethodImpl(MethodImplOptions.AggressiveInlining)] get { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + return this.index; +#elif NETSTANDARD2_1_OR_GREATER return this.span.Length; #else return this.index; diff --git a/src/CommunityToolkit.HighPerformance/Enumerables/RefEnumerable{T}.cs b/src/CommunityToolkit.HighPerformance/Enumerables/RefEnumerable{T}.cs index 4713d11a..a6fab2ab 100644 --- a/src/CommunityToolkit.HighPerformance/Enumerables/RefEnumerable{T}.cs +++ b/src/CommunityToolkit.HighPerformance/Enumerables/RefEnumerable{T}.cs @@ -21,7 +21,17 @@ namespace CommunityToolkit.HighPerformance.Enumerables; /// The type of items to enumerate. public readonly ref struct RefEnumerable { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + /// + /// The reference for the instance. + /// + internal readonly ref T Reference; + + /// + /// The length of the current sequence. + /// + private readonly int length; +#elif NETSTANDARD2_1_OR_GREATER /// /// The instance pointing to the first item in the target memory area. /// @@ -55,7 +65,12 @@ public readonly ref struct RefEnumerable [MethodImpl(MethodImplOptions.AggressiveInlining)] internal RefEnumerable(ref T reference, int length, int step) { +#if NET7_0_OR_GREATER + this.Reference = ref reference; + this.length = length; +#else this.Span = MemoryMarshal.CreateSpan(ref reference, length); +#endif this.Step = step; } @@ -108,7 +123,9 @@ internal RefEnumerable(object? instance, IntPtr offset, int length, int step) public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + get => this.length; +#elif NETSTANDARD2_1_OR_GREATER get => this.Span.Length; #else get; @@ -133,7 +150,9 @@ public ref T this[int index] ThrowHelper.ThrowIndexOutOfRangeException(); } -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + ref T r0 = ref this.Reference; +#elif NETSTANDARD2_1_OR_GREATER ref T r0 = ref MemoryMarshal.GetReference(this.Span); #else ref T r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.Instance, this.Offset); @@ -165,7 +184,9 @@ public ref T this[Index index] [MethodImpl(MethodImplOptions.AggressiveInlining)] public Enumerator GetEnumerator() { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + return new(ref this.Reference, this.length, this.Step); +#elif NETSTANDARD2_1_OR_GREATER return new(this.Span, this.Step); #else return new(this.Instance, this.Offset, this.Length, this.Step); @@ -177,8 +198,18 @@ public Enumerator GetEnumerator() /// public void Clear() { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER // Fast path for contiguous items + if (this.Step == 1) + { + MemoryMarshal.CreateSpan(ref this.Reference, this.length).Clear(); + + return; + } + + ref T r0 = ref this.Reference; + int length = this.length; +#elif NETSTANDARD2_1_OR_GREATER if (this.Step == 1) { this.Span.Clear(); @@ -205,7 +236,26 @@ public void Clear() /// public void CopyTo(RefEnumerable destination) { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + if (this.Step == 1) + { + destination.CopyFrom(MemoryMarshal.CreateReadOnlySpan(ref this.Reference, this.length)); + + return; + } + + if (destination.Step == 1) + { + CopyTo(MemoryMarshal.CreateSpan(ref destination.Reference, destination.length)); + + return; + } + + ref T sourceRef = ref this.Reference; + ref T destinationRef = ref destination.Reference; + int sourceLength = this.length; + int destinationLength = destination.length; +#elif NETSTANDARD2_1_OR_GREATER if (this.Step == 1) { destination.CopyFrom(this.Span); @@ -246,7 +296,10 @@ public void CopyTo(RefEnumerable destination) /// Whether or not the operation was successful. public bool TryCopyTo(RefEnumerable destination) { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + int sourceLength = this.length; + int destinationLength = destination.length; +#elif NETSTANDARD2_1_OR_GREATER int sourceLength = this.Span.Length; int destinationLength = destination.Span.Length; #else @@ -273,7 +326,17 @@ public bool TryCopyTo(RefEnumerable destination) /// public void CopyTo(Span destination) { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + if (this.Step == 1) + { + MemoryMarshal.CreateReadOnlySpan(ref this.Reference, this.length).CopyTo(destination); + + return; + } + + ref T sourceRef = ref this.Reference; + int length = this.length; +#elif NETSTANDARD2_1_OR_GREATER if (this.Step == 1) { this.Span.CopyTo(destination); @@ -304,7 +367,9 @@ public void CopyTo(Span destination) /// Whether or not the operation was successful. public bool TryCopyTo(Span destination) { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + int length = this.length; +#elif NETSTANDARD2_1_OR_GREATER int length = this.Span.Length; #else int length = this.Length; @@ -329,7 +394,17 @@ public bool TryCopyTo(Span destination) /// internal void CopyFrom(ReadOnlySpan source) { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + if (this.Step == 1) + { + source.CopyTo(MemoryMarshal.CreateSpan(ref this.Reference, this.length)); + + return; + } + + ref T destinationRef = ref this.Reference; + int destinationLength = this.length; +#elif NETSTANDARD2_1_OR_GREATER if (this.Step == 1) { source.CopyTo(this.Span); @@ -361,7 +436,9 @@ internal void CopyFrom(ReadOnlySpan source) /// Whether or not the operation was successful. public bool TryCopyFrom(ReadOnlySpan source) { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + int length = this.length; +#elif NETSTANDARD2_1_OR_GREATER int length = this.Span.Length; #else int length = this.Length; @@ -383,7 +460,17 @@ public bool TryCopyFrom(ReadOnlySpan source) /// The value to assign to each element of the instance. public void Fill(T value) { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + if (this.Step == 1) + { + MemoryMarshal.CreateSpan(ref this.Reference, this.length).Fill(value); + + return; + } + + ref T r0 = ref this.Reference; + int length = this.length; +#elif NETSTANDARD2_1_OR_GREATER if (this.Step == 1) { this.Span.Fill(value); @@ -411,7 +498,9 @@ public void Fill(T value) /// public T[] ToArray() { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + int length = this.length; +#elif NETSTANDARD2_1_OR_GREATER int length = this.Span.Length; #else int length = this.Length; @@ -435,7 +524,13 @@ public T[] ToArray() /// public ref struct Enumerator { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + /// + private readonly ref T reference; + + /// + private readonly int length; +#elif NETSTANDARD2_1_OR_GREATER /// private readonly Span span; #else @@ -457,7 +552,22 @@ public ref struct Enumerator /// private int position; -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + /// + /// Initializes a new instance of the struct. + /// + /// The reference to the first item of the sequence. + /// The length of the sequence. + /// The distance between items in the sequence to enumerate. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Enumerator(ref T reference, int length, int step) + { + this.reference = ref reference; + this.length = length; + this.step = step; + this.position = -1; + } +#elif NETSTANDARD2_1_OR_GREATER /// /// Initializes a new instance of the struct. /// @@ -493,7 +603,9 @@ internal Enumerator(object? instance, IntPtr offset, int length, int step) [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + return ++this.position < this.length; +#elif NETSTANDARD2_1_OR_GREATER return ++this.position < this.span.Length; #else return ++this.position < this.length; @@ -506,7 +618,9 @@ public readonly ref T Current [MethodImpl(MethodImplOptions.AggressiveInlining)] get { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + ref T r0 = ref this.reference; +#elif NETSTANDARD2_1_OR_GREATER ref T r0 = ref this.span.DangerousGetReference(); #else ref T r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.instance, this.offset); diff --git a/src/CommunityToolkit.HighPerformance/Enumerables/SpanEnumerable{T}.cs b/src/CommunityToolkit.HighPerformance/Enumerables/SpanEnumerable{T}.cs index 68b84e98..dcfce51c 100644 --- a/src/CommunityToolkit.HighPerformance/Enumerables/SpanEnumerable{T}.cs +++ b/src/CommunityToolkit.HighPerformance/Enumerables/SpanEnumerable{T}.cs @@ -86,10 +86,22 @@ public readonly Item Current [EditorBrowsable(EditorBrowsableState.Never)] public readonly ref struct Item { +#if NET7_0_OR_GREATER + /// + /// The reference for the instance. + /// + private readonly ref T reference; + + /// + /// The index of the current instance. + /// + private readonly int index; +#else /// /// The source instance. /// private readonly Span span; +#endif #if NETSTANDARD2_1_OR_GREATER /// @@ -100,7 +112,12 @@ public readonly ref struct Item [MethodImpl(MethodImplOptions.AggressiveInlining)] public Item(ref T value, int index) { +#if NET7_0_OR_GREATER + this.reference = ref value; + this.index = index; +#else this.span = MemoryMarshal.CreateSpan(ref value, index); +#endif } #else /// @@ -121,15 +138,17 @@ public Item(Span span, int index) } #endif - /// - /// Gets the reference to the current value. - /// + /// + /// Gets the reference to the current value. + /// public ref T Value { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + return ref this.reference; +#elif NETSTANDARD2_1_OR_GREATER return ref MemoryMarshal.GetReference(this.span); #else ref T r0 = ref MemoryMarshal.GetReference(this.span); @@ -148,7 +167,9 @@ public int Index [MethodImpl(MethodImplOptions.AggressiveInlining)] get { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + return this.index; +#elif NETSTANDARD2_1_OR_GREATER return this.span.Length; #else return this.index; diff --git a/src/CommunityToolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.Enumerator.cs b/src/CommunityToolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.Enumerator.cs index c84b5acd..6b55632b 100644 --- a/src/CommunityToolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.Enumerator.cs +++ b/src/CommunityToolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.Enumerator.cs @@ -2,13 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if !NET7_0_OR_GREATER using System; +#endif using System.Runtime.CompilerServices; using CommunityToolkit.HighPerformance.Enumerables; using CommunityToolkit.HighPerformance.Memory.Internals; -#if NETSTANDARD2_1_OR_GREATER +#if NETSTANDARD2_1_OR_GREATER && !NET7_0_OR_GREATER using System.Runtime.InteropServices; -#else +#elif NETSTANDARD2_0 using RuntimeHelpers = CommunityToolkit.HighPerformance.Helpers.Internals.RuntimeHelpers; #endif @@ -84,7 +86,17 @@ public ReadOnlyRefEnumerable GetColumn(int column) /// public ref struct Enumerator { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + /// + /// The reference for the instance. + /// + private readonly ref readonly T reference; + + /// + /// The height of the specified 2D region. + /// + private readonly int height; +#elif NETSTANDARD2_1_OR_GREATER /// /// The instance pointing to the first item in the target memory area. /// @@ -133,7 +145,10 @@ public ref struct Enumerator /// The target instance to enumerate. internal Enumerator(ReadOnlySpan2D span) { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + this.reference = ref span.reference; + this.height = span.height; +#elif NETSTANDARD2_1_OR_GREATER this.span = span.span; #else this.instance = span.instance; @@ -167,7 +182,9 @@ public bool MoveNext() // another row available: wrap to a new line and continue. this.x = 0; -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + return ++this.y < this.height; +#elif NETSTANDARD2_1_OR_GREATER return ++this.y < this.span.Length; #else return ++this.y < this.height; @@ -182,7 +199,9 @@ public readonly ref readonly T Current [MethodImpl(MethodImplOptions.AggressiveInlining)] get { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + ref T r0 = ref Unsafe.AsRef(in this.reference); +#elif NETSTANDARD2_1_OR_GREATER ref T r0 = ref MemoryMarshal.GetReference(this.span); #else ref T r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.instance, this.offset); diff --git a/src/CommunityToolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.cs b/src/CommunityToolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.cs index c5fe720f..e824c22b 100644 --- a/src/CommunityToolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.cs +++ b/src/CommunityToolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.cs @@ -28,7 +28,17 @@ namespace CommunityToolkit.HighPerformance; [DebuggerDisplay("{ToString(),raw}")] public readonly ref partial struct ReadOnlySpan2D { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + /// + /// The reference for the instance. + /// + private readonly ref readonly T reference; + + /// + /// The height of the specified 2D region. + /// + private readonly int height; +#elif NETSTANDARD2_1_OR_GREATER /// /// The instance pointing to the first item in the target memory area. /// @@ -71,7 +81,12 @@ public readonly ref partial struct ReadOnlySpan2D [MethodImpl(MethodImplOptions.AggressiveInlining)] internal ReadOnlySpan2D(in T value, int height, int width, int pitch) { +#if NET7_0_OR_GREATER + this.reference = ref value; + this.height = height; +#else this.span = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(value), height); +#endif this.width = width; this.stride = width + pitch; } @@ -109,7 +124,10 @@ public unsafe ReadOnlySpan2D(void* pointer, int height, int width, int pitch) OverflowHelper.EnsureIsInNativeIntRange(height, width, pitch); -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + this.reference = ref Unsafe.AsRef(pointer); + this.height = height; +#elif NETSTANDARD2_1_OR_GREATER this.span = new ReadOnlySpan(pointer, height); #else this.instance = null; @@ -206,7 +224,10 @@ public ReadOnlySpan2D(T[] array, int offset, int height, int width, int pitch) ThrowHelper.ThrowArgumentException(); } -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + this.reference = ref array.DangerousGetReferenceAt(offset); + this.height = height; +#elif NETSTANDARD2_1_OR_GREATER this.span = MemoryMarshal.CreateReadOnlySpan(ref array.DangerousGetReferenceAt(offset), height); #else this.instance = array; @@ -230,7 +251,10 @@ public ReadOnlySpan2D(T[,]? array) return; } -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + this.reference = ref array.DangerousGetReference(); + this.height = array.GetLength(0); +#elif NETSTANDARD2_1_OR_GREATER this.span = MemoryMarshal.CreateReadOnlySpan(ref array.DangerousGetReference(), array.GetLength(0)); #else this.instance = array; @@ -289,7 +313,10 @@ public ReadOnlySpan2D(T[,]? array, int row, int column, int height, int width) ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); } -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + this.reference = ref array.DangerousGetReferenceAt(row, column); + this.height = height; +#elif NETSTANDARD2_1_OR_GREATER this.span = MemoryMarshal.CreateReadOnlySpan(ref array.DangerousGetReferenceAt(row, column), height); #else this.instance = array; @@ -313,7 +340,10 @@ public ReadOnlySpan2D(T[,,] array, int depth) ThrowHelper.ThrowArgumentOutOfRangeExceptionForDepth(); } -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + this.reference = ref array.DangerousGetReferenceAt(depth, 0, 0); + this.height = array.GetLength(1); +#elif NETSTANDARD2_1_OR_GREATER this.span = MemoryMarshal.CreateReadOnlySpan(ref array.DangerousGetReferenceAt(depth, 0, 0), array.GetLength(1)); #else this.instance = array; @@ -363,7 +393,10 @@ public ReadOnlySpan2D(T[,,] array, int depth, int row, int column, int height, i ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); } -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + this.reference = ref array.DangerousGetReferenceAt(depth, row, column); + this.height = height; +#elif NETSTANDARD2_1_OR_GREATER this.span = MemoryMarshal.CreateReadOnlySpan(ref array.DangerousGetReferenceAt(depth, row, column), height); #else this.instance = array; @@ -441,7 +474,12 @@ internal ReadOnlySpan2D(ReadOnlySpan span, int offset, int height, int width, ThrowHelper.ThrowArgumentException(); } +#if NET7_0_OR_GREATER + this.reference = ref span.DangerousGetReferenceAt(offset); + this.height = height; +#else this.span = MemoryMarshal.CreateSpan(ref span.DangerousGetReferenceAt(offset), height); +#endif this.width = width; this.stride = width + pitch; } @@ -509,7 +547,9 @@ public int Height [MethodImpl(MethodImplOptions.AggressiveInlining)] get { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + return this.height; +#elif NETSTANDARD2_1_OR_GREATER return this.span.Length; #else return this.height; @@ -740,13 +780,15 @@ public bool TryCopyTo(Span2D destination) /// A reference to the 0th element, or a reference. [MethodImpl(MethodImplOptions.AggressiveInlining)] [EditorBrowsable(EditorBrowsableState.Never)] - public unsafe ref T GetPinnableReference() + public unsafe ref readonly T GetPinnableReference() { - ref T r0 = ref Unsafe.AsRef(null); + ref readonly T r0 = ref Unsafe.AsRef(null); if (Length != 0) { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + r0 = ref this.reference; +#elif NETSTANDARD2_1_OR_GREATER r0 = ref MemoryMarshal.GetReference(this.span); #else r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.instance, this.offset); @@ -763,7 +805,9 @@ public unsafe ref T GetPinnableReference() [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T DangerousGetReference() { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + return ref Unsafe.AsRef(in this.reference); +#elif NETSTANDARD2_1_OR_GREATER return ref MemoryMarshal.GetReference(this.span); #else return ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.instance, this.offset); @@ -779,7 +823,9 @@ public ref T DangerousGetReference() [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T DangerousGetReferenceAt(int i, int j) { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + ref T r0 = ref Unsafe.AsRef(in this.reference); +#elif NETSTANDARD2_1_OR_GREATER ref T r0 = ref MemoryMarshal.GetReference(this.span); #else ref T r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.instance, this.offset); @@ -826,7 +872,11 @@ public unsafe ReadOnlySpan2D Slice(int row, int column, int height, int width nint shift = ((nint)(uint)this.stride * (nint)(uint)row) + (nint)(uint)column; int pitch = this.stride - width; -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + ref T r0 = ref Unsafe.Add(ref Unsafe.AsRef(in this.reference), shift); + + return new(in r0, height, width, pitch); +#elif NETSTANDARD2_1_OR_GREATER ref T r0 = ref this.span.DangerousGetReferenceAt(shift); return new(in r0, height, width, pitch); @@ -868,7 +918,11 @@ public bool TryGetSpan(out ReadOnlySpan span) if (this.stride == this.width && Length <= int.MaxValue) { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + span = MemoryMarshal.CreateSpan(ref Unsafe.AsRef(in this.reference), (int)Length); + + return true; +#elif NETSTANDARD2_1_OR_GREATER span = MemoryMarshal.CreateReadOnlySpan(ref MemoryMarshal.GetReference(this.span), (int)Length); return true; @@ -979,7 +1033,10 @@ public override string ToString() public static bool operator ==(ReadOnlySpan2D left, ReadOnlySpan2D right) { return -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + Unsafe.AreSame(ref Unsafe.AsRef(in left.reference), ref Unsafe.AsRef(in right.reference)) && + left.height == right.height && +#elif NETSTANDARD2_1_OR_GREATER left.span == right.span && #else ReferenceEquals( diff --git a/src/CommunityToolkit.HighPerformance/Memory/Span2D{T}.Enumerator.cs b/src/CommunityToolkit.HighPerformance/Memory/Span2D{T}.Enumerator.cs index 77897568..eac6b07b 100644 --- a/src/CommunityToolkit.HighPerformance/Memory/Span2D{T}.Enumerator.cs +++ b/src/CommunityToolkit.HighPerformance/Memory/Span2D{T}.Enumerator.cs @@ -2,13 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if !NET7_0_OR_GREATER using System; +#endif using System.Runtime.CompilerServices; using CommunityToolkit.HighPerformance.Enumerables; using CommunityToolkit.HighPerformance.Memory.Internals; -#if NETSTANDARD2_1_OR_GREATER +#if NETSTANDARD2_1_OR_GREATER && !NET7_0_OR_GREATER using System.Runtime.InteropServices; -#else +#elif NETSTANDARD2_0 using RuntimeHelpers = CommunityToolkit.HighPerformance.Helpers.Internals.RuntimeHelpers; #endif @@ -84,7 +86,17 @@ public RefEnumerable GetColumn(int column) /// public ref struct Enumerator { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + /// + /// The reference for the instance. + /// + private readonly ref T reference; + + /// + /// The height of the specified 2D region. + /// + private readonly int height; +#elif NETSTANDARD2_1_OR_GREATER /// /// The instance pointing to the first item in the target memory area. /// @@ -133,7 +145,10 @@ public ref struct Enumerator /// The target instance to enumerate. internal Enumerator(Span2D span) { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + this.reference = ref span.reference; + this.height = span.height; +#elif NETSTANDARD2_1_OR_GREATER this.span = span.span; #else this.instance = span.Instance; @@ -167,7 +182,9 @@ public bool MoveNext() // another row available: wrap to a new line and continue. this.x = 0; -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + return ++this.y < this.height; +#elif NETSTANDARD2_1_OR_GREATER return ++this.y < this.span.Length; #else return ++this.y < this.height; @@ -182,7 +199,9 @@ public readonly ref T Current [MethodImpl(MethodImplOptions.AggressiveInlining)] get { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + ref T r0 = ref this.reference; +#elif NETSTANDARD2_1_OR_GREATER ref T r0 = ref MemoryMarshal.GetReference(this.span); #else ref T r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.instance, this.offset); diff --git a/src/CommunityToolkit.HighPerformance/Memory/Span2D{T}.cs b/src/CommunityToolkit.HighPerformance/Memory/Span2D{T}.cs index 851863a4..2a32c970 100644 --- a/src/CommunityToolkit.HighPerformance/Memory/Span2D{T}.cs +++ b/src/CommunityToolkit.HighPerformance/Memory/Span2D{T}.cs @@ -4,6 +4,7 @@ using System; using System.ComponentModel; +using System.Data.Common; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -55,7 +56,17 @@ public readonly ref partial struct Span2D // discontiguous row, so that any arbitrary memory locations // can be used to internally represent a 2D span. This gives // users much more flexibility when creating spans from data. -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + /// + /// The reference for the instance. + /// + private readonly ref T reference; + + /// + /// The height of the specified 2D region. + /// + private readonly int height; +#elif NETSTANDARD2_1_OR_GREATER /// /// The instance pointing to the first item in the target memory area. /// @@ -106,7 +117,12 @@ public readonly ref partial struct Span2D [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Span2D(ref T value, int height, int width, int pitch) { +#if NET7_0_OR_GREATER + this.reference = ref value; + this.height = height; +#else this.span = MemoryMarshal.CreateSpan(ref value, height); +#endif this.width = width; this.Stride = width + pitch; } @@ -144,7 +160,10 @@ public unsafe Span2D(void* pointer, int height, int width, int pitch) OverflowHelper.EnsureIsInNativeIntRange(height, width, pitch); -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + this.reference = ref Unsafe.AsRef(pointer); + this.height = height; +#elif NETSTANDARD2_1_OR_GREATER this.span = new Span(pointer, height); #else this.Instance = null; @@ -245,7 +264,10 @@ public Span2D(T[] array, int offset, int height, int width, int pitch) ThrowHelper.ThrowArgumentException(); } -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + this.reference = ref array.DangerousGetReferenceAt(offset); + this.height = height; +#elif NETSTANDARD2_1_OR_GREATER this.span = MemoryMarshal.CreateSpan(ref array.DangerousGetReferenceAt(offset), height); #else this.Instance = array; @@ -277,7 +299,10 @@ public Span2D(T[,]? array) ThrowHelper.ThrowArrayTypeMismatchException(); } -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + this.reference = ref array.DangerousGetReference(); + this.height = array.GetLength(0); +#elif NETSTANDARD2_1_OR_GREATER this.span = MemoryMarshal.CreateSpan(ref array.DangerousGetReference(), array.GetLength(0)); #else this.Instance = array; @@ -344,7 +369,10 @@ public Span2D(T[,]? array, int row, int column, int height, int width) ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); } -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + this.reference = ref array.DangerousGetReferenceAt(row, column); + this.height = height; +#elif NETSTANDARD2_1_OR_GREATER this.span = MemoryMarshal.CreateSpan(ref array.DangerousGetReferenceAt(row, column), height); #else this.Instance = array; @@ -376,7 +404,10 @@ public Span2D(T[,,] array, int depth) ThrowHelper.ThrowArgumentOutOfRangeExceptionForDepth(); } -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + this.reference = ref array.DangerousGetReferenceAt(depth, 0, 0); + this.height = array.GetLength(1); +#elif NETSTANDARD2_1_OR_GREATER this.span = MemoryMarshal.CreateSpan(ref array.DangerousGetReferenceAt(depth, 0, 0), array.GetLength(1)); #else this.Instance = array; @@ -434,7 +465,10 @@ public Span2D(T[,,] array, int depth, int row, int column, int height, int width ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); } -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + this.reference = ref array.DangerousGetReferenceAt(depth, row, column); + this.height = height; +#elif NETSTANDARD2_1_OR_GREATER this.span = MemoryMarshal.CreateSpan(ref array.DangerousGetReferenceAt(depth, row, column), height); #else this.Instance = array; @@ -512,7 +546,12 @@ internal Span2D(Span span, int offset, int height, int width, int pitch) ThrowHelper.ThrowArgumentException(); } +#if NET7_0_OR_GREATER + this.reference = ref span.DangerousGetReferenceAt(offset); + this.height = height; +#else this.span = MemoryMarshal.CreateSpan(ref span.DangerousGetReferenceAt(offset), height); +#endif this.width = width; this.Stride = width + pitch; } @@ -580,7 +619,9 @@ public int Height [MethodImpl(MethodImplOptions.AggressiveInlining)] get { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + return this.height; +#elif NETSTANDARD2_1_OR_GREATER return this.span.Length; #else return this.height; @@ -902,7 +943,9 @@ public unsafe ref T GetPinnableReference() if (Length != 0) { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + r0 = ref this.reference; +#elif NETSTANDARD2_1_OR_GREATER r0 = ref MemoryMarshal.GetReference(this.span); #else r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.Instance, this.Offset); @@ -919,7 +962,9 @@ public unsafe ref T GetPinnableReference() [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T DangerousGetReference() { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + return ref this.reference; +#elif NETSTANDARD2_1_OR_GREATER return ref MemoryMarshal.GetReference(this.span); #else return ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.Instance, this.Offset); @@ -935,7 +980,9 @@ public ref T DangerousGetReference() [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T DangerousGetReferenceAt(int i, int j) { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + ref T r0 = ref this.reference; +#elif NETSTANDARD2_1_OR_GREATER ref T r0 = ref MemoryMarshal.GetReference(this.span); #else ref T r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.Instance, this.Offset); @@ -982,7 +1029,11 @@ public unsafe Span2D Slice(int row, int column, int height, int width) nint shift = ((nint)(uint)this.Stride * (nint)(uint)row) + (nint)(uint)column; int pitch = this.Stride - width; -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + ref T r0 = ref Unsafe.Add(ref this.reference, shift); + + return new(ref r0, height, width, pitch); +#elif NETSTANDARD2_1_OR_GREATER ref T r0 = ref this.span.DangerousGetReferenceAt(shift); return new(ref r0, height, width, pitch); @@ -1024,7 +1075,11 @@ public bool TryGetSpan(out Span span) if (this.Stride == this.width && Length <= int.MaxValue) { -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + span = MemoryMarshal.CreateSpan(ref this.reference, (int)Length); + + return true; +#elif NETSTANDARD2_1_OR_GREATER span = MemoryMarshal.CreateSpan(ref MemoryMarshal.GetReference(this.span), (int)Length); return true; @@ -1135,7 +1190,10 @@ public override string ToString() public static bool operator ==(Span2D left, Span2D right) { return -#if NETSTANDARD2_1_OR_GREATER +#if NET7_0_OR_GREATER + Unsafe.AreSame(ref left.reference, ref right.reference) && + left.height == right.height && +#elif NETSTANDARD2_1_OR_GREATER left.span == right.span && #else ReferenceEquals( diff --git a/tests/CommunityToolkit.HighPerformance.UnitTests/Memory/Test_ReadOnlySpan2D{T}.cs b/tests/CommunityToolkit.HighPerformance.UnitTests/Memory/Test_ReadOnlySpan2D{T}.cs index 2b527c18..4089b74f 100644 --- a/tests/CommunityToolkit.HighPerformance.UnitTests/Memory/Test_ReadOnlySpan2D{T}.cs +++ b/tests/CommunityToolkit.HighPerformance.UnitTests/Memory/Test_ReadOnlySpan2D{T}.cs @@ -351,7 +351,7 @@ public unsafe void Test_ReadOnlySpan2DT_GetPinnableReference() { Assert.IsTrue(Unsafe.AreSame( ref Unsafe.AsRef(null), - ref ReadOnlySpan2D.Empty.GetPinnableReference())); + ref Unsafe.AsRef(in ReadOnlySpan2D.Empty.GetPinnableReference()))); int[,] array = { @@ -361,7 +361,7 @@ ref Unsafe.AsRef(null), ReadOnlySpan2D span2d = new(array); - ref int r0 = ref span2d.GetPinnableReference(); + ref int r0 = ref Unsafe.AsRef(in span2d.GetPinnableReference()); Assert.IsTrue(Unsafe.AreSame(ref r0, ref array[0, 0])); }