// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Microsoft.Toolkit.HighPerformance.Enumerables;
using Microsoft.Toolkit.HighPerformance.Helpers.Internals;
namespace Microsoft.Toolkit.HighPerformance
{
///
/// Helpers for working with the type.
///
public static class SpanExtensions
{
///
/// Returns a reference to the first element within a given , with no bounds checks.
///
/// The type of elements in the input instance.
/// The input instance.
/// A reference to the first element within .
/// This method doesn't do any bounds checks, therefore it is responsibility of the caller to perform checks in case the returned value is dereferenced.
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T DangerousGetReference(this Span span)
{
return ref MemoryMarshal.GetReference(span);
}
///
/// Returns a reference to an element at a specified index within a given , with no bounds checks.
///
/// The type of elements in the input instance.
/// The input instance.
/// The index of the element to retrieve within .
/// A reference to the element within at the index specified by .
/// This method doesn't do any bounds checks, therefore it is responsibility of the caller to ensure the parameter is valid.
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T DangerousGetReferenceAt(this Span span, int i)
{
ref T r0 = ref MemoryMarshal.GetReference(span);
ref T ri = ref Unsafe.Add(ref r0, (nint)(uint)i);
return ref ri;
}
///
/// Returns a reference to an element at a specified index within a given , with no bounds checks.
///
/// The type of elements in the input instance.
/// The input instance.
/// The index of the element to retrieve within .
/// A reference to the element within at the index specified by .
/// This method doesn't do any bounds checks, therefore it is responsibility of the caller to ensure the parameter is valid.
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T DangerousGetReferenceAt(this Span span, nint i)
{
ref T r0 = ref MemoryMarshal.GetReference(span);
ref T ri = ref Unsafe.Add(ref r0, i);
return ref ri;
}
#if SPAN_RUNTIME_SUPPORT
///
/// Returns a instance wrapping the underlying data for the given instance.
///
/// The type of items in the input instance.
/// The input instance.
/// The height of the resulting 2D area.
/// The width of each row in the resulting 2D area.
/// The resulting instance.
///
/// Thrown when one of the input parameters is out of range.
///
///
/// Thrown when the requested area is outside of bounds for .
///
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span2D AsSpan2D(this Span span, int height, int width)
{
return new(span, height, width);
}
///
/// Returns a instance wrapping the underlying data for the given instance.
///
/// The type of items in the input instance.
/// The input instance.
/// The initial offset within .
/// The height of the resulting 2D area.
/// The width of each row in the resulting 2D area.
/// The pitch in the resulting 2D area.
/// The resulting instance.
///
/// Thrown when one of the input parameters is out of range.
///
///
/// Thrown when the requested area is outside of bounds for .
///
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span2D AsSpan2D(this Span span, int offset, int height, int width, int pitch)
{
return new(span, offset, height, width, pitch);
}
#endif
///
/// Casts a of one primitive type to of bytes.
///
/// The type if items in the source .
/// The source slice, of type .
/// A of bytes.
///
/// Thrown if the property of the new would exceed .
///
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span AsBytes(this Span span)
where T : unmanaged
{
return MemoryMarshal.AsBytes(span);
}
///
/// Casts a of one primitive type to another primitive type .
///
/// The type of items in the source .
/// The type of items in the destination .
/// The source slice, of type .
/// A of type
///
/// Supported only for platforms that support misaligned memory access or when the memory block is aligned by other means.
///
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span Cast(this Span span)
where TFrom : unmanaged
where TTo : unmanaged
{
return MemoryMarshal.Cast(span);
}
///
/// Gets the index of an element of a given from its reference.
///
/// The type if items in the input .
/// The input to calculate the index for.
/// The reference to the target item to get the index for.
/// The index of within .
/// Thrown if does not belong to .
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOf(this Span span, ref T value)
{
ref T r0 = ref MemoryMarshal.GetReference(span);
IntPtr byteOffset = Unsafe.ByteOffset(ref r0, ref value);
nint elementOffset = byteOffset / (nint)(uint)Unsafe.SizeOf();
if ((nuint)elementOffset >= (uint)span.Length)
{
ThrowArgumentOutOfRangeExceptionForInvalidReference();
}
return (int)elementOffset;
}
///
/// Counts the number of occurrences of a given value into a target instance.
///
/// The type of items in the input instance.
/// The input instance to read.
/// The value to look for.
/// The number of occurrences of in .
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Count(this Span span, T value)
where T : IEquatable
{
ref T r0 = ref MemoryMarshal.GetReference(span);
nint length = (nint)(uint)span.Length;
return (int)SpanHelper.Count(ref r0, length, value);
}
///
/// Enumerates the items in the input instance, as pairs of reference/index values.
/// This extension should be used directly within a loop:
///
/// Span<int> numbers = new[] { 1, 2, 3, 4, 5, 6, 7 };
///
/// foreach (var item in numbers.Enumerate())
/// {
/// // Access the index and value of each item here...
/// int index = item.Index;
/// ref int value = ref item.Value;
/// }
///
/// The compiler will take care of properly setting up the loop with the type returned from this method.
///
/// The type of items to enumerate.
/// The source to enumerate.
/// A wrapper type that will handle the reference/index enumeration for .
/// The returned value shouldn't be used directly: use this extension in a loop.
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static SpanEnumerable Enumerate(this Span span)
{
return new(span);
}
///
/// Tokenizes the values in the input instance using a specified separator.
/// This extension should be used directly within a loop:
///
/// Span<char> text = "Hello, world!".ToCharArray();
///
/// foreach (var token in text.Tokenize(','))
/// {
/// // Access the tokens here...
/// }
///
/// The compiler will take care of properly setting up the loop with the type returned from this method.
///
/// The type of items in the to tokenize.
/// The source to tokenize.
/// The separator item to use.
/// A wrapper type that will handle the tokenization for .
/// The returned value shouldn't be used directly: use this extension in a loop.
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static SpanTokenizer Tokenize(this Span span, T separator)
where T : IEquatable
{
return new(span, separator);
}
///
/// Gets a content hash from the input instance using the Djb2 algorithm.
/// For more info, see the documentation for .
///
/// The type of items in the input instance.
/// The input instance.
/// The Djb2 value for the input instance.
/// The Djb2 hash is fully deterministic and with no random components.
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetDjb2HashCode(this Span span)
where T : notnull
{
ref T r0 = ref MemoryMarshal.GetReference(span);
nint length = (nint)(uint)span.Length;
return SpanHelper.GetDjb2HashCode(ref r0, length);
}
///
/// Copies the contents of a given into destination instance.
///
/// The type of items in the input instance.
/// The input instance.
/// The instance to copy items into.
///
/// Thrown when the destination is shorter than the source .
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void CopyTo(this Span span, RefEnumerable destination)
{
destination.CopyFrom(span);
}
///
/// Attempts to copy the contents of a given into destination instance.
///
/// The type of items in the input instance.
/// The input instance.
/// The instance to copy items into.
/// Whether or not the operation was successful.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryCopyTo(this Span span, RefEnumerable destination)
{
return destination.TryCopyFrom(span);
}
///
/// Throws an when the given reference is out of range.
///
internal static void ThrowArgumentOutOfRangeExceptionForInvalidReference()
{
throw new ArgumentOutOfRangeException("value", "The input reference does not belong to an element of the input span");
}
}
}