// 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"); } } }