-
-
Save DaZombieKiller/585163a01e78f03b31baa304a2aa2e28 to your computer and use it in GitHub Desktop.
(ReadOnly)SpanView<T> and (ReadOnly)MemoryView<T>
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#nullable enable | |
using System; | |
using System.Runtime.CompilerServices; | |
namespace Microsoft.Toolkit.HighPerformance.Memory | |
{ | |
// Most of these methods are all essentially copies of the methods in RefEnumerableHelper, | |
// with the difference that these versions call Unsafe.AddByteOffset instead of Unsafe.Add. | |
static class MemoryViewHelper | |
{ | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public static unsafe int GetLength<T>(int stride, int byteLength) | |
where T : unmanaged | |
{ | |
return Math.DivRem(byteLength, stride, out int rem) + (rem >= sizeof(T) ? 1 : 0); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public static unsafe int GetByteLength<T>(int stride, int length) | |
where T : unmanaged | |
{ | |
if (length == 0) | |
return 0; | |
if (length == 1) | |
return sizeof(T); | |
return sizeof(T) + (length - 1) * stride; | |
} | |
public static void Clear<T>(ref T r0, nint length, nint stride) | |
{ | |
nint offset = 0; | |
while (length >= 8) | |
{ | |
Unsafe.AddByteOffset(ref r0, offset) = default!; | |
Unsafe.AddByteOffset(ref r0, offset += stride) = default!; | |
Unsafe.AddByteOffset(ref r0, offset += stride) = default!; | |
Unsafe.AddByteOffset(ref r0, offset += stride) = default!; | |
Unsafe.AddByteOffset(ref r0, offset += stride) = default!; | |
Unsafe.AddByteOffset(ref r0, offset += stride) = default!; | |
Unsafe.AddByteOffset(ref r0, offset += stride) = default!; | |
Unsafe.AddByteOffset(ref r0, offset += stride) = default!; | |
length -= 8; | |
offset += stride; | |
} | |
if (length > 4) | |
{ | |
Unsafe.AddByteOffset(ref r0, offset) = default!; | |
Unsafe.AddByteOffset(ref r0, offset += stride) = default!; | |
Unsafe.AddByteOffset(ref r0, offset += stride) = default!; | |
Unsafe.AddByteOffset(ref r0, offset += stride) = default!; | |
length -= 4; | |
offset += stride; | |
} | |
while (length > 0) | |
{ | |
Unsafe.AddByteOffset(ref r0, offset) = default!; | |
length -= 1; | |
offset += stride; | |
} | |
} | |
public static void CopyTo<T>(ref T sourceRef, ref T destinationRef, nint length, nint sourceStride) | |
{ | |
nint | |
sourceOffset = 0, | |
destinationOffset = 0; | |
while (length >= 8) | |
{ | |
Unsafe.Add(ref destinationRef, destinationOffset + 0) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset); | |
Unsafe.Add(ref destinationRef, destinationOffset + 1) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset += sourceStride); | |
Unsafe.Add(ref destinationRef, destinationOffset + 2) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset += sourceStride); | |
Unsafe.Add(ref destinationRef, destinationOffset + 3) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset += sourceStride); | |
Unsafe.Add(ref destinationRef, destinationOffset + 4) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset += sourceStride); | |
Unsafe.Add(ref destinationRef, destinationOffset + 5) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset += sourceStride); | |
Unsafe.Add(ref destinationRef, destinationOffset + 6) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset += sourceStride); | |
Unsafe.Add(ref destinationRef, destinationOffset + 7) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset += sourceStride); | |
length -= 8; | |
sourceOffset += sourceStride; | |
destinationOffset += 8; | |
} | |
if (length >= 4) | |
{ | |
Unsafe.Add(ref destinationRef, destinationOffset + 0) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset); | |
Unsafe.Add(ref destinationRef, destinationOffset + 1) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset += sourceStride); | |
Unsafe.Add(ref destinationRef, destinationOffset + 2) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset += sourceStride); | |
Unsafe.Add(ref destinationRef, destinationOffset + 3) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset += sourceStride); | |
length -= 4; | |
sourceOffset += sourceStride; | |
destinationOffset += 4; | |
} | |
while (length > 0) | |
{ | |
Unsafe.Add(ref destinationRef, destinationOffset) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset); | |
length -= 1; | |
sourceOffset += sourceStride; | |
destinationOffset += 1; | |
} | |
} | |
public static void CopyTo<T>(ref T sourceRef, ref T destinationRef, nint length, nint sourceStride, nint destinationStride) | |
{ | |
nint | |
sourceOffset = 0, | |
destinationOffset = 0; | |
while (length >= 8) | |
{ | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset); | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset += destinationStride) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset += sourceStride); | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset += destinationStride) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset += sourceStride); | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset += destinationStride) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset += sourceStride); | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset += destinationStride) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset += sourceStride); | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset += destinationStride) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset += sourceStride); | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset += destinationStride) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset += sourceStride); | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset += destinationStride) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset += sourceStride); | |
length -= 8; | |
sourceOffset += sourceStride; | |
destinationOffset += destinationStride; | |
} | |
if (length >= 4) | |
{ | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset); | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset += destinationStride) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset += sourceStride); | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset += destinationStride) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset += sourceStride); | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset += destinationStride) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset += sourceStride); | |
length -= 4; | |
sourceOffset += sourceStride; | |
destinationOffset += destinationStride; | |
} | |
while (length > 0) | |
{ | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset) = Unsafe.AddByteOffset(ref sourceRef, sourceOffset); | |
length -= 1; | |
sourceOffset += sourceStride; | |
destinationOffset += destinationStride; | |
} | |
} | |
public static void CopyFrom<T>(ref T sourceRef, ref T destinationRef, nint length, nint destinationStride) | |
{ | |
nint | |
sourceOffset = 0, | |
destinationOffset = 0; | |
while (length >= 8) | |
{ | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset) = Unsafe.Add(ref sourceRef, sourceOffset); | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset += destinationStride) = Unsafe.Add(ref sourceRef, sourceOffset + 1); | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset += destinationStride) = Unsafe.Add(ref sourceRef, sourceOffset + 2); | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset += destinationStride) = Unsafe.Add(ref sourceRef, sourceOffset + 3); | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset += destinationStride) = Unsafe.Add(ref sourceRef, sourceOffset + 4); | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset += destinationStride) = Unsafe.Add(ref sourceRef, sourceOffset + 5); | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset += destinationStride) = Unsafe.Add(ref sourceRef, sourceOffset + 6); | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset += destinationStride) = Unsafe.Add(ref sourceRef, sourceOffset + 7); | |
length -= 8; | |
sourceOffset += 8; | |
destinationOffset += destinationStride; | |
} | |
if (length >= 4) | |
{ | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset) = Unsafe.Add(ref sourceRef, sourceOffset); | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset += destinationStride) = Unsafe.Add(ref sourceRef, sourceOffset + 1); | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset += destinationStride) = Unsafe.Add(ref sourceRef, sourceOffset + 2); | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset += destinationStride) = Unsafe.Add(ref sourceRef, sourceOffset + 3); | |
length -= 4; | |
sourceOffset += 4; | |
destinationOffset += destinationStride; | |
} | |
while (length > 0) | |
{ | |
Unsafe.AddByteOffset(ref destinationRef, destinationOffset) = Unsafe.Add(ref sourceRef, sourceOffset); | |
length -= 1; | |
sourceOffset += 1; | |
destinationOffset += destinationStride; | |
} | |
} | |
public static void Fill<T>(ref T r0, nint length, nint stride, T value) | |
{ | |
nint offset = 0; | |
while (length >= 8) | |
{ | |
Unsafe.AddByteOffset(ref r0, offset) = value; | |
Unsafe.AddByteOffset(ref r0, offset += stride) = value; | |
Unsafe.AddByteOffset(ref r0, offset += stride) = value; | |
Unsafe.AddByteOffset(ref r0, offset += stride) = value; | |
Unsafe.AddByteOffset(ref r0, offset += stride) = value; | |
Unsafe.AddByteOffset(ref r0, offset += stride) = value; | |
Unsafe.AddByteOffset(ref r0, offset += stride) = value; | |
Unsafe.AddByteOffset(ref r0, offset += stride) = value; | |
length -= 8; | |
offset += stride; | |
} | |
if (length > 4) | |
{ | |
Unsafe.AddByteOffset(ref r0, offset) = value; | |
Unsafe.AddByteOffset(ref r0, offset += stride) = value; | |
Unsafe.AddByteOffset(ref r0, offset += stride) = value; | |
Unsafe.AddByteOffset(ref r0, offset += stride) = value; | |
length -= 4; | |
offset += stride; | |
} | |
while (length > 0) | |
{ | |
Unsafe.AddByteOffset(ref r0, offset) = value; | |
length -= 1; | |
offset += stride; | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace Microsoft.Toolkit.HighPerformance.Memory | |
{ | |
/// <summary>Provides methods to interoperate with <see cref="MemoryView{T}"/>, <see cref="ReadOnlyMemoryView{T}"/>, <see cref="SpanView{T}"/>, and <see cref="ReadOnlySpanView{T}"/>.</summary> | |
public static class MemoryViewMarshal | |
{ | |
/// <summary>Returns the underlying <see cref="Memory{T}"/> buffer of a <see cref="MemoryView{T}"/>.</summary> | |
public static Memory<byte> GetMemory<T>(MemoryView<T> view) | |
where T : unmanaged | |
{ | |
return view.Memory; | |
} | |
/// <summary>Returns the underlying <see cref="ReadOnlyMemory{T}"/> buffer of a <see cref="ReadOnlyMemoryView{T}"/>.</summary> | |
public static ReadOnlyMemory<byte> GetMemory<T>(ReadOnlyMemoryView<T> view) | |
where T : unmanaged | |
{ | |
return view.Memory; | |
} | |
/// <summary>Returns the underlying <see cref="Span{T}"/> buffer of a <see cref="SpanView{T}"/>.</summary> | |
public static Span<byte> GetSpan<T>(SpanView<T> view) | |
where T : unmanaged | |
{ | |
return view.Span; | |
} | |
/// <summary>Returns the underlying <see cref="ReadOnlySpan{T}"/> buffer of a <see cref="ReadOnlySpanView{T}"/>.</summary> | |
public static ReadOnlySpan<byte> GetSpan<T>(ReadOnlySpanView<T> view) | |
where T : unmanaged | |
{ | |
return view.Span; | |
} | |
/// <summary>Casts a memory view to a memory view of another type.</summary> | |
public static MemoryView<TTo> Cast<TFrom, TTo>(MemoryView<TFrom> view) | |
where TFrom : unmanaged | |
where TTo : unmanaged | |
{ | |
return new MemoryView<TTo>(view.Memory, 0, view.Stride); | |
} | |
/// <summary>Casts a read-only memory view to a read-only memory view of another type.</summary> | |
public static ReadOnlyMemoryView<TTo> Cast<TFrom, TTo>(ReadOnlyMemoryView<TFrom> view) | |
where TFrom : unmanaged | |
where TTo : unmanaged | |
{ | |
return new ReadOnlyMemoryView<TTo>(view.Memory, 0, view.Stride); | |
} | |
/// <summary>Casts a span view to a span view of another type.</summary> | |
public static SpanView<TTo> Cast<TFrom, TTo>(SpanView<TFrom> view) | |
where TFrom : unmanaged | |
where TTo : unmanaged | |
{ | |
return new SpanView<TTo>(view.Span, 0, view.Stride); | |
} | |
/// <summary>Casts a read-only span view to a read-only span view of another type.</summary> | |
public static ReadOnlySpanView<TTo> Cast<TFrom, TTo>(ReadOnlySpanView<TFrom> view) | |
where TFrom : unmanaged | |
where TTo : unmanaged | |
{ | |
return new ReadOnlySpanView<TTo>(view.Span, 0, view.Stride); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#nullable enable | |
using System; | |
using System.Buffers; | |
using System.ComponentModel; | |
using System.Runtime.InteropServices; | |
using System.Runtime.CompilerServices; | |
using Microsoft.Toolkit.HighPerformance.Extensions; | |
namespace Microsoft.Toolkit.HighPerformance.Memory | |
{ | |
/// <summary>Provides a view over a <see cref="Memory{T}"/> instance's data.</summary> | |
public readonly struct MemoryView<T> : IEquatable<MemoryView<T>> | |
where T : unmanaged | |
{ | |
readonly Memory<byte> memory; | |
internal Memory<byte> Memory => memory; | |
/// <summary>Returns the length of the current view.</summary> | |
public int Length { get; } | |
/// <summary>Returns the stride of the current view.</summary> | |
public int Stride { get; } | |
/// <summary>Returns a value that indicates whether the current <see cref="MemoryView{T}"/> is empty.</summary> | |
public bool IsEmpty => 0 >= (uint)Length; | |
/// <summary>Returns a <see cref="SpanView{T}"/> from the current instance.</summary> | |
public SpanView<T> SpanView => new SpanView<T>(memory.Span, 0, Stride, Length); | |
/// <summary>Returns an empty <see cref="MemoryView{T}"/> object.</summary> | |
public static MemoryView<T> Empty => default; | |
/// <summary>Returns a <see cref="MemoryView{T}"/> over a buffer, representing the values at a given offset from each element.</summary> | |
/// <remarks>The value referenced by <paramref name="field"/> must refer to a field relative to the first <typeparamref name="TBuffer"/> element in the buffer.</remarks> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public static unsafe MemoryView<T> DangerousCreate<TBuffer>(Memory<TBuffer> buffer, ref T field) | |
where TBuffer : unmanaged | |
{ | |
var memory = buffer.Cast<TBuffer, byte>(); | |
var offset = Unsafe.ByteOffset(ref MemoryMarshal.GetReference(memory.Span), ref Unsafe.As<T, byte>(ref field)); | |
return new MemoryView<T>(memory, (int)offset, sizeof(TBuffer)); | |
} | |
/// <summary>Returns a <see cref="MemoryView{T}"/> over a buffer, representing the values at a given offset from each element.</summary> | |
/// <remarks>The given offset must refer to a field relative to the first <typeparamref name="TBuffer"/> element in the buffer.</remarks> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public static unsafe MemoryView<T> DangerousCreate<TBuffer>(Memory<TBuffer> buffer, int offset) | |
where TBuffer : unmanaged | |
{ | |
return new MemoryView<T>(buffer.Cast<TBuffer, byte>(), offset, sizeof(TBuffer)); | |
} | |
/// <summary>Creates a new <see cref="MemoryView{T}"/> object from a specified buffer starting at a specified offset with stride.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public unsafe MemoryView(Memory<byte> memory, int offset, int stride) | |
{ | |
if (stride < sizeof(T)) | |
throw new ArgumentException(null, nameof(stride)); | |
this.memory = memory[offset..]; | |
Stride = stride; | |
Length = MemoryViewHelper.GetLength<T>(stride, this.memory.Length); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
internal unsafe MemoryView(Memory<byte> memory, int offset, int stride, int length) | |
{ | |
if (stride < sizeof(T)) | |
throw new ArgumentException(null, nameof(stride)); | |
if (length > MemoryViewHelper.GetLength<T>(stride, memory[offset..].Length)) | |
throw new ArgumentException(null, nameof(length)); | |
this.memory = memory[offset..]; | |
Stride = stride; | |
Length = length; | |
} | |
/// <summary>Forms a slice out of the current view that begins at a specified index.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public MemoryView<T> Slice(int start) | |
{ | |
if ((uint)start > (uint)Length) | |
throw new ArgumentOutOfRangeException(); | |
return new MemoryView<T>(memory, start * Stride, Stride, Length - start); | |
} | |
/// <summary>Forms a slice out of the current view starting at a specified index for a specified length.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public MemoryView<T> Slice(int start, int length) | |
{ | |
#if TARGET_64BIT | |
if ((nuint)start + (nuint)length > (nuint)Length) | |
throw new ArgumentOutOfRangeException(); | |
#else | |
if ((uint)start > (uint)Length || (uint)length > (uint)(Length - start)) | |
throw new ArgumentOutOfRangeException(); | |
#endif | |
return new MemoryView<T>(memory[..MemoryViewHelper.GetByteLength<T>(Stride, length)], start * Stride, Stride, length); | |
} | |
/// <summary>Copies the contents of this <see cref="MemoryView{T}"/> into a destination <see cref="MemoryView{T}"/>.</summary> | |
public void CopyTo(MemoryView<T> destination) | |
{ | |
SpanView.CopyTo(destination.SpanView); | |
} | |
/// <summary>Attempts to copy the current <see cref="MemoryView{T}"/> to a destination <see cref="MemoryView{T}"/> and returns a value that indicates whether the copy operation succeeded.</summary> | |
public bool TryCopyTo(MemoryView<T> destination) | |
{ | |
return SpanView.TryCopyTo(destination.SpanView); | |
} | |
/// <summary>Copies the contents of this view into a new array.</summary> | |
public T[] ToArray() | |
{ | |
return SpanView.ToArray(); | |
} | |
/// <summary>Creates a handle for the underlying <see cref="Memory{T}"/> object.</summary> | |
public MemoryHandle Pin() | |
{ | |
return memory.Pin(); | |
} | |
/// <summary>Determines whether the specified <see cref="MemoryView{T}"/> object is equal to the current object.</summary> | |
public bool Equals(MemoryView<T> other) | |
{ | |
return SpanView.Equals(other.SpanView); | |
} | |
/// <summary>Defines an implicit conversion of a <see cref="Memory{T}"/> to a <see cref="MemoryView{T}"/>.</summary> | |
public static unsafe implicit operator MemoryView<T>(Memory<T> memory) | |
{ | |
return new MemoryView<T>(memory.Cast<T, byte>(), 0, sizeof(T), memory.Length); | |
} | |
/// <summary>Returns a value that indicates whether two <see cref="MemoryView{T}"/> objects are equal.</summary> | |
public static bool operator ==(MemoryView<T> left, MemoryView<T> right) | |
{ | |
return left.Equals(right); | |
} | |
/// <summary>Returns a value that indicates whether two <see cref="MemoryView{T}"/> objects are not equal.</summary> | |
public static bool operator !=(MemoryView<T> left, MemoryView<T> right) | |
{ | |
return !left.Equals(right); | |
} | |
/// <summary>Determines whether the specified object is equal to the current object.</summary> | |
[EditorBrowsable(EditorBrowsableState.Never)] | |
public override bool Equals(object obj) | |
{ | |
if (obj is MemoryView<T> view) | |
return Equals(view.memory); | |
else if (obj is ReadOnlyMemoryView<T> readView) | |
return readView.Equals(this); | |
else | |
return false; | |
} | |
/// <summary>Returns the hash code for this instance.</summary> | |
[EditorBrowsable(EditorBrowsableState.Never)] | |
public override int GetHashCode() | |
{ | |
return memory.GetHashCode(); | |
} | |
/// <summary>Returns the string representation of this <see cref="MemoryView{T}"/> object.</summary> | |
public override string ToString() | |
{ | |
if (typeof(T) == typeof(char)) | |
return SpanView.ToString(); | |
return string.Format(nameof(MemoryView<T>) + "<{0}>[{1}]", typeof(T).Name, Length); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#nullable enable | |
using System; | |
using System.Buffers; | |
using System.ComponentModel; | |
using System.Runtime.InteropServices; | |
using System.Runtime.CompilerServices; | |
using Microsoft.Toolkit.HighPerformance.Extensions; | |
namespace Microsoft.Toolkit.HighPerformance.Memory | |
{ | |
/// <summary>Provides a read-only view over a <see cref="ReadOnlyMemory{T}"/> instance's data.</summary> | |
public readonly struct ReadOnlyMemoryView<T> : IEquatable<ReadOnlyMemoryView<T>> | |
where T : unmanaged | |
{ | |
readonly ReadOnlyMemory<byte> memory; | |
internal ReadOnlyMemory<byte> Memory => memory; | |
/// <summary>Returns the length of the current view.</summary> | |
public int Length { get; } | |
/// <summary>Returns the stride of the current view.</summary> | |
public int Stride { get; } | |
/// <summary>Returns a value that indicates whether the current <see cref="ReadOnlyMemoryView{T}"/> is empty.</summary> | |
public bool IsEmpty => 0 >= (uint)Length; | |
/// <summary>Returns a <see cref="ReadOnlySpanView{T}"/> from the current instance.</summary> | |
public ReadOnlySpanView<T> SpanView => new ReadOnlySpanView<T>(memory.Span, 0, Stride, Length); | |
/// <summary>Returns an empty <see cref="ReadOnlyMemoryView{T}"/> object.</summary> | |
public static ReadOnlyMemoryView<T> Empty => default; | |
/// <summary>Returns a <see cref="ReadOnlyMemoryView{T}"/> over a buffer, representing the values at a given offset from each element.</summary> | |
/// <remarks>The value referenced by <paramref name="field"/> must refer to a field relative to the first <typeparamref name="TBuffer"/> element in the buffer.</remarks> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public static unsafe ReadOnlyMemoryView<T> DangerousCreate<TBuffer>(ReadOnlyMemory<TBuffer> buffer, in T field) | |
where TBuffer : unmanaged | |
{ | |
var memory = buffer.Cast<TBuffer, byte>(); | |
var offset = Unsafe.ByteOffset(ref MemoryMarshal.GetReference(memory.Span), ref Unsafe.As<T, byte>(ref Unsafe.AsRef(in field))); | |
return new ReadOnlyMemoryView<T>(memory, (int)offset, sizeof(TBuffer)); | |
} | |
/// <summary>Returns a <see cref="ReadOnlyMemoryView{T}"/> over a buffer, representing the values at a given offset from each element.</summary> | |
/// <remarks>The given offset must refer to a field relative to the first <typeparamref name="TBuffer"/> element in the buffer.</remarks> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public static unsafe ReadOnlyMemoryView<T> DangerousCreate<TBuffer>(ReadOnlyMemory<TBuffer> buffer, int offset) | |
where TBuffer : unmanaged | |
{ | |
return new ReadOnlyMemoryView<T>(buffer.Cast<TBuffer, byte>(), offset, sizeof(TBuffer)); | |
} | |
/// <summary>Creates a new <see cref="ReadOnlyMemoryView{T}"/> object from a specified buffer starting at a specified offset with stride.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public unsafe ReadOnlyMemoryView(ReadOnlyMemory<byte> memory, int offset, int stride) | |
{ | |
if (stride < sizeof(T)) | |
throw new ArgumentException(null, nameof(stride)); | |
this.memory = memory[offset..]; | |
Stride = stride; | |
Length = MemoryViewHelper.GetLength<T>(stride, this.memory.Length); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
internal unsafe ReadOnlyMemoryView(ReadOnlyMemory<byte> memory, int offset, int stride, int length) | |
{ | |
if (stride < sizeof(T)) | |
throw new ArgumentException(null, nameof(stride)); | |
if (length > MemoryViewHelper.GetLength<T>(stride, memory[offset..].Length)) | |
throw new ArgumentException(null, nameof(length)); | |
this.memory = memory[offset..]; | |
Stride = stride; | |
Length = length; | |
} | |
/// <summary>Forms a slice out of the current view that begins at a specified index.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public ReadOnlyMemoryView<T> Slice(int start) | |
{ | |
if ((uint)start > (uint)Length) | |
throw new ArgumentOutOfRangeException(); | |
return new ReadOnlyMemoryView<T>(memory, start * Stride, Stride, Length - start); | |
} | |
/// <summary>Forms a slice out of the current view starting at a specified index for a specified length.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public ReadOnlyMemoryView<T> Slice(int start, int length) | |
{ | |
#if TARGET_64BIT | |
if ((nuint)start + (nuint)length > (nuint)Length) | |
throw new ArgumentOutOfRangeException(); | |
#else | |
if ((uint)start > (uint)Length || (uint)length > (uint)(Length - start)) | |
throw new ArgumentOutOfRangeException(); | |
#endif | |
return new ReadOnlyMemoryView<T>(memory[..MemoryViewHelper.GetByteLength<T>(Stride, length)], start * Stride, Stride, length); | |
} | |
/// <summary>Copies the contents of this <see cref="ReadOnlyMemoryView{T}"/> into a destination <see cref="MemoryView{T}"/>.</summary> | |
public void CopyTo(MemoryView<T> destination) | |
{ | |
SpanView.CopyTo(destination.SpanView); | |
} | |
/// <summary>Attempts to copy the current <see cref="ReadOnlyMemoryView{T}"/> to a destination <see cref="MemoryView{T}"/> and returns a value that indicates whether the copy operation succeeded.</summary> | |
public bool TryCopyTo(MemoryView<T> destination) | |
{ | |
return SpanView.TryCopyTo(destination.SpanView); | |
} | |
/// <summary>Copies the contents of this view into a new array.</summary> | |
public T[] ToArray() | |
{ | |
return SpanView.ToArray(); | |
} | |
/// <summary>Creates a handle for the underlying <see cref="ReadOnlyMemory{T}"/> object.</summary> | |
public MemoryHandle Pin() | |
{ | |
return memory.Pin(); | |
} | |
/// <summary>Determines whether the specified <see cref="ReadOnlyMemoryView{T}"/> object is equal to the current object.</summary> | |
public bool Equals(ReadOnlyMemoryView<T> other) | |
{ | |
return SpanView.Equals(other.SpanView); | |
} | |
/// <summary>Defines an implicit conversion of a <see cref="ReadOnlyMemory{T}"/> to a <see cref="ReadOnlyMemoryView{T}"/>.</summary> | |
public static unsafe implicit operator ReadOnlyMemoryView<T>(ReadOnlyMemory<T> memory) | |
{ | |
return new ReadOnlyMemoryView<T>(memory.Cast<T, byte>(), 0, sizeof(T), memory.Length); | |
} | |
/// <summary>Defines an implicit conversion of a <see cref="Memory{T}"/> to a <see cref="ReadOnlyMemoryView{T}"/>.</summary> | |
public static unsafe implicit operator ReadOnlyMemoryView<T>(Memory<T> memory) | |
{ | |
return new ReadOnlyMemoryView<T>(memory.Cast<T, byte>(), 0, sizeof(T), memory.Length); | |
} | |
/// <summary>Defines an implicit conversion of a <see cref="MemoryView{T}"/> to a <see cref="ReadOnlyMemoryView{T}"/>.</summary> | |
public static implicit operator ReadOnlyMemoryView<T>(MemoryView<T> view) | |
{ | |
return new ReadOnlyMemoryView<T>(view.Memory, 0, view.Stride, view.Length); | |
} | |
/// <summary>Returns a value that indicates whether two <see cref="ReadOnlyMemoryView{T}"/> objects are equal.</summary> | |
public static bool operator ==(ReadOnlyMemoryView<T> left, ReadOnlyMemoryView<T> right) | |
{ | |
return left.Equals(right); | |
} | |
/// <summary>Returns a value that indicates whether two <see cref="ReadOnlyMemoryView{T}"/> objects are not equal.</summary> | |
public static bool operator !=(ReadOnlyMemoryView<T> left, ReadOnlyMemoryView<T> right) | |
{ | |
return !left.Equals(right); | |
} | |
/// <summary>Determines whether the specified object is equal to the current object.</summary> | |
[EditorBrowsable(EditorBrowsableState.Never)] | |
public override bool Equals(object obj) | |
{ | |
if (obj is MemoryView<T> view) | |
return Equals(view); | |
else if (obj is ReadOnlyMemoryView<T> readView) | |
return Equals(readView); | |
else | |
return false; | |
} | |
/// <summary>Returns the hash code for this instance.</summary> | |
[EditorBrowsable(EditorBrowsableState.Never)] | |
public override int GetHashCode() | |
{ | |
return memory.GetHashCode(); | |
} | |
/// <summary>Returns the string representation of this <see cref="MemoryView{T}"/> object.</summary> | |
public override string ToString() | |
{ | |
if (typeof(T) == typeof(char)) | |
return SpanView.ToString(); | |
return string.Format(nameof(ReadOnlyMemoryView<T>) + "<{0}>[{1}]", typeof(T).Name, Length); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#nullable enable | |
using System; | |
using System.ComponentModel; | |
using System.Runtime.InteropServices; | |
using System.Runtime.CompilerServices; | |
namespace Microsoft.Toolkit.HighPerformance.Memory | |
{ | |
/// <summary>Provides a read-only view over a <see cref="ReadOnlySpan{T}"/> instance's data.</summary> | |
public readonly ref struct ReadOnlySpanView<T> | |
where T : unmanaged | |
{ | |
readonly ReadOnlySpan<byte> span; | |
internal ReadOnlySpan<byte> Span => span; | |
/// <summary>Returns the length of the current view.</summary> | |
public int Length { get; } | |
/// <summary>Returns the stride of the current view.</summary> | |
public int Stride { get; } | |
/// <summary>Returns a value that indicates whether the current <see cref="ReadOnlySpanView{T}"/> is empty.</summary> | |
public bool IsEmpty => 0 >= (uint)Length; | |
/// <summary>Returns an empty <see cref="ReadOnlySpanView{T}"/> object.</summary> | |
public static ReadOnlySpanView<T> Empty => default; | |
/// <summary>Returns a <see cref="ReadOnlySpanView{T}"/> over a buffer, representing the values at a given offset from each element.</summary> | |
/// <remarks>The value referenced by <paramref name="field"/> must refer to a field relative to the first <typeparamref name="TBuffer"/> element in the buffer.</remarks> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public static unsafe ReadOnlySpanView<T> DangerousCreate<TBuffer>(ReadOnlySpan<TBuffer> buffer, in T field) | |
where TBuffer : unmanaged | |
{ | |
var span = MemoryMarshal.AsBytes(buffer); | |
var offset = Unsafe.ByteOffset(ref MemoryMarshal.GetReference(span), ref Unsafe.As<T, byte>(ref Unsafe.AsRef(in field))); | |
return new ReadOnlySpanView<T>(span, (int)offset, sizeof(TBuffer)); | |
} | |
/// <summary>Returns a <see cref="ReadOnlySpanView{T}"/> over a buffer, representing the values at a given offset from each element.</summary> | |
/// <remarks>The given offset must refer to a field relative to the first <typeparamref name="TBuffer"/> element in the buffer.</remarks> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public static unsafe ReadOnlySpanView<T> DangerousCreate<TBuffer>(ReadOnlySpan<TBuffer> buffer, int offset) | |
where TBuffer : unmanaged | |
{ | |
return new ReadOnlySpanView<T>(MemoryMarshal.AsBytes(buffer), offset, sizeof(TBuffer)); | |
} | |
/// <summary>Creates a new <see cref="ReadOnlySpanView{T}"/> object from a specified number of <typeparamref name="T"/> elements starting at a specified memory address with stride.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public unsafe ReadOnlySpanView(void* pointer, int stride, int length) | |
{ | |
if (stride < sizeof(T)) | |
throw new ArgumentException(null, nameof(stride)); | |
span = new ReadOnlySpan<byte>(pointer, MemoryViewHelper.GetByteLength<T>(stride, length)); | |
Stride = stride; | |
Length = length; | |
} | |
/// <summary>Creates a new <see cref="ReadOnlySpanView{T}"/> object from a specified buffer starting at a specified offset with stride.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public unsafe ReadOnlySpanView(ReadOnlySpan<byte> span, int offset, int stride) | |
{ | |
if (stride < sizeof(T)) | |
throw new ArgumentException(null, nameof(stride)); | |
this.span = span[offset..]; | |
Stride = stride; | |
Length = MemoryViewHelper.GetLength<T>(stride, this.span.Length); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
internal unsafe ReadOnlySpanView(ReadOnlySpan<byte> span, int offset, int stride, int length) | |
{ | |
if (stride < sizeof(T)) | |
throw new ArgumentException(null, nameof(stride)); | |
if (length > MemoryViewHelper.GetLength<T>(stride, span[offset..].Length)) | |
throw new ArgumentException(null, nameof(length)); | |
this.span = span[offset..]; | |
Stride = stride; | |
Length = length; | |
} | |
/// <summary>Gets the element at the specified zero-based index.</summary> | |
public ref readonly T this[int index] | |
{ | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
get | |
{ | |
if ((uint)index >= (uint)Length) | |
throw new IndexOutOfRangeException(); | |
return ref DangerousGetReferenceAt(index); | |
} | |
} | |
/// <summary>Returns an enumerator for this <see cref="ReadOnlySpanView{T}"/>.</summary> | |
public Enumerator GetEnumerator() | |
{ | |
return new Enumerator(this); | |
} | |
/// <summary>Forms a slice out of the current view that begins at a specified index.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public ReadOnlySpanView<T> Slice(int start) | |
{ | |
if ((uint)start > (uint)Length) | |
throw new ArgumentOutOfRangeException(); | |
return new ReadOnlySpanView<T>(span, start * Stride, Stride, Length - start); | |
} | |
/// <summary>Forms a slice out of the current view starting at a specified index for a specified length.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public ReadOnlySpanView<T> Slice(int start, int length) | |
{ | |
#if TARGET_64BIT | |
if ((nuint)start + (nuint)length > (nuint)Length) | |
throw new ArgumentOutOfRangeException(); | |
#else | |
if ((uint)start > (uint)Length || (uint)length > (uint)(Length - start)) | |
throw new ArgumentOutOfRangeException(); | |
#endif | |
return new ReadOnlySpanView<T>(span[..MemoryViewHelper.GetByteLength<T>(Stride, length)], start * Stride, Stride, length); | |
} | |
/// <summary>Copies the contents of this <see cref="ReadOnlySpanView{T}"/> into a destination <see cref="SpanView{T}"/>.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public void CopyTo(SpanView<T> destination) | |
{ | |
if (Length > destination.Length) | |
throw new ArgumentException(null, nameof(destination)); | |
MemoryViewHelper.CopyTo( | |
ref Unsafe.AsRef(in DangerousGetReference()), | |
ref destination.DangerousGetReference(), | |
Length, | |
Stride, | |
destination.Stride | |
); | |
} | |
/// <summary>Attempts to copy the current <see cref="ReadOnlySpanView{T}"/> to a destination <see cref="SpanView{T}"/> and returns a value that indicates whether the copy operation succeeded.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public bool TryCopyTo(SpanView<T> destination) | |
{ | |
if (Length > destination.Length) | |
return false; | |
CopyTo(destination); | |
return true; | |
} | |
/// <summary>Copies the contents of this <see cref="ReadOnlySpanView{T}"/> into a destination <see cref="Span{T}"/>.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public void CopyTo(Span<T> destination) | |
{ | |
if (Length > destination.Length) | |
throw new ArgumentException(null, nameof(destination)); | |
MemoryViewHelper.CopyTo( | |
ref Unsafe.AsRef(in DangerousGetReference()), | |
ref MemoryMarshal.GetReference(destination), | |
Length, | |
Stride | |
); | |
} | |
/// <summary>Attempts to copy the current <see cref="ReadOnlySpanView{T}"/> to a destination <see cref="Span{T}"/> and returns a value that indicates whether the copy operation succeeded.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public bool TryCopyTo(Span<T> destination) | |
{ | |
if (Length > destination.Length) | |
return false; | |
CopyTo(destination); | |
return true; | |
} | |
/// <summary>Returns a reference to the storage location of an element at index zero.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public ref readonly T DangerousGetReference() | |
{ | |
return ref Unsafe.As<byte, T>(ref MemoryMarshal.GetReference(span)); | |
} | |
/// <summary>Returns a reference to the storage location of an element at a given index.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public ref readonly T DangerousGetReferenceAt(int index) | |
{ | |
return ref Unsafe.AddByteOffset(ref Unsafe.As<byte, T>(ref MemoryMarshal.GetReference(span)), (nint)index * Stride); | |
} | |
/// <summary>Returns a reference to the element of the <see cref="ReadOnlySpanView{T}"/> at index zero.</summary> | |
[EditorBrowsable(EditorBrowsableState.Never)] | |
public ref readonly T GetPinnableReference() | |
{ | |
ref readonly T result = ref Unsafe.NullRef<T>(); | |
if (Length != 0) | |
result = ref DangerousGetReference(); | |
return ref result; | |
} | |
/// <summary>Determines whether the specified <see cref="ReadOnlySpanView{T}"/> object is equal to the current object.</summary> | |
public bool Equals(ReadOnlySpanView<T> other) | |
{ | |
return Length == other.Length && Unsafe.AreSame( | |
ref MemoryMarshal.GetReference(span), | |
ref MemoryMarshal.GetReference(other.span) | |
); | |
} | |
/// <summary>Calls to this method are not supported.</summary> | |
[EditorBrowsable(EditorBrowsableState.Never)] | |
public override bool Equals(object obj) | |
{ | |
throw new NotSupportedException(); | |
} | |
/// <summary>Calls to this method are not supported.</summary> | |
[EditorBrowsable(EditorBrowsableState.Never)] | |
public override int GetHashCode() | |
{ | |
throw new NotSupportedException(); | |
} | |
/// <summary>Returns the string representation of this <see cref="SpanView{T}"/> object.</summary> | |
public unsafe override string ToString() | |
{ | |
if (typeof(T) == typeof(char)) | |
{ | |
var result = new string('\0', Length); | |
fixed (char* p = result) | |
CopyTo(new Span<T>(p, Length)); | |
return result; | |
} | |
return string.Format(nameof(ReadOnlySpanView<T>) + "<{0}>[{1}]", typeof(T).Name, Length); | |
} | |
/// <summary>Copies the contents of this view into a new array.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public T[] ToArray() | |
{ | |
if (Length == 0) | |
return Array.Empty<T>(); | |
var array = new T[Length]; | |
CopyTo(array); | |
return array; | |
} | |
/// <summary>Defines an implicit conversion of a <see cref="SpanView{T}"/> to a <see cref="ReadOnlySpanView{T}"/>.</summary> | |
public static implicit operator ReadOnlySpanView<T>(SpanView<T> view) | |
{ | |
return new ReadOnlySpanView<T>(view.Span, 0, view.Stride, view.Length); | |
} | |
/// <summary>Defines an implicit conversion of a <see cref="ReadOnlySpan{T}"/> to a <see cref="ReadOnlySpanView{T}"/>.</summary> | |
public static unsafe implicit operator ReadOnlySpanView<T>(ReadOnlySpan<T> span) | |
{ | |
return new ReadOnlySpanView<T>(MemoryMarshal.AsBytes(span), 0, sizeof(T), span.Length); | |
} | |
/// <summary>Defines an implicit conversion of a <see cref="Span{T}"/> to a <see cref="ReadOnlySpanView{T}"/>.</summary> | |
public static unsafe implicit operator ReadOnlySpanView<T>(Span<T> span) | |
{ | |
return new ReadOnlySpanView<T>(MemoryMarshal.AsBytes(span), 0, sizeof(T), span.Length); | |
} | |
/// <summary>Returns a value that indicates whether two <see cref="ReadOnlySpanView{T}"/> objects are equal.</summary> | |
public static bool operator ==(ReadOnlySpanView<T> left, ReadOnlySpanView<T> right) | |
{ | |
return left.Equals(right); | |
} | |
/// <summary>Returns a value that indicates whether two <see cref="ReadOnlySpanView{T}"/> objects are not equal.</summary> | |
public static bool operator !=(ReadOnlySpanView<T> left, ReadOnlySpanView<T> right) | |
{ | |
return !(left == right); | |
} | |
/// <summary>Provides an enumerator for the elements of a <see cref="ReadOnlySpanView{T}"/>.</summary> | |
public ref struct Enumerator | |
{ | |
int position; | |
readonly ReadOnlySpanView<T> view; | |
/// <summary>Gets a reference to the item at the current position of the enumerator.</summary> | |
public ref readonly T Current | |
{ | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
get => ref view.DangerousGetReferenceAt(position); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
internal Enumerator(ReadOnlySpanView<T> view) | |
{ | |
this.view = view; | |
position = -1; | |
} | |
/// <summary>Advances the enumerator to the next item of the <see cref="ReadOnlySpanView{T}"/>.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public bool MoveNext() | |
{ | |
return ++position < view.Length; | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#nullable enable | |
using System; | |
using System.ComponentModel; | |
using System.Runtime.InteropServices; | |
using System.Runtime.CompilerServices; | |
namespace Microsoft.Toolkit.HighPerformance.Memory | |
{ | |
/// <summary>Provides a view over a <see cref="Span{T}"/> instance's data.</summary> | |
public readonly ref struct SpanView<T> | |
where T : unmanaged | |
{ | |
readonly Span<byte> span; | |
internal Span<byte> Span => span; | |
/// <summary>Returns the length of the current view.</summary> | |
public int Length { get; } | |
/// <summary>Returns the stride of the current view.</summary> | |
public int Stride { get; } | |
/// <summary>Returns a value that indicates whether the current <see cref="SpanView{T}"/> is empty.</summary> | |
public bool IsEmpty => 0 >= (uint)Length; | |
/// <summary>Returns an empty <see cref="SpanView{T}"/> object.</summary> | |
public static SpanView<T> Empty => default; | |
/// <summary>Returns a <see cref="SpanView{T}"/> over a buffer, representing the values at a given offset from each element.</summary> | |
/// <remarks>The value referenced by <paramref name="field"/> must refer to a field relative to the first <typeparamref name="TBuffer"/> element in the buffer.</remarks> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public static unsafe SpanView<T> DangerousCreate<TBuffer>(Span<TBuffer> buffer, ref T field) | |
where TBuffer : unmanaged | |
{ | |
var span = MemoryMarshal.AsBytes(buffer); | |
var offset = Unsafe.ByteOffset(ref MemoryMarshal.GetReference(span), ref Unsafe.As<T, byte>(ref field)); | |
return new SpanView<T>(span, (int)offset, sizeof(TBuffer)); | |
} | |
/// <summary>Returns a <see cref="SpanView{T}"/> over a buffer, representing the values at a given offset from each element.</summary> | |
/// <remarks>The given offset must refer to a field relative to the first <typeparamref name="TBuffer"/> element in the buffer.</remarks> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public static unsafe SpanView<T> DangerousCreate<TBuffer>(Span<TBuffer> buffer, int offset) | |
where TBuffer : unmanaged | |
{ | |
return new SpanView<T>(MemoryMarshal.AsBytes(buffer), offset, sizeof(TBuffer)); | |
} | |
/// <summary>Creates a new <see cref="SpanView{T}"/> object from a specified number of <typeparamref name="T"/> elements starting at a specified memory address with stride.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public unsafe SpanView(void* pointer, int stride, int length) | |
{ | |
if (stride < sizeof(T)) | |
throw new ArgumentException(null, nameof(stride)); | |
span = new Span<byte>(pointer, MemoryViewHelper.GetByteLength<T>(stride, length)); | |
Stride = stride; | |
Length = length; | |
} | |
/// <summary>Creates a new <see cref="SpanView{T}"/> object from a specified buffer starting at a specified offset with stride.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public unsafe SpanView(Span<byte> span, int offset, int stride) | |
{ | |
if (stride < sizeof(T)) | |
throw new ArgumentException(null, nameof(stride)); | |
this.span = span[offset..]; | |
Stride = stride; | |
Length = MemoryViewHelper.GetLength<T>(stride, this.span.Length); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
internal unsafe SpanView(Span<byte> span, int offset, int stride, int length) | |
{ | |
if (stride < sizeof(T)) | |
throw new ArgumentException(null, nameof(stride)); | |
if (length > MemoryViewHelper.GetLength<T>(stride, span[offset..].Length)) | |
throw new ArgumentException(null, nameof(length)); | |
this.span = span[offset..]; | |
Stride = stride; | |
Length = length; | |
} | |
/// <summary>Gets the element at the specified zero-based index.</summary> | |
public ref T this[int index] | |
{ | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
get | |
{ | |
if ((uint)index >= (uint)Length) | |
throw new IndexOutOfRangeException(); | |
return ref DangerousGetReferenceAt(index); | |
} | |
} | |
/// <summary>Returns an enumerator for this <see cref="SpanView{T}"/>.</summary> | |
public Enumerator GetEnumerator() | |
{ | |
return new Enumerator(this); | |
} | |
/// <summary>Forms a slice out of the current view that begins at a specified index.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public SpanView<T> Slice(int start) | |
{ | |
if ((uint)start > (uint)Length) | |
throw new ArgumentOutOfRangeException(); | |
return new SpanView<T>(span, start * Stride, Stride, Length - start); | |
} | |
/// <summary>Forms a slice out of the current view starting at a specified index for a specified length.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public SpanView<T> Slice(int start, int length) | |
{ | |
#if TARGET_64BIT | |
if ((nuint)start + (nuint)length > (nuint)Length) | |
throw new ArgumentOutOfRangeException(); | |
#else | |
if ((uint)start > (uint)Length || (uint)length > (uint)(Length - start)) | |
throw new ArgumentOutOfRangeException(); | |
#endif | |
return new SpanView<T>(span[..MemoryViewHelper.GetByteLength<T>(Stride, length)], start * Stride, Stride, length); | |
} | |
/// <summary>Clears the contents of this <see cref="SpanView{T}"/> object.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public void Clear() | |
{ | |
MemoryViewHelper.Clear(ref DangerousGetReference(), Length, Stride); | |
} | |
/// <summary>Fills the elements of this view with a specified value.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public void Fill(T value) | |
{ | |
MemoryViewHelper.Fill(ref DangerousGetReference(), Length, Stride, value); | |
} | |
/// <summary>Copies the contents of a source <see cref="ReadOnlySpan{T}"/> into this <see cref="SpanView{T}"/>.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public void CopyFrom(ReadOnlySpan<T> source) | |
{ | |
if ((uint)Length < (uint)source.Length) | |
throw new ArgumentException(null, nameof(source)); | |
MemoryViewHelper.CopyFrom( | |
ref MemoryMarshal.GetReference(source), | |
ref DangerousGetReference(), | |
source.Length, | |
Stride | |
); | |
} | |
/// <summary>Attempts to copy a source <see cref="ReadOnlySpan{T}"/> into the current <see cref="SpanView{T}"/> and returns a value that indicates whether the copy operation succeeded.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public bool TryCopyFrom(ReadOnlySpan<T> source) | |
{ | |
if ((uint)Length < (uint)source.Length) | |
return false; | |
CopyFrom(source); | |
return true; | |
} | |
/// <summary>Copies the contents of a source <see cref="ReadOnlySpanView{T}"/> into this <see cref="SpanView{T}"/>.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public void CopyFrom(ReadOnlySpanView<T> source) | |
{ | |
source.CopyTo(this); | |
} | |
/// <summary>Attempts to copy a source <see cref="ReadOnlySpanView{T}"/> into the current <see cref="SpanView{T}"/> and returns a value that indicates whether the copy operation succeeded.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public bool TryCopyFrom(ReadOnlySpanView<T> source) | |
{ | |
return source.TryCopyTo(this); | |
} | |
/// <summary>Copies the contents of this <see cref="SpanView{T}"/> into a destination <see cref="SpanView{T}"/>.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public void CopyTo(SpanView<T> destination) | |
{ | |
if ((uint)Length > (uint)destination.Length) | |
throw new ArgumentException(null, nameof(destination)); | |
MemoryViewHelper.CopyTo( | |
ref DangerousGetReference(), | |
ref destination.DangerousGetReference(), | |
Length, | |
Stride, | |
destination.Stride | |
); | |
} | |
/// <summary>Attempts to copy the current <see cref="SpanView{T}"/> to a destination <see cref="SpanView{T}"/> and returns a value that indicates whether the copy operation succeeded.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public bool TryCopyTo(SpanView<T> destination) | |
{ | |
if ((uint)Length > (uint)destination.Length) | |
return false; | |
CopyTo(destination); | |
return true; | |
} | |
/// <summary>Copies the contents of this <see cref="SpanView{T}"/> into a destination <see cref="Span{T}"/>.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public void CopyTo(Span<T> destination) | |
{ | |
if (Length > destination.Length) | |
throw new ArgumentException(null, nameof(destination)); | |
MemoryViewHelper.CopyTo( | |
ref DangerousGetReference(), | |
ref MemoryMarshal.GetReference(destination), | |
Length, | |
Stride | |
); | |
} | |
/// <summary>Attempts to copy the current <see cref="SpanView{T}"/> to a destination <see cref="Span{T}"/> and returns a value that indicates whether the copy operation succeeded.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public bool TryCopyTo(Span<T> destination) | |
{ | |
if (Length > destination.Length) | |
return false; | |
CopyTo(destination); | |
return true; | |
} | |
/// <summary>Returns a reference to the storage location of an element at index zero.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public ref T DangerousGetReference() | |
{ | |
return ref Unsafe.As<byte, T>(ref MemoryMarshal.GetReference(span)); | |
} | |
/// <summary>Returns a reference to the storage location of an element at a given index.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public ref T DangerousGetReferenceAt(int index) | |
{ | |
return ref Unsafe.AddByteOffset(ref Unsafe.As<byte, T>(ref MemoryMarshal.GetReference(span)), (nint)index * Stride); | |
} | |
/// <summary>Returns a reference to the element of the <see cref="SpanView{T}"/> at index zero.</summary> | |
[EditorBrowsable(EditorBrowsableState.Never)] | |
public ref T GetPinnableReference() | |
{ | |
ref T result = ref Unsafe.NullRef<T>(); | |
if (Length != 0) | |
result = ref DangerousGetReference(); | |
return ref result; | |
} | |
/// <summary>Determines whether the specified <see cref="SpanView{T}"/> object is equal to the current object.</summary> | |
public bool Equals(SpanView<T> other) | |
{ | |
return Length == other.Length && Unsafe.AreSame( | |
ref MemoryMarshal.GetReference(span), | |
ref MemoryMarshal.GetReference(other.span) | |
); | |
} | |
/// <summary>Calls to this method are not supported.</summary> | |
[EditorBrowsable(EditorBrowsableState.Never)] | |
public override bool Equals(object obj) | |
{ | |
throw new NotSupportedException(); | |
} | |
/// <summary>Calls to this method are not supported.</summary> | |
[EditorBrowsable(EditorBrowsableState.Never)] | |
public override int GetHashCode() | |
{ | |
throw new NotSupportedException(); | |
} | |
/// <summary>Returns the string representation of this <see cref="SpanView{T}"/> object.</summary> | |
public unsafe override string ToString() | |
{ | |
if (typeof(T) == typeof(char)) | |
{ | |
var result = new string('\0', Length); | |
fixed (char* p = result) | |
CopyTo(new Span<T>(p, Length)); | |
return result; | |
} | |
return string.Format(nameof(SpanView<T>) + "<{0}>[{1}]", typeof(T).Name, Length); | |
} | |
/// <summary>Copies the contents of this view into a new array.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public T[] ToArray() | |
{ | |
if (Length == 0) | |
return Array.Empty<T>(); | |
var array = new T[Length]; | |
CopyTo(array); | |
return array; | |
} | |
/// <summary>Defines an implicit conversion of a <see cref="Span{T}"/> to a <see cref="SpanView{T}"/>.</summary> | |
public static unsafe implicit operator SpanView<T>(Span<T> span) | |
{ | |
return new SpanView<T>(MemoryMarshal.AsBytes(span), 0, sizeof(T), span.Length); | |
} | |
/// <summary>Returns a value that indicates whether two <see cref="SpanView{T}"/> objects are equal.</summary> | |
public static bool operator ==(SpanView<T> left, SpanView<T> right) | |
{ | |
return left.Equals(right); | |
} | |
/// <summary>Returns a value that indicates whether two <see cref="SpanView{T}"/> objects are not equal.</summary> | |
public static bool operator !=(SpanView<T> left, SpanView<T> right) | |
{ | |
return !left.Equals(right); | |
} | |
/// <summary>Provides an enumerator for the elements of a <see cref="SpanView{T}"/>.</summary> | |
public ref struct Enumerator | |
{ | |
int position; | |
readonly SpanView<T> view; | |
/// <summary>Gets a reference to the item at the current position of the enumerator.</summary> | |
public ref T Current | |
{ | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
get => ref view.DangerousGetReferenceAt(position); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
internal Enumerator(SpanView<T> view) | |
{ | |
this.view = view; | |
position = -1; | |
} | |
/// <summary>Advances the enumerator to the next item of the <see cref="SpanView{T}"/>.</summary> | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public bool MoveNext() | |
{ | |
return ++position < view.Length; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment