| using J2N.Collections; |
| using Lucene.Net.Diagnostics; |
| using Lucene.Net.Util; |
| using System; |
| using System.Collections.Generic; |
| using System.Diagnostics.CodeAnalysis; |
| using System.Runtime.CompilerServices; |
| #if NETSTANDARD2_0_OR_GREATER |
| using System.Runtime.InteropServices; |
| #endif |
| using System.Text; |
| |
| namespace Lucene.Net.Support |
| { |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| internal static class Arrays |
| { |
| /// <summary> |
| /// Compares the entire members of one array with the other one. |
| /// </summary> |
| /// <param name="a">The array to be compared.</param> |
| /// <param name="b">The array to be compared with.</param> |
| /// <returns>Returns true if the two specified arrays of Objects are equal |
| /// to one another. The two arrays are considered equal if both arrays |
| /// contain the same number of elements, and all corresponding pairs of |
| /// elements in the two arrays are equal. Two objects e1 and e2 are |
| /// considered equal if (e1==null ? e2==null : e1.Equals(e2)). In other |
| /// words, the two arrays are equal if they contain the same elements in |
| /// the same order. Also, two array references are considered equal if |
| /// both are null. |
| /// <para/> |
| /// Note that if the type of <typeparam name="T"/> is a <see cref="IDictionary{TKey, TValue}"/>, |
| /// <see cref="IList{T}"/>, or <see cref="ISet{T}"/>, its values and any nested collection values |
| /// will be compared for equality as well. |
| /// </returns> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| public static bool Equals<T>(T[] a, T[] b) |
| { |
| return ArrayEqualityComparer<T>.OneDimensional.Equals(a, b); |
| } |
| |
| /// <summary> |
| /// Returns a hash code based on the contents of the given array. For any two |
| /// <typeparamref name="T"/> arrays <c>a</c> and <c>b</c>, if |
| /// <c>Arrays.Equals(b)</c> returns <c>true</c>, it means |
| /// that the return value of <c>Arrays.GetHashCode(a)</c> equals <c>Arrays.GetHashCode(b)</c>. |
| /// </summary> |
| /// <typeparam name="T">The array element type.</typeparam> |
| /// <param name="array">The array whose hash code to compute.</param> |
| /// <returns>The hash code for <paramref name="array"/>.</returns> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| public static int GetHashCode<T>(T[] array) |
| { |
| return ArrayEqualityComparer<T>.OneDimensional.GetHashCode(array); |
| } |
| |
| /// <summary> |
| /// Assigns the specified value to each element of the specified array. |
| /// </summary> |
| /// <typeparam name="T">the type of the array</typeparam> |
| /// <param name="a">the array to be filled</param> |
| /// <param name="val">the value to be stored in all elements of the array</param> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| public static void Fill<T>(T[] a, T val) |
| { |
| ArrayFiller<T>.Default.Fill(a, val, 0, a.Length); |
| } |
| |
| /// <summary> |
| /// Assigns the specified long value to each element of the specified |
| /// range of the specified array of longs. The range to be filled |
| /// extends from index <paramref name="fromIndex"/>, inclusive, to index |
| /// <paramref name="toIndex"/>, exclusive. (If <c>fromIndex==toIndex</c>, the |
| /// range to be filled is empty.) |
| /// </summary> |
| /// <typeparam name="T">the type of the array</typeparam> |
| /// <param name="a">the array to be filled</param> |
| /// <param name="fromIndex"> |
| /// the index of the first element (inclusive) to be |
| /// filled with the specified value |
| /// </param> |
| /// <param name="toIndex"> |
| /// the index of the last element (exclusive) to be |
| /// filled with the specified value |
| /// </param> |
| /// <param name="val">the value to be stored in all elements of the array</param> |
| /// <exception cref="ArgumentException">if <c>fromIndex > toIndex</c></exception> |
| /// <exception cref="ArgumentOutOfRangeException">if <c>fromIndex < 0</c> or <c>toIndex > a.Length</c></exception> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| public static void Fill<T>(T[] a, int fromIndex, int toIndex, T val) |
| { |
| //Java Arrays.fill exception logic |
| if (fromIndex > toIndex) |
| throw new ArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")"); |
| if (fromIndex < 0) |
| throw new ArgumentOutOfRangeException(nameof(fromIndex)); |
| if (toIndex > a.Length) |
| throw new ArgumentOutOfRangeException(nameof(toIndex)); |
| |
| int length = toIndex - fromIndex; |
| ArrayFiller<T>.Default.Fill(a, val, fromIndex, length); |
| } |
| |
| #region ArrayFiller<T> |
| private static class ArrayFiller<T> |
| { |
| public static readonly IArrayFiller<T> Default = LoadArrayFiller(); |
| |
| private static IArrayFiller<T> LoadArrayFiller() |
| { |
| #if FEATURE_ARRAY_FILL |
| if (PlatformDetection.IsNetCore) |
| return new SpanFillArrayFiller<T>(); |
| |
| return new ArrayFillArrayFiller<T>(); |
| #else |
| return new SpanFillArrayFiller<T>(); |
| #endif |
| } |
| |
| } |
| |
| private interface IArrayFiller<in T> |
| { |
| void Fill(T[] array, T value, int startIndex, int count); |
| } |
| |
| #if FEATURE_ARRAY_FILL |
| private sealed class ArrayFillArrayFiller<T> : IArrayFiller<T> |
| { |
| public void Fill(T[] array, T value, int startIndex, int count) |
| { |
| Array.Fill(array, value, startIndex, count); |
| } |
| } |
| #endif |
| private sealed class SpanFillArrayFiller<T> : IArrayFiller<T> |
| { |
| public void Fill(T[] array, T value, int startIndex, int count) |
| { |
| array.AsSpan(startIndex, count).Fill(value); |
| } |
| } |
| |
| #endregion ArrayFiller<T> |
| |
| /// <summary> |
| /// Copies a range of elements from an Array starting at the first element and pastes them |
| /// into another Array starting at the first element. The length is specified as a 32-bit integer. |
| /// <para/> |
| /// <b>Usage Note:</b> This implementation uses the most efficient (known) method for copying the |
| /// array based on the data type and platform. |
| /// </summary> |
| /// <typeparam name="T">The array type.</typeparam> |
| /// <param name="sourceArray">The Array that contains the data to copy.</param> |
| /// <param name="destinationArray">The Array that receives the data.</param> |
| /// <param name="length">A 32-bit integer that represents the number of elements to copy.</param> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| public static void Copy<T>(T[] sourceArray, T[] destinationArray, int length) |
| { |
| if (length == 0) |
| return; |
| |
| if (Debugging.AssertsEnabled) // LUCENENET: Since this is internal, we are relying on Debugging.Assert to ensure the values passed in are correct. |
| { |
| Debugging.Assert(sourceArray is not null); |
| Debugging.Assert(destinationArray is not null); |
| Debugging.Assert(length >= 0 || length <= sourceArray.Length || length <= destinationArray.Length); |
| } |
| |
| ArrayCopier<T>.Default.Copy(sourceArray, sourceIndex: 0, destinationArray, destinationIndex: 0, length); |
| } |
| |
| /// <summary> |
| /// Copies a range of elements from an Array starting at the specified source index and pastes them |
| /// to another Array starting at the specified destination index. The length and the indexes are |
| /// specified as 32-bit integers. |
| /// <para/> |
| /// <b>Usage Note:</b> This implementation uses the most efficient (known) method for copying the |
| /// array based on the data type and platform. |
| /// </summary> |
| /// <typeparam name="T">The array type.</typeparam> |
| /// <param name="sourceArray">The Array that contains the data to copy.</param> |
| /// <param name="sourceIndex">A 32-bit integer that represents the index in the |
| /// <paramref name="sourceArray"/> at which copying begins.</param> |
| /// <param name="destinationArray">The Array that receives the data.</param> |
| /// <param name="destinationIndex">A 32-bit integer that represents the index in the |
| /// <paramref name="destinationArray"/> at which storing begins.</param> |
| /// <param name="length">A 32-bit integer that represents the number of elements to copy.</param> |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| public static void Copy<T>(T[] sourceArray, int sourceIndex, T[] destinationArray, int destinationIndex, int length) |
| { |
| if (length == 0) |
| return; |
| |
| if (Debugging.AssertsEnabled) // LUCENENET: Since this is internal, we are relying on Debugging.Assert to ensure the values passed in are correct. |
| { |
| Debugging.Assert(sourceArray is not null); |
| Debugging.Assert(destinationArray is not null); |
| Debugging.Assert(sourceIndex >= 0 || sourceIndex <= sourceArray.Length - length); |
| Debugging.Assert(destinationIndex >= 0 || destinationIndex <= destinationArray.Length - length); |
| Debugging.Assert(length >= 0 || length < sourceArray.Length || length < destinationArray.Length); |
| } |
| |
| ArrayCopier<T>.Default.Copy(sourceArray, sourceIndex, destinationArray, destinationIndex, length); |
| } |
| |
| [SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "This is a SonarCloud issue")] |
| [SuppressMessage("CodeQuality", "S3400:Methods should not return constants", Justification = "Clearly, this is not always a constant value (SonarCloud bug)")] |
| private static class PlatformDetection |
| { |
| // We put this in its own class so every type doesn't have to reload it. But, at the same time, |
| // we don't want to have to load this just to use the Arrays class. |
| public static readonly bool IsFullFramework = LoadIsFullFramework(); |
| public static readonly bool IsNetCore = LoadIsNetCore(); |
| |
| private static bool LoadIsFullFramework() |
| { |
| #if NETSTANDARD2_0 |
| return RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework", StringComparison.OrdinalIgnoreCase); |
| #elif NET40_OR_GREATER |
| return true; |
| #else |
| return false; |
| #endif |
| } |
| |
| private static bool LoadIsNetCore() |
| { |
| #if NETSTANDARD2_0_OR_GREATER |
| return RuntimeInformation.FrameworkDescription.StartsWith(".NET Core", StringComparison.OrdinalIgnoreCase); |
| #elif NET5_0_OR_GREATER || NETCOREAPP1_0_OR_GREATER |
| return true; |
| #else |
| return false; |
| #endif |
| } |
| } |
| |
| #region ArrayCopier<T> |
| |
| private static class ArrayCopier<T> |
| { |
| public static readonly IArrayCopier<T> Default = LoadArrayCopier(); |
| |
| private static IArrayCopier<T> LoadArrayCopier() |
| { |
| // Default to Array.Copy() for unknown platforms (Span<T>.Copy() is slow on Mono) |
| if (!PlatformDetection.IsFullFramework && !PlatformDetection.IsNetCore) |
| return new SystemArrayCopyArrayCopier<T>(); |
| |
| var type = typeof(T); |
| |
| if (!type.IsValueType) // Reference types |
| { |
| // Span<T> and Memory<T> are horribly slow on.NET Framework for |
| // copying arrays of reference types. |
| if (PlatformDetection.IsFullFramework) |
| return new SystemArrayCopyArrayCopier<T>(); |
| |
| return new SpanArrayCopier<T>(); |
| } |
| |
| // Value types are generally tied with or faster than Buffer.MemoryCopy() using |
| // Span<T>.Copy() on Linux and macOS. |
| if (!Constants.WINDOWS && !PlatformDetection.IsFullFramework) |
| return new SpanArrayCopier<T>(); |
| |
| if (type == typeof(byte)) |
| { |
| // On Windows, copying bytes with Buffer.MemoryCopy() is fastest. |
| return (IArrayCopier<T>)(object)new ByteBufferMemoryCopyArrayCopier(); |
| } |
| else if (type == typeof(sbyte)) |
| { |
| return (IArrayCopier<T>)(object)new SByteBufferMemoryCopyArrayCopier(); |
| } |
| else if (PlatformDetection.IsFullFramework) |
| { |
| // .NET Framework is 2-3x faster to use Buffer.MemoryCopy() than any other method for primitive types. |
| if (type == typeof(short)) |
| return (IArrayCopier<T>)(object)new Int16BufferMemoryCopyArrayCopier(); |
| if (type == typeof(ushort)) |
| return (IArrayCopier<T>)(object)new UInt16BufferMemoryCopyArrayCopier(); |
| if (type == typeof(int)) |
| return (IArrayCopier<T>)(object)new Int32BufferMemoryCopyArrayCopier(); |
| if (type == typeof(uint)) |
| return (IArrayCopier<T>)(object)new UInt32BufferMemoryCopyArrayCopier(); |
| if (type == typeof(long)) |
| return (IArrayCopier<T>)(object)new Int64BufferMemoryCopyArrayCopier(); |
| if (type == typeof(ulong)) |
| return (IArrayCopier<T>)(object)new UInt64BufferMemoryCopyArrayCopier(); |
| if (type == typeof(float)) |
| return (IArrayCopier<T>)(object)new SingleBufferMemoryCopyArrayCopier(); |
| if (type == typeof(double)) |
| return (IArrayCopier<T>)(object)new DoubleBufferMemoryCopyArrayCopier(); |
| if (type == typeof(char)) |
| return (IArrayCopier<T>)(object)new CharBufferMemoryCopyArrayCopier(); |
| if (type == typeof(bool)) |
| return (IArrayCopier<T>)(object)new BooleanBufferMemoryCopyArrayCopier(); |
| |
| return new SystemArrayCopyArrayCopier<T>(); |
| } |
| else |
| { |
| return new SpanArrayCopier<T>(); |
| } |
| } |
| } |
| |
| private interface IArrayCopier<in T> |
| { |
| void Copy(T[] sourceArray, int sourceIndex, T[] destinationArray, int destinationIndex, int length); |
| } |
| |
| private sealed class SpanArrayCopier<T> : IArrayCopier<T> |
| { |
| public void Copy(T[] sourceArray, int sourceIndex, T[] destinationArray, int destinationIndex, int length) |
| { |
| sourceArray.AsSpan(sourceIndex, length).CopyTo(destinationArray.AsSpan(destinationIndex, length)); |
| } |
| } |
| |
| private sealed class SystemArrayCopyArrayCopier<T> : IArrayCopier<T> |
| { |
| public void Copy(T[] sourceArray, int sourceIndex, T[] destinationArray, int destinationIndex, int length) |
| { |
| Array.Copy(sourceArray, sourceIndex, destinationArray, destinationIndex, length); |
| } |
| } |
| |
| #region Primitive Type Buffer.MemoryCopy() Array Copiers |
| // We save some arithmetic by having a specialized type for byte, since we know it is measured in bytes. |
| private sealed class ByteBufferMemoryCopyArrayCopier : IArrayCopier<byte> |
| { |
| public void Copy(byte[] sourceArray, int sourceIndex, byte[] destinationArray, int destinationIndex, int length) |
| { |
| unsafe |
| { |
| fixed (byte* sourcePointer = &sourceArray[sourceIndex], destinationPointer = &destinationArray[destinationIndex]) |
| { |
| // NOTE: We are relying on the fact that passing the pointers into this method is creating copies of them |
| // that are not fixed. |
| Buffer.MemoryCopy(sourcePointer, destinationPointer, destinationArray.Length - destinationIndex, length); |
| } |
| } |
| } |
| } |
| |
| // We save some arithmetic by having a specialized type for byte, since we know it is measured in bytes. |
| private sealed class SByteBufferMemoryCopyArrayCopier : IArrayCopier<sbyte> |
| { |
| public void Copy(sbyte[] sourceArray, int sourceIndex, sbyte[] destinationArray, int destinationIndex, int length) |
| { |
| unsafe |
| { |
| fixed (sbyte* sourcePointer = &sourceArray[sourceIndex], destinationPointer = &destinationArray[destinationIndex]) |
| { |
| // NOTE: We are relying on the fact that passing the pointers into this method is creating copies of them |
| // that are not fixed. |
| Buffer.MemoryCopy(sourcePointer, destinationPointer, destinationArray.Length - destinationIndex, length); |
| } |
| } |
| } |
| } |
| |
| // LUCENENET NOTE: Tried to make the following types one generic type, but SDKs prior to .NET 7 won't compile it. |
| // See: https://github.com/dotnet/runtime/issues/76255 |
| |
| private sealed class Int16BufferMemoryCopyArrayCopier : IArrayCopier<short> |
| { |
| public void Copy(short[] sourceArray, int sourceIndex, short[] destinationArray, int destinationIndex, int length) |
| { |
| unsafe |
| { |
| fixed (short* sourcePointer = &sourceArray[sourceIndex], destinationPointer = &destinationArray[destinationIndex]) |
| { |
| const int size = sizeof(short); |
| long destinationSizeInBytes = (destinationArray.Length - destinationIndex) * size; |
| long sourceBytesToCopy = length * size; |
| // NOTE: We are relying on the fact that passing the pointers into this method is creating copies of them |
| // that are not fixed. |
| Buffer.MemoryCopy(sourcePointer, destinationPointer, destinationSizeInBytes, sourceBytesToCopy); |
| } |
| } |
| } |
| } |
| |
| private sealed class UInt16BufferMemoryCopyArrayCopier : IArrayCopier<ushort> |
| { |
| public void Copy(ushort[] sourceArray, int sourceIndex, ushort[] destinationArray, int destinationIndex, int length) |
| { |
| unsafe |
| { |
| fixed (ushort* sourcePointer = &sourceArray[sourceIndex], destinationPointer = &destinationArray[destinationIndex]) |
| { |
| const int size = sizeof(ushort); |
| long destinationSizeInBytes = (destinationArray.Length - destinationIndex) * size; |
| long sourceBytesToCopy = length * size; |
| // NOTE: We are relying on the fact that passing the pointers into this method is creating copies of them |
| // that are not fixed. |
| Buffer.MemoryCopy(sourcePointer, destinationPointer, destinationSizeInBytes, sourceBytesToCopy); |
| } |
| } |
| } |
| } |
| |
| private sealed class Int32BufferMemoryCopyArrayCopier : IArrayCopier<int> |
| { |
| public void Copy(int[] sourceArray, int sourceIndex, int[] destinationArray, int destinationIndex, int length) |
| { |
| unsafe |
| { |
| fixed (int* sourcePointer = &sourceArray[sourceIndex], destinationPointer = &destinationArray[destinationIndex]) |
| { |
| const int size = sizeof(int); |
| long destinationSizeInBytes = (destinationArray.Length - destinationIndex) * size; |
| long sourceBytesToCopy = length * size; |
| // NOTE: We are relying on the fact that passing the pointers into this method is creating copies of them |
| // that are not fixed. |
| Buffer.MemoryCopy(sourcePointer, destinationPointer, destinationSizeInBytes, sourceBytesToCopy); |
| } |
| } |
| } |
| } |
| |
| private sealed class UInt32BufferMemoryCopyArrayCopier : IArrayCopier<uint> |
| { |
| public void Copy(uint[] sourceArray, int sourceIndex, uint[] destinationArray, int destinationIndex, int length) |
| { |
| unsafe |
| { |
| fixed (uint* sourcePointer = &sourceArray[sourceIndex], destinationPointer = &destinationArray[destinationIndex]) |
| { |
| const int size = sizeof(uint); |
| long destinationSizeInBytes = (destinationArray.Length - destinationIndex) * size; |
| long sourceBytesToCopy = length * size; |
| // NOTE: We are relying on the fact that passing the pointers into this method is creating copies of them |
| // that are not fixed. |
| Buffer.MemoryCopy(sourcePointer, destinationPointer, destinationSizeInBytes, sourceBytesToCopy); |
| } |
| } |
| } |
| } |
| |
| private sealed class Int64BufferMemoryCopyArrayCopier : IArrayCopier<long> |
| { |
| public void Copy(long[] sourceArray, int sourceIndex, long[] destinationArray, int destinationIndex, int length) |
| { |
| unsafe |
| { |
| fixed (long* sourcePointer = &sourceArray[sourceIndex], destinationPointer = &destinationArray[destinationIndex]) |
| { |
| const int size = sizeof(long); |
| long destinationSizeInBytes = (destinationArray.Length - destinationIndex) * size; |
| long sourceBytesToCopy = length * size; |
| // NOTE: We are relying on the fact that passing the pointers into this method is creating copies of them |
| // that are not fixed. |
| Buffer.MemoryCopy(sourcePointer, destinationPointer, destinationSizeInBytes, sourceBytesToCopy); |
| } |
| } |
| } |
| } |
| |
| private sealed class UInt64BufferMemoryCopyArrayCopier : IArrayCopier<ulong> |
| { |
| public void Copy(ulong[] sourceArray, int sourceIndex, ulong[] destinationArray, int destinationIndex, int length) |
| { |
| unsafe |
| { |
| fixed (ulong* sourcePointer = &sourceArray[sourceIndex], destinationPointer = &destinationArray[destinationIndex]) |
| { |
| const int size = sizeof(ulong); |
| long destinationSizeInBytes = (destinationArray.Length - destinationIndex) * size; |
| long sourceBytesToCopy = length * size; |
| // NOTE: We are relying on the fact that passing the pointers into this method is creating copies of them |
| // that are not fixed. |
| Buffer.MemoryCopy(sourcePointer, destinationPointer, destinationSizeInBytes, sourceBytesToCopy); |
| } |
| } |
| } |
| } |
| |
| private sealed class SingleBufferMemoryCopyArrayCopier : IArrayCopier<float> |
| { |
| public void Copy(float[] sourceArray, int sourceIndex, float[] destinationArray, int destinationIndex, int length) |
| { |
| unsafe |
| { |
| fixed (float* sourcePointer = &sourceArray[sourceIndex], destinationPointer = &destinationArray[destinationIndex]) |
| { |
| const int size = sizeof(float); |
| long destinationSizeInBytes = (destinationArray.Length - destinationIndex) * size; |
| long sourceBytesToCopy = length * size; |
| // NOTE: We are relying on the fact that passing the pointers into this method is creating copies of them |
| // that are not fixed. |
| Buffer.MemoryCopy(sourcePointer, destinationPointer, destinationSizeInBytes, sourceBytesToCopy); |
| } |
| } |
| } |
| } |
| |
| private sealed class DoubleBufferMemoryCopyArrayCopier : IArrayCopier<double> |
| { |
| public void Copy(double[] sourceArray, int sourceIndex, double[] destinationArray, int destinationIndex, int length) |
| { |
| unsafe |
| { |
| fixed (double* sourcePointer = &sourceArray[sourceIndex], destinationPointer = &destinationArray[destinationIndex]) |
| { |
| const int size = sizeof(double); |
| long destinationSizeInBytes = (destinationArray.Length - destinationIndex) * size; |
| long sourceBytesToCopy = length * size; |
| // NOTE: We are relying on the fact that passing the pointers into this method is creating copies of them |
| // that are not fixed. |
| Buffer.MemoryCopy(sourcePointer, destinationPointer, destinationSizeInBytes, sourceBytesToCopy); |
| } |
| } |
| } |
| } |
| |
| private sealed class CharBufferMemoryCopyArrayCopier : IArrayCopier<char> |
| { |
| public void Copy(char[] sourceArray, int sourceIndex, char[] destinationArray, int destinationIndex, int length) |
| { |
| unsafe |
| { |
| fixed (char* sourcePointer = &sourceArray[sourceIndex], destinationPointer = &destinationArray[destinationIndex]) |
| { |
| const int size = sizeof(char); |
| long destinationSizeInBytes = (destinationArray.Length - destinationIndex) * size; |
| long sourceBytesToCopy = length * size; |
| // NOTE: We are relying on the fact that passing the pointers into this method is creating copies of them |
| // that are not fixed. |
| Buffer.MemoryCopy(sourcePointer, destinationPointer, destinationSizeInBytes, sourceBytesToCopy); |
| } |
| } |
| } |
| } |
| |
| private sealed class BooleanBufferMemoryCopyArrayCopier : IArrayCopier<bool> |
| { |
| public void Copy(bool[] sourceArray, int sourceIndex, bool[] destinationArray, int destinationIndex, int length) |
| { |
| unsafe |
| { |
| fixed (bool* sourcePointer = &sourceArray[sourceIndex], destinationPointer = &destinationArray[destinationIndex]) |
| { |
| const int size = sizeof(bool); |
| long destinationSizeInBytes = (destinationArray.Length - destinationIndex) * size; |
| long sourceBytesToCopy = length * size; |
| // NOTE: We are relying on the fact that passing the pointers into this method is creating copies of them |
| // that are not fixed. |
| Buffer.MemoryCopy(sourcePointer, destinationPointer, destinationSizeInBytes, sourceBytesToCopy); |
| } |
| } |
| } |
| } |
| |
| // Intentionally not adding support for IntPtr and UIntPtr |
| |
| #endregion Primitive Type Buffer.MemoryCopy() Array Copiers |
| |
| #endregion ArrayCopier<T> |
| |
| |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| public static T[] CopyOf<T>(T[] original, int newLength) |
| { |
| T[] newArray = new T[newLength]; |
| Copy(original, newArray, Math.Min(original.Length, newLength)); |
| return newArray; |
| } |
| |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| public static T[] CopyOfRange<T>(T[] original, int startIndexInc, int endIndexExc) |
| { |
| int newLength = endIndexExc - startIndexInc; |
| T[] newArray = new T[newLength]; |
| Copy(original, startIndexInc, newArray, 0, newLength); |
| return newArray; |
| } |
| |
| /// <summary> |
| /// Creates a <see cref="string"/> representation of the array passed. |
| /// The result is surrounded by brackets <c>"[]"</c>, each |
| /// element is converted to a <see cref="string"/> via the |
| /// <see cref="J2N.Text.StringFormatter.InvariantCulture"/> and separated by <c>", "</c>. If |
| /// the array is <c>null</c>, then <c>"null"</c> is returned. |
| /// </summary> |
| /// <typeparam name="T">The type of array element.</typeparam> |
| /// <param name="array">The array to convert.</param> |
| /// <returns>The converted array string.</returns> |
| public static string ToString<T>(T[] array) |
| { |
| if (array is null) |
| return "null"; //$NON-NLS-1$ |
| if (array.Length == 0) |
| return "[]"; //$NON-NLS-1$ |
| StringBuilder sb = new StringBuilder(2 + array.Length * 4); |
| sb.Append('['); |
| sb.AppendFormat(J2N.Text.StringFormatter.InvariantCulture, "{0}", array[0]); |
| for (int i = 1; i < array.Length; i++) |
| { |
| sb.Append(", "); //$NON-NLS-1$ |
| sb.AppendFormat(J2N.Text.StringFormatter.InvariantCulture, "{0}", array[i]); |
| } |
| sb.Append(']'); |
| return sb.ToString(); |
| } |
| } |
| } |