// 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;
#if NETCORE_RUNTIME
using System.Runtime.InteropServices;
#endif
using Microsoft.Toolkit.HighPerformance.Enumerables;
using Microsoft.Toolkit.HighPerformance.Helpers.Internals;
namespace Microsoft.Toolkit.HighPerformance.Extensions
{
///
/// Helpers for working with the type.
///
public static partial class ArrayExtensions
{
///
/// Returns a reference to the first element within a given array, with no bounds checks.
///
/// The type of elements in the input array instance.
/// The input array instance.
/// A reference to the first element within , or the location it would have used, if is empty.
/// 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 T[] array)
{
#if NETCORE_RUNTIME
var arrayData = Unsafe.As(array);
ref T r0 = ref Unsafe.As(ref arrayData.Data);
return ref r0;
#else
#pragma warning disable SA1131 // Inverted comparison to remove JIT bounds check
// Checking the length of the array like so allows the JIT
// to skip its own bounds check, which results in the element
// access below to be executed without branches.
if (0u < (uint)array.Length)
{
return ref array[0];
}
unsafe
{
return ref Unsafe.AsRef(null);
}
#pragma warning restore SA1131
#endif
}
///
/// Returns a reference to an element at a specified index within a given array, with no bounds checks.
///
/// The type of elements in the input array instance.
/// The input array 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 T[] array, int i)
{
#if NETCORE_RUNTIME
var arrayData = Unsafe.As(array);
ref T r0 = ref Unsafe.As(ref arrayData.Data);
ref T ri = ref Unsafe.Add(ref r0, i);
return ref ri;
#else
if ((uint)i < (uint)array.Length)
{
return ref array[i];
}
unsafe
{
return ref Unsafe.AsRef(null);
}
#endif
}
#if NETCORE_RUNTIME
// Description taken from CoreCLR: see https://source.dot.net/#System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs,285.
// CLR arrays are laid out in memory as follows (multidimensional array bounds are optional):
// [ sync block || pMethodTable || num components || MD array bounds || array data .. ]
// ^ ^ ^ returned reference
// | \-- ref Unsafe.As(array).Data
// \-- array
// The base size of an array includes all the fields before the array data,
// including the sync block and method table. The reference to RawData.Data
// points at the number of components, skipping over these two pointer-sized fields.
[StructLayout(LayoutKind.Sequential)]
private sealed class RawArrayData
{
#pragma warning disable CS0649 // Unassigned fields
#pragma warning disable SA1401 // Fields should be private
public IntPtr Length;
public byte Data;
#pragma warning restore CS0649
#pragma warning restore SA1401
}
#endif
///
/// Counts the number of occurrences of a given value into a target array instance.
///
/// The type of items in the input array instance.
/// The input array instance.
/// The value to look for.
/// The number of occurrences of in .
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Count(this T[] array, T value)
where T : IEquatable
{
ref T r0 = ref array.DangerousGetReference();
IntPtr length = (IntPtr)array.Length;
return SpanHelper.Count(ref r0, length, value);
}
///
/// Enumerates the items in the input array instance, as pairs of reference/index values.
/// This extension should be used directly within a loop:
///
/// 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 array 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 T[] array)
{
return new SpanEnumerable(array);
}
///
/// Tokenizes the values in the input array instance using a specified separator.
/// This extension should be used directly within a loop:
///
/// 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 array to tokenize.
/// The source array 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 T[] array, T separator)
where T : IEquatable
{
return new SpanTokenizer(array, separator);
}
///
/// Gets a content hash from the input array instance using the Djb2 algorithm.
/// For more info, see the documentation for .
///
/// The type of items in the input array instance.
/// The input array instance.
/// The Djb2 value for the input array instance.
/// The Djb2 hash is fully deterministic and with no random components.
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetDjb2HashCode(this T[] array)
where T : notnull
{
ref T r0 = ref array.DangerousGetReference();
IntPtr length = (IntPtr)array.Length;
return SpanHelper.GetDjb2HashCode(ref r0, length);
}
}
}