| using Lucene.Net.Diagnostics; |
| using System; |
| using System.Collections.Generic; |
| |
| namespace Lucene.Net.Util |
| { |
| /* |
| * 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. |
| */ |
| |
| /// <summary> |
| /// Methods for manipulating arrays. |
| /// <para/> |
| /// @lucene.internal |
| /// </summary> |
| public static class ArrayUtil // LUCENENET specific - made static |
| { |
| /// <summary> |
| /// Maximum length for an array; we set this to "a |
| /// bit" below <see cref="int.MaxValue"/> because the exact max |
| /// allowed byte[] is JVM dependent, so we want to avoid |
| /// a case where a large value worked during indexing on |
| /// one JVM but failed later at search time with a |
| /// different JVM. |
| /// </summary> |
| public static readonly int MAX_ARRAY_LENGTH = int.MaxValue - 256; |
| |
| /* |
| Begin Apache Harmony code |
| |
| Revision taken on Friday, June 12. https://svn.apache.org/repos/asf/harmony/enhanced/classlib/archive/java6/modules/luni/src/main/java/java/lang/Integer.java |
| |
| */ |
| |
| /// <summary> |
| /// Parses the string argument as if it was an <see cref="int"/> value and returns the |
| /// result. Throws <see cref="FormatException"/> if the string does not represent an |
| /// int quantity. |
| /// <para/> |
| /// NOTE: This was parseInt() in Lucene |
| /// </summary> |
| /// <param name="chars"> A string representation of an int quantity. </param> |
| /// <returns> The value represented by the argument </returns> |
| /// <exception cref="FormatException"> If the argument could not be parsed as an int quantity. </exception> |
| public static int ParseInt32(char[] chars) |
| { |
| return ParseInt32(chars, 0, chars.Length, 10); |
| } |
| |
| /// <summary> |
| /// Parses a char array into an <see cref="int"/>. |
| /// <para/> |
| /// NOTE: This was parseInt() in Lucene |
| /// </summary> |
| /// <param name="chars"> The character array </param> |
| /// <param name="offset"> The offset into the array </param> |
| /// <param name="len"> The length </param> |
| /// <returns> the <see cref="int"/> </returns> |
| /// <exception cref="FormatException"> If it can't parse </exception> |
| public static int ParseInt32(char[] chars, int offset, int len) |
| { |
| return ParseInt32(chars, offset, len, 10); |
| } |
| |
| /// <summary> |
| /// Parses the string argument as if it was an <see cref="int"/> value and returns the |
| /// result. Throws <see cref="FormatException"/> if the string does not represent an |
| /// <see cref="int"/> quantity. The second argument specifies the radix to use when parsing |
| /// the value. |
| /// <para/> |
| /// NOTE: This was parseInt() in Lucene |
| /// </summary> |
| /// <param name="chars"> A string representation of an int quantity. </param> |
| /// <param name="radix"> The base to use for conversion. </param> |
| /// <returns> The value represented by the argument </returns> |
| /// <exception cref="FormatException"> If the argument could not be parsed as an int quantity. </exception> |
| public static int ParseInt32(char[] chars, int offset, int len, int radix) |
| { |
| int minRadix = 2, maxRadix = 36; |
| if (chars == null || radix < minRadix || radix > maxRadix) |
| { |
| throw new FormatException(); |
| } |
| int i = 0; |
| if (len == 0) |
| { |
| throw new FormatException("chars length is 0"); |
| } |
| bool negative = chars[offset + i] == '-'; |
| if (negative && ++i == len) |
| { |
| throw new FormatException("can't convert to an int"); |
| } |
| if (negative == true) |
| { |
| offset++; |
| len--; |
| } |
| return Parse(chars, offset, len, radix, negative); |
| } |
| |
| private static int Parse(char[] chars, int offset, int len, int radix, bool negative) |
| { |
| int max = int.MinValue / radix; |
| int result = 0; |
| for (int i = 0; i < len; i++) |
| { |
| int digit = (int)char.GetNumericValue(chars[i + offset]); |
| if (digit == -1) |
| { |
| throw new FormatException("Unable to parse"); |
| } |
| if (max > result) |
| { |
| throw new FormatException("Unable to parse"); |
| } |
| int next = result * radix - digit; |
| if (next > result) |
| { |
| throw new FormatException("Unable to parse"); |
| } |
| result = next; |
| } |
| /*while (offset < len) { |
| }*/ |
| if (!negative) |
| { |
| result = -result; |
| if (result < 0) |
| { |
| throw new FormatException("Unable to parse"); |
| } |
| } |
| return result; |
| } |
| |
| /* |
| |
| END APACHE HARMONY CODE |
| */ |
| |
| /// <summary> |
| /// Returns an array size >= <paramref name="minTargetSize"/>, generally |
| /// over-allocating exponentially to achieve amortized |
| /// linear-time cost as the array grows. |
| /// <para/> |
| /// NOTE: this was originally borrowed from Python 2.4.2 |
| /// listobject.c sources (attribution in LICENSE.txt), but |
| /// has now been substantially changed based on |
| /// discussions from java-dev thread with subject "Dynamic |
| /// array reallocation algorithms", started on Jan 12 |
| /// 2010. |
| /// <para/> |
| /// @lucene.internal |
| /// </summary> |
| /// <param name="minTargetSize"> Minimum required value to be returned. </param> |
| /// <param name="bytesPerElement"> Bytes used by each element of |
| /// the array. See constants in <see cref="RamUsageEstimator"/>. </param> |
| public static int Oversize(int minTargetSize, int bytesPerElement) |
| { |
| if (minTargetSize < 0) |
| { |
| // catch usage that accidentally overflows int |
| throw new ArgumentException("invalid array size " + minTargetSize); |
| } |
| |
| if (minTargetSize == 0) |
| { |
| // wait until at least one element is requested |
| return 0; |
| } |
| |
| // asymptotic exponential growth by 1/8th, favors |
| // spending a bit more CPU to not tie up too much wasted |
| // RAM: |
| int extra = minTargetSize >> 3; |
| |
| if (extra < 3) |
| { |
| // for very small arrays, where constant overhead of |
| // realloc is presumably relatively high, we grow |
| // faster |
| extra = 3; |
| } |
| |
| int newSize = minTargetSize + extra; |
| |
| // add 7 to allow for worst case byte alignment addition below: |
| if (newSize + 7 < 0) |
| { |
| // int overflowed -- return max allowed array size |
| return int.MaxValue; |
| } |
| |
| if (Constants.RUNTIME_IS_64BIT) |
| { |
| // round up to 8 byte alignment in 64bit env |
| switch (bytesPerElement) |
| { |
| case 4: |
| // round up to multiple of 2 |
| return (newSize + 1) & 0x7ffffffe; |
| |
| case 2: |
| // round up to multiple of 4 |
| return (newSize + 3) & 0x7ffffffc; |
| |
| case 1: |
| // round up to multiple of 8 |
| return (newSize + 7) & 0x7ffffff8; |
| |
| case 8: |
| // no rounding |
| default: |
| // odd (invalid?) size |
| return newSize; |
| } |
| } |
| else |
| { |
| // round up to 4 byte alignment in 64bit env |
| switch (bytesPerElement) |
| { |
| case 2: |
| // round up to multiple of 2 |
| return (newSize + 1) & 0x7ffffffe; |
| |
| case 1: |
| // round up to multiple of 4 |
| return (newSize + 3) & 0x7ffffffc; |
| |
| case 4: |
| case 8: |
| // no rounding |
| default: |
| // odd (invalid?) size |
| return newSize; |
| } |
| } |
| } |
| |
| public static int GetShrinkSize(int currentSize, int targetSize, int bytesPerElement) |
| { |
| int newSize = Oversize(targetSize, bytesPerElement); |
| // Only reallocate if we are "substantially" smaller. |
| // this saves us from "running hot" (constantly making a |
| // bit bigger then a bit smaller, over and over): |
| if (newSize < currentSize / 2) |
| { |
| return newSize; |
| } |
| else |
| { |
| return currentSize; |
| } |
| } |
| |
| public static short[] Grow(short[] array, int minSize) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(minSize >= 0, () => "size must be positive (got " + minSize + "): likely integer overflow?"); |
| if (array.Length < minSize) |
| { |
| short[] newArray = new short[Oversize(minSize, RamUsageEstimator.NUM_BYTES_INT16)]; |
| Array.Copy(array, 0, newArray, 0, array.Length); |
| return newArray; |
| } |
| else |
| { |
| return array; |
| } |
| } |
| |
| public static short[] Grow(short[] array) |
| { |
| return Grow(array, 1 + array.Length); |
| } |
| |
| public static float[] Grow(float[] array, int minSize) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(minSize >= 0, () => "size must be positive (got " + minSize + "): likely integer overflow?"); |
| if (array.Length < minSize) |
| { |
| float[] newArray = new float[Oversize(minSize, RamUsageEstimator.NUM_BYTES_SINGLE)]; |
| Array.Copy(array, 0, newArray, 0, array.Length); |
| return newArray; |
| } |
| else |
| { |
| return array; |
| } |
| } |
| |
| public static float[] Grow(float[] array) |
| { |
| return Grow(array, 1 + array.Length); |
| } |
| |
| public static double[] Grow(double[] array, int minSize) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(minSize >= 0, () => "size must be positive (got " + minSize + "): likely integer overflow?"); |
| if (array.Length < minSize) |
| { |
| double[] newArray = new double[Oversize(minSize, RamUsageEstimator.NUM_BYTES_DOUBLE)]; |
| Array.Copy(array, 0, newArray, 0, array.Length); |
| return newArray; |
| } |
| else |
| { |
| return array; |
| } |
| } |
| |
| public static double[] Grow(double[] array) |
| { |
| return Grow(array, 1 + array.Length); |
| } |
| |
| public static short[] Shrink(short[] array, int targetSize) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(targetSize >= 0, () => "size must be positive (got " + targetSize + "): likely integer overflow?"); |
| int newSize = GetShrinkSize(array.Length, targetSize, RamUsageEstimator.NUM_BYTES_INT16); |
| if (newSize != array.Length) |
| { |
| short[] newArray = new short[newSize]; |
| Array.Copy(array, 0, newArray, 0, newSize); |
| return newArray; |
| } |
| else |
| { |
| return array; |
| } |
| } |
| |
| public static int[] Grow(int[] array, int minSize) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(minSize >= 0, () => "size must be positive (got " + minSize + "): likely integer overflow?"); |
| if (array.Length < minSize) |
| { |
| int[] newArray = new int[Oversize(minSize, RamUsageEstimator.NUM_BYTES_INT32)]; |
| Array.Copy(array, 0, newArray, 0, array.Length); |
| return newArray; |
| } |
| else |
| { |
| return array; |
| } |
| } |
| |
| public static int[] Grow(int[] array) |
| { |
| return Grow(array, 1 + array.Length); |
| } |
| |
| public static int[] Shrink(int[] array, int targetSize) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(targetSize >= 0, () => "size must be positive (got " + targetSize + "): likely integer overflow?"); |
| int newSize = GetShrinkSize(array.Length, targetSize, RamUsageEstimator.NUM_BYTES_INT32); |
| if (newSize != array.Length) |
| { |
| int[] newArray = new int[newSize]; |
| Array.Copy(array, 0, newArray, 0, newSize); |
| return newArray; |
| } |
| else |
| { |
| return array; |
| } |
| } |
| |
| public static long[] Grow(long[] array, int minSize) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(minSize >= 0, () => "size must be positive (got " + minSize + "): likely integer overflow?"); |
| if (array.Length < minSize) |
| { |
| long[] newArray = new long[Oversize(minSize, RamUsageEstimator.NUM_BYTES_INT64)]; |
| Array.Copy(array, 0, newArray, 0, array.Length); |
| return newArray; |
| } |
| else |
| { |
| return array; |
| } |
| } |
| |
| public static long[] Grow(long[] array) |
| { |
| return Grow(array, 1 + array.Length); |
| } |
| |
| public static long[] Shrink(long[] array, int targetSize) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(targetSize >= 0, () => "size must be positive (got " + targetSize + "): likely integer overflow?"); |
| int newSize = GetShrinkSize(array.Length, targetSize, RamUsageEstimator.NUM_BYTES_INT64); |
| if (newSize != array.Length) |
| { |
| long[] newArray = new long[newSize]; |
| Array.Copy(array, 0, newArray, 0, newSize); |
| return newArray; |
| } |
| else |
| { |
| return array; |
| } |
| } |
| |
| [CLSCompliant(false)] |
| public static sbyte[] Grow(sbyte[] array, int minSize) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(minSize >= 0, () => "size must be positive (got " + minSize + "): likely integer overflow?"); |
| if (array.Length < minSize) |
| { |
| var newArray = new sbyte[Oversize(minSize, 1)]; |
| Array.Copy(array, 0, newArray, 0, array.Length); |
| return newArray; |
| } |
| else |
| { |
| return array; |
| } |
| } |
| |
| public static byte[] Grow(byte[] array, int minSize) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(minSize >= 0, () => "size must be positive (got " + minSize + "): likely integer overflow?"); |
| if (array.Length < minSize) |
| { |
| byte[] newArray = new byte[Oversize(minSize, 1)]; |
| Array.Copy(array, 0, newArray, 0, array.Length); |
| return newArray; |
| } |
| else |
| { |
| return array; |
| } |
| } |
| |
| public static byte[] Grow(byte[] array) |
| { |
| return Grow(array, 1 + array.Length); |
| } |
| |
| public static byte[] Shrink(byte[] array, int targetSize) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(targetSize >= 0, () => "size must be positive (got " + targetSize + "): likely integer overflow?"); |
| int newSize = GetShrinkSize(array.Length, targetSize, 1); |
| if (newSize != array.Length) |
| { |
| var newArray = new byte[newSize]; |
| Array.Copy(array, 0, newArray, 0, newSize); |
| return newArray; |
| } |
| else |
| { |
| return array; |
| } |
| } |
| |
| public static bool[] Grow(bool[] array, int minSize) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(minSize >= 0, () => "size must be positive (got " + minSize + "): likely integer overflow?"); |
| if (array.Length < minSize) |
| { |
| bool[] newArray = new bool[Oversize(minSize, 1)]; |
| Array.Copy(array, 0, newArray, 0, array.Length); |
| return newArray; |
| } |
| else |
| { |
| return array; |
| } |
| } |
| |
| public static bool[] Grow(bool[] array) |
| { |
| return Grow(array, 1 + array.Length); |
| } |
| |
| public static bool[] Shrink(bool[] array, int targetSize) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(targetSize >= 0, () => "size must be positive (got " + targetSize + "): likely integer overflow?"); |
| int newSize = GetShrinkSize(array.Length, targetSize, 1); |
| if (newSize != array.Length) |
| { |
| bool[] newArray = new bool[newSize]; |
| Array.Copy(array, 0, newArray, 0, newSize); |
| return newArray; |
| } |
| else |
| { |
| return array; |
| } |
| } |
| |
| public static char[] Grow(char[] array, int minSize) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(minSize >= 0, () => "size must be positive (got " + minSize + "): likely integer overflow?"); |
| if (array.Length < minSize) |
| { |
| char[] newArray = new char[Oversize(minSize, RamUsageEstimator.NUM_BYTES_CHAR)]; |
| Array.Copy(array, 0, newArray, 0, array.Length); |
| return newArray; |
| } |
| else |
| { |
| return array; |
| } |
| } |
| |
| public static char[] Grow(char[] array) |
| { |
| return Grow(array, 1 + array.Length); |
| } |
| |
| public static char[] Shrink(char[] array, int targetSize) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(targetSize >= 0, () => "size must be positive (got " + targetSize + "): likely integer overflow?"); |
| int newSize = GetShrinkSize(array.Length, targetSize, RamUsageEstimator.NUM_BYTES_CHAR); |
| if (newSize != array.Length) |
| { |
| char[] newArray = new char[newSize]; |
| Array.Copy(array, 0, newArray, 0, newSize); |
| return newArray; |
| } |
| else |
| { |
| return array; |
| } |
| } |
| |
| [CLSCompliant(false)] |
| public static int[][] Grow(int[][] array, int minSize) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(minSize >= 0, () => "size must be positive (got " + minSize + "): likely integer overflow?"); |
| if (array.Length < minSize) |
| { |
| var newArray = new int[Oversize(minSize, RamUsageEstimator.NUM_BYTES_OBJECT_REF)][]; |
| Array.Copy(array, 0, newArray, 0, array.Length); |
| return newArray; |
| } |
| else |
| { |
| return array; |
| } |
| } |
| |
| [CLSCompliant(false)] |
| public static int[][] Grow(int[][] array) |
| { |
| return Grow(array, 1 + array.Length); |
| } |
| |
| [CLSCompliant(false)] |
| public static int[][] Shrink(int[][] array, int targetSize) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(targetSize >= 0, () => "size must be positive (got " + targetSize + "): likely integer overflow?"); |
| int newSize = GetShrinkSize(array.Length, targetSize, RamUsageEstimator.NUM_BYTES_OBJECT_REF); |
| if (newSize != array.Length) |
| { |
| int[][] newArray = new int[newSize][]; |
| Array.Copy(array, 0, newArray, 0, newSize); |
| return newArray; |
| } |
| else |
| { |
| return array; |
| } |
| } |
| |
| [CLSCompliant(false)] |
| public static float[][] Grow(float[][] array, int minSize) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(minSize >= 0, () => "size must be positive (got " + minSize + "): likely integer overflow?"); |
| if (array.Length < minSize) |
| { |
| float[][] newArray = new float[Oversize(minSize, RamUsageEstimator.NUM_BYTES_OBJECT_REF)][]; |
| Array.Copy(array, 0, newArray, 0, array.Length); |
| return newArray; |
| } |
| else |
| { |
| return array; |
| } |
| } |
| |
| [CLSCompliant(false)] |
| public static float[][] Grow(float[][] array) |
| { |
| return Grow(array, 1 + array.Length); |
| } |
| |
| [CLSCompliant(false)] |
| public static float[][] Shrink(float[][] array, int targetSize) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(targetSize >= 0, () => "size must be positive (got " + targetSize + "): likely integer overflow?"); |
| int newSize = GetShrinkSize(array.Length, targetSize, RamUsageEstimator.NUM_BYTES_OBJECT_REF); |
| if (newSize != array.Length) |
| { |
| float[][] newArray = new float[newSize][]; |
| Array.Copy(array, 0, newArray, 0, newSize); |
| return newArray; |
| } |
| else |
| { |
| return array; |
| } |
| } |
| |
| /// <summary> |
| /// Returns hash of chars in range start (inclusive) to |
| /// end (inclusive) |
| /// </summary> |
| public static int GetHashCode(char[] array, int start, int end) |
| { |
| int code = 0; |
| for (int i = end - 1; i >= start; i--) |
| { |
| code = code * 31 + array[i]; |
| } |
| return code; |
| } |
| |
| /// <summary> |
| /// Returns hash of bytes in range start (inclusive) to |
| /// end (inclusive) |
| /// </summary> |
| public static int GetHashCode(byte[] array, int start, int end) |
| { |
| int code = 0; |
| for (int i = end - 1; i >= start; i--) |
| { |
| code = code * 31 + array[i]; |
| } |
| return code; |
| } |
| |
| // Since Arrays.equals doesn't implement offsets for equals |
| /// <summary> |
| /// See if two array slices are the same. |
| /// </summary> |
| /// <param name="left"> The left array to compare </param> |
| /// <param name="offsetLeft"> The offset into the array. Must be positive </param> |
| /// <param name="right"> The right array to compare </param> |
| /// <param name="offsetRight"> the offset into the right array. Must be positive </param> |
| /// <param name="length"> The length of the section of the array to compare </param> |
| /// <returns> <c>true</c> if the two arrays, starting at their respective offsets, are equal |
| /// </returns> |
| /// <seealso cref="Support.Arrays.Equals{T}(T[], T[])"/> |
| public static bool Equals(char[] left, int offsetLeft, char[] right, int offsetRight, int length) |
| { |
| if ((offsetLeft + length <= left.Length) && (offsetRight + length <= right.Length)) |
| { |
| for (int i = 0; i < length; i++) |
| { |
| if (left[offsetLeft + i] != right[offsetRight + i]) |
| { |
| return false; |
| } |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| // Since Arrays.equals doesn't implement offsets for equals |
| /// <summary> |
| /// See if two array slices are the same. |
| /// </summary> |
| /// <param name="left"> The left array to compare </param> |
| /// <param name="offsetLeft"> The offset into the array. Must be positive </param> |
| /// <param name="right"> The right array to compare </param> |
| /// <param name="offsetRight"> the offset into the right array. Must be positive </param> |
| /// <param name="length"> The length of the section of the array to compare </param> |
| /// <returns> <c>true</c> if the two arrays, starting at their respective offsets, are equal |
| /// </returns> |
| /// <seealso cref="Support.Arrays.Equals{T}(T[], T[])"/> |
| public static bool Equals(byte[] left, int offsetLeft, byte[] right, int offsetRight, int length) |
| { |
| if ((offsetLeft + length <= left.Length) && (offsetRight + length <= right.Length)) |
| { |
| for (int i = 0; i < length; i++) |
| { |
| if (left[offsetLeft + i] != right[offsetRight + i]) |
| { |
| return false; |
| } |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| /* DISABLE this FOR NOW: this has performance problems until Java creates intrinsics for Class#getComponentType() and Array.newInstance() |
| public static <T> T[] grow(T[] array, int minSize) { |
| assert minSize >= 0: "size must be positive (got " + minSize + "): likely integer overflow?"; |
| if (array.length < minSize) { |
| @SuppressWarnings("unchecked") final T[] newArray = |
| (T[]) Array.newInstance(array.getClass().getComponentType(), oversize(minSize, RamUsageEstimator.NUM_BYTES_OBJECT_REF)); |
| System.arraycopy(array, 0, newArray, 0, array.length); |
| return newArray; |
| } else |
| return array; |
| } |
| |
| public static <T> T[] grow(T[] array) { |
| return grow(array, 1 + array.length); |
| } |
| |
| public static <T> T[] shrink(T[] array, int targetSize) { |
| assert targetSize >= 0: "size must be positive (got " + targetSize + "): likely integer overflow?"; |
| final int newSize = getShrinkSize(array.length, targetSize, RamUsageEstimator.NUM_BYTES_OBJECT_REF); |
| if (newSize != array.length) { |
| @SuppressWarnings("unchecked") final T[] newArray = |
| (T[]) Array.newInstance(array.getClass().getComponentType(), newSize); |
| System.arraycopy(array, 0, newArray, 0, newSize); |
| return newArray; |
| } else |
| return array; |
| } |
| */ |
| |
| // Since Arrays.equals doesn't implement offsets for equals |
| /// <summary> |
| /// See if two array slices are the same. |
| /// </summary> |
| /// <param name="left"> The left array to compare </param> |
| /// <param name="offsetLeft"> The offset into the array. Must be positive </param> |
| /// <param name="right"> The right array to compare </param> |
| /// <param name="offsetRight"> the offset into the right array. Must be positive </param> |
| /// <param name="length"> The length of the section of the array to compare </param> |
| /// <returns> <c>true</c> if the two arrays, starting at their respective offsets, are equal |
| /// </returns> |
| /// <seealso cref="Support.Arrays.Equals{T}(T[], T[])"/> |
| public static bool Equals(int[] left, int offsetLeft, int[] right, int offsetRight, int length) |
| { |
| if ((offsetLeft + length <= left.Length) && (offsetRight + length <= right.Length)) |
| { |
| for (int i = 0; i < length; i++) |
| { |
| if (left[offsetLeft + i] != right[offsetRight + i]) |
| { |
| return false; |
| } |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| /// <summary> |
| /// NOTE: This was toIntArray() in Lucene |
| /// </summary> |
| public static int[] ToInt32Array(ICollection<int?> ints) |
| { |
| int[] result = new int[ints.Count]; |
| int upto = 0; |
| foreach (int? v in ints) |
| { |
| if (v.HasValue) |
| { |
| result[upto++] = v.Value; |
| } |
| else |
| { |
| throw new NullReferenceException(); // LUCENENET NOTE: This is the same behavior you get in Java when setting int = Integer and the integer is null. |
| } |
| } |
| |
| // paranoia: |
| if (Debugging.AssertsEnabled) Debugging.Assert(upto == result.Length); |
| |
| return result; |
| } |
| |
| // LUCENENET specific - we don't have an IComparable<T> constraint - |
| // the logic of GetNaturalComparer<T> handles that so we just |
| // do a cast here. |
| #if FEATURE_SERIALIZABLE |
| [Serializable] |
| #endif |
| private class NaturalComparer<T> : IComparer<T> //where T : IComparable<T> |
| { |
| private NaturalComparer() |
| { |
| } |
| |
| public static NaturalComparer<T> Default { get; } = new NaturalComparer<T>(); |
| |
| public virtual int Compare(T o1, T o2) |
| { |
| return ((IComparable<T>)o1).CompareTo(o2); |
| } |
| } |
| |
| /// <summary> |
| /// Get the natural <see cref="IComparer{T}"/> for the provided object class. |
| /// <para/> |
| /// The comparer returned depends on the <typeparam name="T"/> argument: |
| /// <list type="number"> |
| /// <item><description>If the type is <see cref="string"/>, the comparer returned uses |
| /// the <see cref="string.CompareOrdinal(string, string)"/> to make the comparison |
| /// to ensure that the current culture doesn't affect the results. This is the |
| /// default string comparison used in Java, and what Lucene's design depends on.</description></item> |
| /// <item><description>If the type implements <see cref="IComparable{T}"/>, the comparer uses |
| /// <see cref="IComparable{T}.CompareTo(T)"/> for the comparison. This allows |
| /// the use of types with custom comparison schemes.</description></item> |
| /// <item><description>If neither of the above conditions are true, will default to <see cref="Comparer{T}.Default"/>.</description></item> |
| /// </list> |
| /// <para/> |
| /// NOTE: This was naturalComparer() in Lucene |
| /// </summary> |
| public static IComparer<T> GetNaturalComparer<T>() |
| //where T : IComparable<T> // LUCENENET specific: removing constraint because in .NET, it is not needed |
| { |
| Type genericClosingType = typeof(T); |
| |
| // LUCENENET specific - we need to ensure that strings are compared |
| // in a culture-insenitive manner. |
| if (genericClosingType.Equals(typeof(string))) |
| { |
| return (IComparer<T>)StringComparer.Ordinal; |
| } |
| // LUCENENET specific - Only return the NaturalComparer if the type |
| // implements IComparable<T>, otherwise use Comparer<T>.Default. |
| // This allows the comparison to be customized, but it is not mandatory |
| // to implement IComparable<T>. |
| else if (typeof(IComparable<T>).IsAssignableFrom(genericClosingType)) |
| { |
| return NaturalComparer<T>.Default; |
| } |
| |
| return Comparer<T>.Default; |
| } |
| |
| /// <summary> |
| /// Swap values stored in slots <paramref name="i"/> and <paramref name="j"/> </summary> |
| public static void Swap<T>(T[] arr, int i, int j) |
| { |
| T tmp = arr[i]; |
| arr[i] = arr[j]; |
| arr[j] = tmp; |
| } |
| |
| // intro-sorts |
| |
| /// <summary> |
| /// Sorts the given array slice using the <see cref="IComparer{T}"/>. This method uses the intro sort |
| /// algorithm, but falls back to insertion sort for small arrays. </summary> |
| /// <param name="fromIndex"> Start index (inclusive) </param> |
| /// <param name="toIndex"> End index (exclusive) </param> |
| public static void IntroSort<T>(T[] a, int fromIndex, int toIndex, IComparer<T> comp) |
| { |
| if (toIndex - fromIndex <= 1) |
| { |
| return; |
| } |
| (new ArrayIntroSorter<T>(a, comp)).Sort(fromIndex, toIndex); |
| } |
| |
| /// <summary> |
| /// Sorts the given array using the <see cref="IComparer{T}"/>. This method uses the intro sort |
| /// algorithm, but falls back to insertion sort for small arrays. |
| /// </summary> |
| public static void IntroSort<T>(T[] a, IComparer<T> comp) |
| { |
| IntroSort(a, 0, a.Length, comp); |
| } |
| |
| /// <summary> |
| /// Sorts the given array slice in natural order. This method uses the intro sort |
| /// algorithm, but falls back to insertion sort for small arrays. </summary> |
| /// <param name="fromIndex"> Start index (inclusive) </param> |
| /// <param name="toIndex"> End index (exclusive) </param> |
| public static void IntroSort<T>(T[] a, int fromIndex, int toIndex) //where T : IComparable<T> // LUCENENET specific: removing constraint because in .NET, it is not needed |
| { |
| if (toIndex - fromIndex <= 1) |
| { |
| return; |
| } |
| IntroSort(a, fromIndex, toIndex, ArrayUtil.GetNaturalComparer<T>()); |
| } |
| |
| /// <summary> |
| /// Sorts the given array in natural order. This method uses the intro sort |
| /// algorithm, but falls back to insertion sort for small arrays. |
| /// </summary> |
| public static void IntroSort<T>(T[] a) //where T : IComparable<T> // LUCENENET specific: removing constraint because in .NET, it is not needed |
| { |
| IntroSort(a, 0, a.Length); |
| } |
| |
| // tim sorts: |
| |
| /// <summary> |
| /// Sorts the given array slice using the <see cref="IComparer{T}"/>. This method uses the Tim sort |
| /// algorithm, but falls back to binary sort for small arrays. </summary> |
| /// <param name="fromIndex"> Start index (inclusive) </param> |
| /// <param name="toIndex"> End index (exclusive) </param> |
| public static void TimSort<T>(T[] a, int fromIndex, int toIndex, IComparer<T> comp) |
| { |
| if (toIndex - fromIndex <= 1) |
| { |
| return; |
| } |
| (new ArrayTimSorter<T>(a, comp, a.Length / 64)).Sort(fromIndex, toIndex); |
| } |
| |
| /// <summary> |
| /// Sorts the given array using the <see cref="IComparer{T}"/>. this method uses the Tim sort |
| /// algorithm, but falls back to binary sort for small arrays. |
| /// </summary> |
| public static void TimSort<T>(T[] a, IComparer<T> comp) |
| { |
| TimSort(a, 0, a.Length, comp); |
| } |
| |
| /// <summary> |
| /// Sorts the given array slice in natural order. this method uses the Tim sort |
| /// algorithm, but falls back to binary sort for small arrays. </summary> |
| /// <param name="fromIndex"> Start index (inclusive) </param> |
| /// <param name="toIndex"> End index (exclusive) </param> |
| public static void TimSort<T>(T[] a, int fromIndex, int toIndex) //where T : IComparable<T> // LUCENENET specific: removing constraint because in .NET, it is not needed |
| { |
| if (toIndex - fromIndex <= 1) |
| { |
| return; |
| } |
| TimSort(a, fromIndex, toIndex, ArrayUtil.GetNaturalComparer<T>()); |
| } |
| |
| /// <summary> |
| /// Sorts the given array in natural order. this method uses the Tim sort |
| /// algorithm, but falls back to binary sort for small arrays. |
| /// </summary> |
| public static void TimSort<T>(T[] a) //where T : IComparable<T> // LUCENENET specific: removing constraint because in .NET, it is not needed |
| { |
| TimSort(a, 0, a.Length); |
| } |
| } |
| } |