Skip to content

Instantly share code, notes, and snippets.

@DaZombieKiller
Created January 12, 2021 03:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DaZombieKiller/585163a01e78f03b31baa304a2aa2e28 to your computer and use it in GitHub Desktop.
Save DaZombieKiller/585163a01e78f03b31baa304a2aa2e28 to your computer and use it in GitHub Desktop.
(ReadOnly)SpanView<T> and (ReadOnly)MemoryView<T>
#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;
}
}
}
}
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);
}
}
}
#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);
}
}
}
#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);
}
}
}
#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;
}
}
}
}
#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