/Utilities/Helpers/ArrayHelper.cs
C# | 1690 lines | 1028 code | 116 blank | 546 comment | 152 complexity | 16ca740437cf6fdf9e0a239565ae6d1b MD5 | raw file
Possible License(s): Apache-2.0
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.IO;
- using System.Text;
- using NUnit.Framework;
-
- namespace Delta.Utilities.Helpers
- {
- /// <summary>
- /// Array helper class adding extra functionality to the System.Array class
- /// and similar classes (lists). Most useful methods here are resizing
- /// arrays, building sub arrays and adding arrays together.
- /// </summary>
- public static class ArrayHelper
- {
- #region Delegates
- /// <summary>
- /// Parameterless delegate for code that should be invoked by the
- /// FreeDataWhenMemoryIsLow event handler.
- /// </summary>
- public delegate void FreeMemoryDelegate();
- #endregion
-
- #region GetSubArray (Static)
- /// <summary>
- /// Get sub array (similar to String.Substring), again there is no function
- /// for arrays to do that easily.
- /// </summary>
- /// <typeparam name="T">Type of array</typeparam>
- /// <param name="length">
- /// Length of data to copy (can be smaller than originalData array size)
- /// </param>
- /// <param name="originalData">Data to grab from (full array)</param>
- /// <param name="startIndex">Start index for originalData</param>
- /// <returns>New array of type T with the specified length</returns>
- /// <exception cref="ArgumentOutOfRangeException">
- /// <c>startIndex</c> is out of range.
- /// </exception>
- public static T[] GetSubArray<T>(
- this T[] originalData, int startIndex, int length)
- {
- if (startIndex > originalData.Length)
- {
- throw new ArgumentOutOfRangeException("startIndex",
- "Must be smaller than originalData.Length=" + originalData.Length);
- }
- if (length > startIndex + originalData.Length)
- {
- throw new ArgumentOutOfRangeException("length",
- "Must be smaller than startIndex=" + startIndex +
- "+originalData.Length=" + originalData.Length);
- }
-
- T[] ret = new T[length];
- Array.Copy(originalData, startIndex, ret, 0, length);
- return ret;
- }
-
- /// <summary>
- /// Get sub array (similar to String.Substring), again there is no function
- /// for arrays to do that easily.
- /// </summary>
- /// <typeparam name="T">Type of array</typeparam>
- /// <param name="originalData">Original Data</param>
- /// <param name="startIndex">Start Index</param>
- public static T[] GetSubArray<T>(this T[] originalData, int startIndex)
- {
- return GetSubArray(originalData, startIndex,
- originalData.Length - startIndex);
- }
- #endregion
-
- #region Resize (Static)
- /// <summary>
- /// Resize array, will create a new array with specified size, then copy
- /// all data (with Array.Copy, which is fast) and return the new array.
- /// There is no other faster possibility for managed arrays in C#!
- /// </summary>
- /// <param name="sourceArray">Source array to grab data from, can be
- /// bigger or smaller.</param>
- /// <param name="newLength">New length for the array we return here</param>
- /// <returns>The reduced or increased array</returns>
- public static Array Resize(this Array sourceArray, int newLength)
- {
- // Construct array of same type
- Array newArray = Array.CreateInstance(
- sourceArray.GetType().GetElementType(),
- newLength);
- // Copy data
- Array.Copy(sourceArray, 0, newArray, 0,
- // Don't copy more than any of these 2 arrays can handle!
- Math.Min(sourceArray.Length, newArray.Length));
- // Return resized array
- return newArray;
- }
-
- /// <summary>
- /// If you need to resize a generic list into another generic list,
- /// use Resize. But Resize is slow, the generation of the new list
- /// takes some time, use this function to speed that up to 50% faster.
- /// It will return a simple array, if this is whats you need you
- /// have almost as good as the performance of the normal Resize method.
- /// </summary>
- /// <typeparam name="T">Type for the sourceArray list</typeparam>
- /// <param name="sourceArray">Input</param>
- /// <param name="newLength">New desired length for resized array</param>
- /// <returns>Resized array, if you need a list, use ResizeSlow</returns>
- public static T[] Resize<T>(this List<T> sourceArray, int newLength)
- {
- // Construct array of same type
- T[] newArray = new T[newLength];
- // Copy data
- sourceArray.CopyTo(0, newArray, 0,
- // Don't copy more than any of these 2 arrays can handle!
- Math.Min(sourceArray.Count, newArray.Length));
- // Return resized array
- return newArray;
- }
- #endregion
-
- #region IsEqualWithoutOrder (Static)
- /// <summary>
- /// Returns 'true' if both list matches, this means, they have the same
- /// elements but the order of the elements doesn't matter.
- /// </summary>
- /// <typeparam name="T">Type of arrays to compare</typeparam>
- /// <param name="list1">List 1</param>
- /// <param name="list2">List 2</param>
- /// <returns>Returns 'true' if both list matches</returns>
- public static bool IsEqualWithoutOrder<T>(this T[] list1, T[] list2)
- {
- #region Validation check
- if (list1 == null || list2 == null ||
- list1.Length != list2.Length)
- {
- return false;
- }
- #endregion
-
- // Just iterate over all list values
- for (int i = 0; i < list1.Length; i++)
- {
- // and look
- bool isNotFound = true;
- for (int j = 0; j < list2.Length; j++)
- {
- // if the current value of the first list exists somewhere at any
- // position in the other list
- if (list1[i].Equals(list2[j]))
- {
- // Ok found ^^
- isNotFound = false;
- break;
- }
- }
-
- // else if the current value doesn't exist in the other list, then they
- // don't match (even by ignoring the ordering)
- if (isNotFound)
- {
- return false;
- }
- }
-
- // We haven't jumped out yet, so both lists are equal :)
- return true;
- }
- #endregion
-
- #region Compare (Static)
- /// <summary>
- /// Compare 2 generic Lists. Returns 'true' if both list matches, this
- /// means, they have the same elements in the same order.
- /// </summary>
- /// <typeparam name="T">Type of lists to compare</typeparam>
- /// <param name="list1">List 1</param>
- /// <param name="list2">List 2</param>
- /// <returns>Returns 'true' if both list matches</returns>
- public static bool Compare<T>(IList<T> list1, IList<T> list2)
- where T : IEquatable<T>
- {
- // If both lists are exactly equal, we can skip checking
- if (list1 == list2)
- {
- return true;
- }
-
- // Validation check
- if (list1 == null ||
- list2 == null ||
- list1.Count != list2.Count)
- {
- return false;
- }
-
- // Go through the whole list and abort if anything is different
- for (int num = 0; num < list1.Count; num++)
- {
- if (list1[num].Equals(list2[num]) == false)
- {
- return false;
- }
- }
-
- // Everything was checked, both lists have the same values
- return true;
- }
-
- /// <summary>
- /// Compare 2 arrays, optimized for bytes, which are compared quite often.
- /// Returns 'true' if both list matches, this means, they have the same
- /// elements in the same order.
- /// </summary>
- /// <param name="list1">First bytes array</param>
- /// <param name="list2">Second bytes array</param>
- /// <returns>Returns 'true' if both list matches</returns>
- public static bool Compare(byte[] list1, byte[] list2)
- {
- // If both lists are exactly equal, we can skip checking
- if (list1 == list2)
- {
- return true;
- }
-
- // Validation check
- if (list1 == null ||
- list2 == null ||
- list1.Length != list2.Length)
- {
- return false;
- }
-
- // Go through the whole list and abort if anything is different
- for (int num = 0; num < list1.Length; num++)
- {
- if (list1[num] != list2[num])
- {
- return false;
- }
- }
-
- // Everything was checked, both lists have the same values
- return true;
- }
- #endregion
-
- #region Add (Static)
- /// <summary>
- /// Adds 2 arrays, pretty simple, but not implemented in Array!
- /// </summary>
- /// <param name="array1">Array 1</param>
- /// <param name="array2">Array 2</param>
- /// <returns>Big new array containing array1 and array2</returns>
- public static Array Add(this Array array1, Array array2)
- {
- // Construct array of same type
- Array newArray = Array.CreateInstance(
- array1.GetType().GetElementType(),
- array1.Length + array2.Length);
- // Copy data
- Array.Copy(array1, 0, newArray, 0, array1.Length);
- Array.Copy(array2, 0, newArray, array1.Length, array2.Length);
- // Return new array
- return newArray;
- }
-
- /// <summary>
- /// Adds 2 arrays, pretty simple, but not implemented in Array!
- /// </summary>
- /// <param name="array1">Array 1</param>
- /// <param name="array2">Array 2</param>
- /// <returns>Big new array containing array1 and array2</returns>
- public static byte[] Add(this byte[] array1, byte[] array2)
- {
- // Construct array of same type
- byte[] newArray = new byte[array1.Length + array2.Length];
- // Copy data
- Array.Copy(array1, 0, newArray, 0, array1.Length);
- Array.Copy(array2, 0, newArray, array1.Length, array2.Length);
- // Return new array
- return newArray;
- }
-
- /// <summary>
- /// For generic lists we can simply use .AddRange to do this!
- /// Use this method only if you want to create a new list from the
- /// first list + all data in the second list.
- /// </summary>
- /// <typeparam name="T">Type of lists</typeparam>
- /// <param name="list1">List 1</param>
- /// <param name="list2">List 2</param>
- /// <returns>Big new array containing array1 and array2</returns>
- public static List<T> Add<T>(this List<T> list1, List<T> list2)
- {
- List<T> retList = new List<T>(list1);
- retList.AddRange(list2);
- return retList;
- }
- #endregion
-
- #region ConvertStringToIntArray (Static)
- /// <summary>
- /// Convert string data to int array, string must be in the form
- /// "1, 3, 8, 7", etc. WriteArrayData is the complementary function.
- /// </summary>
- /// <param name="text">Input text to be split up, if empty this method
- /// will return an empty array.</param>
- /// <returns>int array, will be null if string is invalid!</returns>
- public static int[] ConvertStringToIntArray(string text)
- {
- // Invalid?
- if (text == null ||
- text.Length == 0)
- {
- return null;
- }
-
- string[] splitted = text.Split(new[]
- {
- ',', ' '
- },
- StringSplitOptions.RemoveEmptyEntries);
- List<int> ret = new List<int>(); //new int[splitted.Length];
- for (int i = 0; i < splitted.Length; i++)
- {
- int newValue;
- if (int.TryParse(splitted[i], out newValue))
- {
- ret.Add(newValue);
- }
- }
-
- return ret.ToArray();
- }
- #endregion
-
- #region ConvertByteArrayToIntArray (Static)
- /// <summary>
- /// Convert byte array to int array, we use 4 bytes and combine them
- /// into 1 int (first byte gets shifted 24 bits, next 16, next 8, next 0)
- /// </summary>
- /// <param name="byteArray">Byte Array</param>
- /// <returns>Integer array, has byteArray.Length/4 Length!</returns>
- public static int[] ConvertByteArrayToIntArray(byte[] byteArray)
- {
- // Invalid or too small?
- if (byteArray == null ||
- byteArray.Length < 4)
- {
- return null;
- }
-
- // 8bit per element to 32bit = /4
- int[] ret = new int[byteArray.Length / 4];
- for (int i = 0; i < ret.Length; i++)
- {
- ret[i] =
- (byteArray[i * 4 + 0]) +
- (byteArray[i * 4 + 1] << 8) +
- (byteArray[i * 4 + 2] << 16) +
- (byteArray[i * 4 + 3] << 24);
- }
- return ret;
- }
- #endregion
-
- #region ConvertIntArrayToByteArray (Static)
- /// <summary>
- /// Convert int array to byte array, we split each int into 4 bytes
- /// (first byte gets shifted 24 bits, next 16, next 8, next 0)
- /// </summary>
- /// <param name="intArray">Integer array</param>
- /// <returns>Byte array, has intArray.Length*4 Length!</returns>
- public static byte[] ConvertIntArrayToByteArray(int[] intArray)
- {
- // Invalid?
- if (intArray == null)
- {
- return null;
- }
-
- // 32bit per element to 8bit = *4
- byte[] ret = new byte[intArray.Length * 4];
- for (int i = 0; i < ret.Length / 4; i++)
- {
- ret[i * 4 + 0] = (byte)(intArray[i]);
- ret[i * 4 + 1] = (byte)(intArray[i] >> 8);
- ret[i * 4 + 2] = (byte)(intArray[i] >> 16);
- ret[i * 4 + 3] = (byte)(intArray[i] >> 24);
- }
- return ret;
- }
- #endregion
-
- #region ConvertByteArrayToString (Static)
- /// <summary>
- /// Convert a byte array to a string, converts each byte to a char
- /// and add them up to the returning string.
- /// Useful for generating string data from a byte stream (ANSI style).
- /// </summary>
- /// <param name="byteArray">Byte array to write from (interpreted as char
- /// values)</param>
- /// <returns>String with all char values from the byteArray</returns>
- public static string ConvertByteArrayToString(byte[] byteArray)
- {
- if (byteArray == null)
- {
- return "";
- }
-
- StringBuilder ret = new StringBuilder(byteArray.Length);
- for (int num = 0; num < byteArray.Length; num++)
- {
- ret.Append((char)byteArray[num]);
- }
- return ret.ToString();
- }
- #endregion
-
- #region ToText (Static)
- /// <summary>
- /// Converts the list of chars to a string.
- /// </summary>
- /// <param name="charList">Char list</param>
- public static string ToText(this IList<char> charList)
- {
- if (charList == null)
- {
- return "";
- } // if
-
- // Note: We use here the StringBuilder because its appending of chars is
- // 20x faster than by a string
- StringBuilder buildString = new StringBuilder(charList.Count);
- for (int charId = 0; charId < charList.Count; charId++)
- {
- buildString.Append(charList[charId]);
- }
-
- return buildString.ToString();
- }
- #endregion
-
- #region RemoveIndex (Static)
- /// <summary>
- /// Remove a specific array element and return reduced array,
- /// often used in ArrayLists, but its more complicated for normal arrays!
- /// <para />
- /// Note: This method is slower than using dynamic lists.
- /// </summary>
- /// <typeparam name="T">Type of array data</typeparam>
- /// <param name="originalArray">Original Array</param>
- /// <param name="index">Index in originalArray</param>
- /// <returns>New array with the index removed</returns>
- public static T[] RemoveIndex<T>(T[] originalArray, int index)
- {
- if (index < 0 || originalArray == null ||
- index >= originalArray.Length ||
- originalArray.Length <= 1)
- {
- return originalArray;
- }
- T[] ret = new T[originalArray.Length - 1];
-
- // First part until index
- for (int i = 0; i < index; i++)
- {
- ret[i] = originalArray[i];
- }
-
- // Second part from index+1 to end
- for (int i = index; i < ret.Length; i++)
- {
- ret[i] = originalArray[i + 1];
- }
- return ret;
- }
- #endregion
-
- #region SetBitInByteArray (Static)
- /// <summary>
- /// Set bit in byte array
- /// </summary>
- /// <param name="byteArray">Byte Array</param>
- /// <param name="pos">Position to set bit (in bit position)</param>
- /// <param name="val">Value to set, either true (1) or false (0)</param>
- /// <exception cref="IndexOutOfRangeException">
- /// Throws index out of range if the position is more than array.Length*8.
- /// </exception>
- public static void SetBitInByteArray(
- byte[] byteArray, int pos, bool val)
- {
- int bytePos = pos / 8;
- if (bytePos >= byteArray.Length)
- {
- throw new IndexOutOfRangeException(
- "SetBitInByteArray invalid pos=" + pos +
- " index, byteArray.Length=" + byteArray.Length);
- }
-
- // Set bit at specified place or clear it
- if (val)
- {
- // Just bitwise or with specified bit
- byteArray[bytePos] |= (byte)(1 << (pos % 8));
- }
- else
- {
- // Create inverted bitmask (only bit position is set to 0)
- // then and this with given byte and we have cleared it!
- byteArray[bytePos] &= (byte)(~(1 << (pos % 8)));
- }
- }
- #endregion
-
- #region GetBitInByteArray (Static)
- /// <summary>
- /// Get bit in byte array
- /// </summary>
- /// <param name="byteArray">Byte Array</param>
- /// <param name="pos">Position to set bit (in bit position)</param>
- /// <returns>Value to at bit position in byteArray, true (1) or false (0)
- /// </returns>
- /// <exception cref="IndexOutOfRangeException">
- /// Throws index out of range if the position is more than array.Length*8.
- /// </exception>
- public static bool GetBitInByteArray(
- byte[] byteArray, int pos)
- {
- int bytePos = pos / 8;
- if (bytePos >= byteArray.Length)
- {
- throw new IndexOutOfRangeException(
- "SetBitInByteArray invalid pos=" + pos +
- " index, byteArray.Length=" + byteArray.Length);
- }
-
- // Check if bit is set, we simply shift byte as
- // much bit positions as we need and check if value is 1 there!
- return (byteArray[bytePos] & (1 << (pos % 8))) != 0;
- }
- #endregion
-
- #region BuildCompressedByteArrayFromBoolArray (Static)
- /// <summary>
- /// Creates a byte array from bool array. E.g if bool array has 8 entries
- /// we can compress this into one single byte, if it has 21 entries we can
- /// use 3 bytes, etc.!
- /// </summary>
- /// <param name="boolArray">Bool array</param>
- /// <returns>Byte array containing all bits from the boolArray</returns>
- public static byte[] BuildCompressedByteArrayFromBoolArray(
- bool[] boolArray)
- {
- byte[] byteArray = new byte[1 + ((boolArray.Length - 1) / 8)];
- for (int i = 0; i < boolArray.Length; i++)
- {
- SetBitInByteArray(byteArray, i, boolArray[i]);
- }
- return byteArray;
- }
- #endregion
-
- #region FillBoolArrayFromCompressedByteArray (Static)
- /// <summary>
- /// Rebuilds a bool array compressed with
- /// BuildCompressedByteArrayFromBoolArray. E.g. we can extract 8 bool
- /// entries from a byte or 14 entries from 2 bytes, etc.
- /// </summary>
- /// <param name="boolArray">Bool array</param>
- /// <param name="byteArray">Byte array, with is 8 times smaller than
- /// boolArray</param>
- public static void FillBoolArrayFromCompressedByteArray(
- bool[] boolArray, byte[] byteArray)
- {
- for (int i = 0; i < boolArray.Length; i++)
- {
- boolArray[i] = GetBitInByteArray(byteArray, i);
- }
- }
- #endregion
-
- #region AreCollectionsEqual (Static)
- /// <summary>
- /// Determines if the two collections contain equal items in the same
- /// order. The two collections do not need to be of the same type; it is
- /// permissible to compare an array and an OrderedBag, for instance.
- /// </summary>
- /// <remarks>The default sense of equality for T is used, as defined by T's
- /// implementation of IComparable<T>.Equals or object.Equals.
- /// </remarks>
- /// <typeparam name="T">The type of items in the collections.</typeparam>
- /// <param name="collection1">The first collection to compare.</param>
- /// <param name="collection2">The second collection to compare.</param>
- /// <returns>True if the collections have equal items in the same order.
- /// If both collections are empty, true is returned.</returns>
- public static bool AreCollectionsEqual<T>(IEnumerable<T> collection1,
- IEnumerable<T> collection2)
- {
- return AreCollectionsEqual(collection1, collection2,
- EqualityComparer<T>.Default);
- }
-
- /// <summary>
- /// Determines if the two collections contain equal items in the same
- /// order. The passed instance of IEqualityComparer<T> is used for
- /// determining if two items are equal.
- /// </summary>
- /// <typeparam name="T">The type of items in the collections.</typeparam>
- /// <param name="collection1">The first collection to compare.</param>
- /// <param name="collection2">The second collection to compare.</param>
- /// <param name="equalityComparer">The IEqualityComparer<T> used to
- /// compare items for equality.
- /// Only the Equals member function of this interface is called.</param>
- /// <returns>True if the collections have equal items in the same order.
- /// If both collections are empty, true is returned.</returns>
- /// <exception cref="ArgumentNullException"><paramref name="collection1"/>,
- /// <paramref name="collection2"/>, or <paramref name="equalityComparer"/>
- /// is null.</exception>
- public static bool AreCollectionsEqual<T>(IEnumerable<T> collection1,
- IEnumerable<T> collection2, IEqualityComparer<T> equalityComparer)
- {
- if (collection1 == null)
- {
- throw new ArgumentNullException("collection1");
- }
- if (collection2 == null)
- {
- throw new ArgumentNullException("collection2");
- }
- if (equalityComparer == null)
- {
- throw new ArgumentNullException("equalityComparer");
- }
-
- using (IEnumerator<T> enum1 = collection1.GetEnumerator(),
- enum2 = collection2.GetEnumerator())
- {
- bool continue1, continue2;
-
- // Go through all items until done
- for (;;)
- {
- continue1 = enum1.MoveNext();
- continue2 = enum2.MoveNext();
- if (!continue1 ||
- !continue2)
- {
- break;
- }
-
- if (!equalityComparer.Equals(enum1.Current, enum2.Current))
- {
- // The two items are not equal.
- return false;
- }
- }
-
- // If both continue1 and continue2 are false, we reached the end of
- // both sequences at the same time and found success. If one is true
- // and one is false, the sequences were of difference lengths->failure.
- return (continue1 == continue2);
- }
- }
- #endregion
-
- #region ToArray (Static)
- /// <summary>
- /// Create an array with the items in a collection.
- /// </summary>
- /// <remarks>
- /// If <paramref name="collection"/> implements ICollection<T>T, then
- /// ICollection<T>.CopyTo() is used to fill the array. Otherwise, the
- /// IEnumerable<T>.GetEnumerator() is used to fill the array.
- /// </remarks>
- /// <typeparam name="T">Element type of the collection.</typeparam>
- /// <param name="collection">Collection to create array from.</param>
- /// <returns>
- /// An array with the items from the collection, in enumeration order.
- /// </returns>
- /// <exception cref="ArgumentNullException">
- /// <paramref name="collection"/> is null.
- /// </exception>
- public static T[] ToArray<T>(IEnumerable<T> collection)
- {
- if (collection == null)
- {
- throw new ArgumentNullException("collection");
- }
-
- ICollection<T> coll = collection as ICollection<T>;
- if (coll != null)
- {
- // Use ICollection methods to do it more efficiently.
- T[] array = new T[coll.Count];
- coll.CopyTo(array, 0);
- return array;
- }
- else
- {
- // We can't allocate the correct size array now, because IEnumerable
- // doesn't have a Count property. We could enumerate twice, once to
- // count and once to copy. Or we could enumerate once, copying to a
- // List, then copy the list to the correct size array. The latter
- // algorithm seems more efficient, although it allocates extra memory
- // for the list which is then discarded.
- List<T> list = new List<T>(collection);
- return list.ToArray();
- }
- }
- #endregion
-
- #region Write (Static)
- /// <summary>
- /// Returns a string with the array data separated with commas.
- /// This is the byte array version.
- /// </summary>
- /// <param name="array">Byte array</param>
- /// <returns>A string with the byte array data, comma separated</returns>
- public static string Write(this byte[] array)
- {
- string ret = "";
- if (array != null)
- {
- for (int i = 0; i < array.Length; i++)
- {
- ret +=
- (ret.Length == 0
- ? ""
- : ", ") + array[i];
- }
- }
- return ret;
- }
-
- /// <summary>
- /// Returns a string with the array data separated with commas.
- /// This is the int array version.
- /// </summary>
- /// <param name="array">Integer array</param>
- /// <returns>A string with the int array data, comma separated</returns>
- public static string Write(this int[] array)
- {
- string ret = "";
- if (array != null)
- {
- for (int i = 0; i < array.Length; i++)
- {
- ret +=
- (ret.Length == 0
- ? ""
- : ", ") + array[i];
- }
- }
- return ret;
- }
-
- /// <summary>
- /// Returns a string with the array data separated with commas.
- /// This is the general array version.
- /// </summary>
- /// <param name="array">Array of any type</param>
- /// <returns>A string with the array data, comma separated</returns>
- public static string Write(this Array array)
- {
- string ret = "";
- if (array != null)
- {
- for (int i = 0; i < array.Length; i++)
- {
- ret +=
- (ret.Length == 0
- ? ""
- : ", ") +
- (array.GetValue(i) == null
- ? "null"
- : StringHelper.ToInvariantString(array.GetValue(i)));
- }
- }
- return ret;
- }
-
- /// <summary>
- /// Returns a string with the array data separated with commas.
- /// This is the general array version with maxLength bounding (will return
- /// string with max. this number of entries).
- /// </summary>
- /// <param name="list">List</param>
- /// <param name="maxLength">MaxLength</param>
- /// <returns>A string with the array data, comma separated</returns>
- public static string Write(this Array list, int maxLength)
- {
- string ret = "";
- if (list != null)
- {
- for (int i = 0; i < list.Length && i < maxLength; i++)
- {
- ret +=
- (ret.Length == 0
- ? ""
- : ", ") +
- StringHelper.ToInvariantString(list.GetValue(i));
- }
- }
- return ret;
- }
-
- /// <summary>
- /// Returns a string with the array data separated with commas.
- /// </summary>
- /// <param name="list">List</param>
- /// <returns>A string with the array data, comma separated</returns>
- public static string Write(this ICollection list)
- {
- return Write(list, ", ");
- }
-
- /// <summary>
- /// Returns a string with the array data with given separator.
- /// </summary>
- /// <param name="list">List to write out</param>
- /// <param name="separator">Separator</param>
- /// <returns>A string with the array data, comma separated</returns>
- public static string Write(this ICollection list, string separator)
- {
- string ret = "";
- if (list != null)
- {
- foreach (object obj in list)
- {
- ret +=
- (ret.Length == 0
- ? ""
- : separator) +
- StringHelper.ToInvariantString(obj);
- }
- }
- return ret;
- }
-
- /// <summary>
- /// Returns a string with the array data separated with commas.
- /// This is the List<T> version.
- /// </summary>
- /// <typeparam name="T">Type of list data</typeparam>
- /// <param name="list">List to write out</param>
- /// <returns>A string with the list data, comma separated</returns>
- public static string Write<T>(this List<T> list)
- {
- string ret = "";
- if (list != null)
- {
- foreach (T obj in list)
- {
- ret +=
- (ret.Length == 0
- ? ""
- : ", ") +
- StringHelper.ToInvariantString(obj);
- }
- }
- return ret;
- }
-
- /// <summary>
- /// Returns a string with the array data separated with commas.
- /// This is the List<T> version with a maximum count.
- /// </summary>
- /// <typeparam name="T">Type of list data</typeparam>
- /// <param name="count">Count</param>
- /// <param name="list">List</param>
- /// <returns>A string with the list data, comma separated</returns>
- public static string Write<T>(this List<T> list, int count)
- {
- string ret = "";
- if (list != null)
- {
- for (int i = 0; i < list.Count && i < count; i++)
- {
- ret +=
- (ret.Length == 0
- ? ""
- : ", ") +
- StringHelper.ToInvariantString(list[i]);
- }
- }
- return ret;
- }
-
- /// <summary>
- /// Returns a string with the array data with given separator.
- /// This is the List<T> version.
- /// </summary>
- /// <typeparam name="T">Type of the list data, often strings</typeparam>
- /// <param name="list">List to concatenate</param>
- /// <param name="separator">Separator used to separate (e.g. comma)</param>
- /// <returns>A string with the list data, separator separated</returns>
- public static string Write<T>(this List<T> list, string separator)
- {
- string ret = "";
- if (list != null)
- {
- foreach (T obj in list)
- {
- ret +=
- (ret.Length == 0
- ? ""
- : separator) +
- StringHelper.ToInvariantString(obj);
- }
- }
- return ret;
- }
-
- /// <summary>
- /// Returns a string with the array data separated with commas.
- /// This is the enumerable class version.
- /// </summary>
- /// <param name="enumerableClass">Enumerable class</param>
- /// <returns>A string with the list data, comma separated</returns>
- public static string Write(this IEnumerable enumerableClass)
- {
- return Write(enumerableClass, ", ");
- }
-
- /// <summary>
- /// Returns a string with the array data separated with commas.
- /// This is the enumerable class version.
- /// </summary>
- /// <param name="enumerableClass">Enumerable class</param>
- /// <param name="separator">Separator</param>
- /// <returns>A string with the list data, comma separated</returns>
- public static string Write(this IEnumerable enumerableClass,
- string separator)
- {
- string ret = "";
- if (enumerableClass != null)
- {
- foreach (object obj in enumerableClass)
- {
- ret +=
- (ret.Length == 0
- ? ""
- : separator) +
- StringHelper.ToInvariantString(obj);
- }
- }
- return ret;
- }
- #endregion
-
- #region WriteFirstAndLastValues (Static)
- /// <summary>
- /// Write array data first and last values, e.g. the first 10 and the
- /// last 10 values of a really long array (e.g. a network package).
- /// All values are separated by commas.
- /// <para />
- /// Note: If the array has less entries than numberOfItemsAtStartAndEnd
- /// times two, we will just display the whole array.
- /// </summary>
- /// <param name="list">List to grab data from, can be huge</param>
- /// <param name="numberOfItemsAtStartAndEnd">Number of items at start and
- /// end we want to have in the output string</param>
- /// <returns>A string with the list data, comma separated</returns>
- public static string WriteFirstAndLastValues(this Array list,
- int numberOfItemsAtStartAndEnd)
- {
- string ret = "";
- if (list != null)
- {
- // If the array has less entries than numberOfItemsAtStartAndEnd * 2,
- // display the whole array normally.
- if (list.Length <= numberOfItemsAtStartAndEnd * 2)
- {
- return Write(list);
- }
-
- for (int i = 0; i < list.Length && i < numberOfItemsAtStartAndEnd; i++)
- {
- ret += (ret.Length == 0
- ? ""
- : ", ") +
- StringHelper.ToInvariantString(list.GetValue(i));
- }
- ret += " ... ";
- for (int i = list.Length - numberOfItemsAtStartAndEnd;
- i >= 0 && i < list.Length;
- i++)
- {
- ret += (ret.EndsWith(" ... ")
- ? ""
- : ", ") +
- StringHelper.ToInvariantString(list.GetValue(i));
- }
- }
- return ret;
- }
- #endregion
-
- #region ConcatenateCollections (Static)
- /// <summary>
- /// Concatenates all the items from several collections. The collections
- /// need not be of the same type, but must have the same item type.
- /// </summary>
- /// <param name="collections">Set of collections to concatenate.
- /// In many languages, this parameter can be specified as several
- /// individual parameters.</param>
- /// <returns>An IEnumerable that enumerates all the items in each of the
- /// collections, in order.</returns>
- public static IEnumerable<T> ConcatenateCollections<T>(
- IEnumerable<T>[] collections)
- {
- if (collections == null)
- {
- throw new ArgumentNullException("collections");
- }
-
- foreach (IEnumerable<T> coll in collections)
- {
- foreach (T item in coll)
- {
- yield return item;
- }
- }
- }
- #endregion
-
- #region Count (Static)
- /// <summary>
- /// Count the number of items in an IEnumerable<T> collection. If
- /// a more specific collection type is being used, it is more efficient to
- /// use the Count property, if one is provided.
- /// </summary>
- /// <remarks>If the collection implements ICollection<T>, this method
- /// simply returns ICollection<T>.Count. Otherwise, it enumerates all
- /// items and counts them.</remarks>
- /// <param name="collection">The collection to count items in.</param>
- /// <returns>The number of items in the collection.</returns>
- /// <exception cref="ArgumentNullException"><paramref name="collection"/>
- /// is null.</exception>
- public static int Count<T>(IEnumerable<T> collection)
- {
- if (collection == null)
- {
- throw new ArgumentNullException("collection",
- "Unable to count number of items if no valid collection was given");
- }
-
- // If it's really an ICollection, use that Count property as it is much
- // faster.
- ICollection<T> genericCollection = collection as ICollection<T>;
- if (genericCollection != null)
- {
- return genericCollection.Count;
- }
-
- // Traverse the collection and count the elements.
- int count = 0;
- foreach (T item in collection)
- {
- // Dummy line to avoid Warning CA1804
- if (item.GetType() != null)
- {
- count++;
- }
- }
-
- return count;
- }
- #endregion
-
- #region CountEqual (Static)
- /// <summary>
- /// Counts the number of items in the collection that are equal to
- /// <paramref name="find"/>.
- /// </summary>
- /// <remarks>The default sense of equality for T is used, as defined by
- /// T's implementation of IComparable<T>.Equals or object.Equals.
- /// </remarks>
- /// <param name="collection">The collection to count items in.</param>
- /// <param name="find">The item to compare to.</param>
- /// <returns>The number of items in the collection that are equal to
- /// <paramref name="find"/>.</returns>
- public static int CountEqual<T>(IEnumerable<T> collection, T find)
- {
- return CountEqual(collection, find, EqualityComparer<T>.Default);
- }
-
- /// <summary>
- /// Counts the number of items in the collection that are equal to
- /// <paramref name="find"/>.
- /// </summary>
- /// <param name="collection">The collection to count items in.</param>
- /// <param name="find">The item to compare to.</param>
- /// <param name="equalityComparer">The comparer to use to determine if
- /// two items are equal. Only the Equals member function will be called.
- /// </param>
- /// <returns>The number of items in the collection that are equal to
- /// <paramref name="find"/>.</returns>
- /// <exception cref="ArgumentException"><paramref name="collection"/> or
- /// <paramref name="equalityComparer"/> is null.</exception>
- public static int CountEqual<T>(IEnumerable<T> collection, T find,
- IEqualityComparer<T> equalityComparer)
- {
- if (collection == null)
- {
- throw new ArgumentException("collection");
- }
- if (equalityComparer == null)
- {
- throw new ArgumentNullException("equalityComparer");
- }
-
- int count = 0;
- foreach (T item in collection)
- {
- if (equalityComparer.Equals(item, find))
- {
- ++count;
- }
- }
-
- return count;
- }
- #endregion
-
- #region IsValidIndex (Static)
- /// <summary>
- /// Returns 'true' if the given index of the array stays in the
- /// "array range".
- /// </summary>
- /// <param name="arrayIndex">Array index</param>
- /// <param name="anyArray">Any array</param>
- /// <returns>'true' if the given index of the array stays in the range
- /// </returns>
- public static bool IsValidIndex(this IList list, int arrayIndex)
- {
- return list != null &&
- arrayIndex < list.Count &&
- arrayIndex > MathHelper.InvalidIndex;
- }
- #endregion
-
- #region Pop (Static)
- /// <summary>
- /// Returns the top most item of the list and removes it from the list.
- /// Warning: This method is a bit slow because we need to change the list
- /// and removing the last entry is not as fast as for Collections like
- /// Queue, use it with care.
- /// </summary>
- /// <typeparam name="T">The object type in the list</typeparam>
- /// <param name="list">The list we want to pop from</param>
- /// <returns>The top most object of the list</returns>
- public static T Pop<T>(this List<T> list)
- {
- if (list.Count == 0)
- {
- throw new Exception("The list is empty!");
- }
-
- T result = list[list.Count - 1];
- list.RemoveAt(list.Count - 1);
- return result;
- }
- #endregion
-
- #region IsNullOrEmpty (Static)
- /// <summary>
- /// Helper method for a common check if an array is null or has no
- /// elements. Same as String.IsNullOrEmpty
- /// </summary>
- /// <param name="array">The array to check.</param>
- /// <returns>True if array is null or empty.</returns>
- public static bool IsNullOrEmpty(Array array)
- {
- return array == null ||
- array.Length == 0;
- }
-
- /// <summary>
- /// Helper method for a common check if a list is null or has no
- /// elements. Same as String.IsNullOrEmpty
- /// </summary>
- /// <param name="list">The list to check.</param>
- /// <returns>True if list is null or empty.</returns>
- public static bool IsNullOrEmpty<T>(List<T> list)
- {
- return list == null ||
- list.Count == 0;
- }
- #endregion
-
- #region SafeGet (Static)
- /// <summary>
- /// Gets given property value in a safe way (not found? then default is
- /// returned, otherwise the found object is returned).
- /// </summary>
- /// <param name="dict">Dictionary</param>
- /// <param name="key">Key</param>
- /// <returns>Value from dictionary or default value if not found</returns>
- public static ValueType SafeGet<KeyType, ValueType>(
- Dictionary<KeyType, object> dict, KeyType key)
- {
- if (dict.ContainsKey(key) == false)
- {
- return default(ValueType);
- }
-
- ValueType result;
- try
- {
- result = (ValueType)dict[key];
- }
- catch
- {
- result = default(ValueType);
- }
- return result;
-
- //return (ValueType)Convert.ChangeType(dict[key], typeof(ValueType));
- }
- #endregion
-
- #region Save
- /// <summary>
- /// Helper method to save out an array of strings by just saving the length
- /// first and then all the data. Use LoadStrings to load the data again.
- /// </summary>
- /// <param name="someArray">Some string array with data</param>
- /// <param name="writer">Binary data writer</param>
- public static void Save(this string[] someArray, BinaryWriter writer)
- {
- if (writer == null)
- {
- Log.Warning("Unable to save array, writer is not valid!");
- return;
- }
- if (someArray == null)
- {
- writer.Write((int)0);
- return;
- }
-
- writer.Write(someArray.Length);
- for (int num = 0; num < someArray.Length; num++)
- {
- writer.Write(someArray[num]);
- }
- }
-
- /// <summary>
- /// Helper method to save out an array of integers by saving the length
- /// first and then all the data. Use LoadIntegers to load the data again.
- /// </summary>
- /// <param name="someArray">Some integer array with data</param>
- /// <param name="writer">Binary data writer</param>
- public static void Save(this int[] someArray, BinaryWriter writer)
- {
- if (writer == null)
- {
- Log.Warning("Unable to save array, writer is not valid!");
- return;
- }
- if (someArray == null)
- {
- writer.Write((int)0);
- return;
- }
-
- writer.Write(someArray.Length);
- for (int num = 0; num < someArray.Length; num++)
- {
- writer.Write(someArray[num]);
- }
- }
- #endregion
-
- #region Load
- /// <summary>
- /// Helper method to load an array of strings by first reading the length
- /// and then all the data. Use Save to save the data.
- /// </summary>
- /// <param name="reader">Binary data reader</param>
- /// <returns>String array with all the data filled in</returns>
- public static string[] LoadStrings(BinaryReader reader)
- {
- if (reader == null)
- {
- Log.Warning("Unable to read array, reader is not valid!");
- return new string[0];
- }
-
- int length = reader.ReadInt32();
- string[] ret = new string[length];
- for (int num = 0; num < length; num++)
- {
- ret[num] = reader.ReadString();
- }
- return ret;
- }
-
- /// <summary>
- /// Helper method to load an array of strings by first reading the length
- /// and then all the data. Use Save to save the data.
- /// </summary>
- /// <param name="reader">Binary data reader</param>
- /// <returns>String array with all the data filled in</returns>
- public static int[] LoadIntegers(BinaryReader reader)
- {
- if (reader == null)
- {
- Log.Warning("Unable to read array, reader is not valid!");
- return new int[0];
- }
-
- int length = reader.ReadInt32();
- int[] ret = new int[length];
- for (int num = 0; num < length; num++)
- {
- ret[num] = reader.ReadInt32();
- }
- return ret;
- }
- #endregion
-
- #region FreeDataWhenMemoryIsLow (Static)
- /// <summary>
- /// Event handler everyone can attach to for freeing unneeded memory.
- /// The primary user of this delegate are all Cache classes, which will
- /// allow to free cache data when memory is low. All data will be reloaded
- /// internally, so we just need some extra CPU time to recalculate
- /// everything, but we can save a lot of memory without having to go to
- /// real data that is currently needed, which is much harder to optimize.
- /// </summary>
- public static FreeMemoryDelegate FreeDataWhenMemoryIsLow;
- #endregion
-
- #region Internal
-
- #region NumberOfCachesGenerated (Internal)
- /// <summary>
- /// Helper variable to keep track on how many caches were generated. Only
- /// used for warnings, debugging and profiling, but cannot be inside the
- /// generic Cache class because each input/output type combination would
- /// have its own counter, we want to know the overall number of caches.
- /// </summary>
- internal static int NumberOfCachesGenerated;
- #endregion
-
- #endregion
-
- /// <summary>
- /// ArrayHelper tests
- /// </summary>
- internal class ArrayHelperTests
- {
- #region CompareArrays (Static)
- /// <summary>
- /// Test compare arrays. Note: Too slow for a dynamic unit test.
- /// </summary>
- [Test]
- public static void CompareArrays()
- {
- // Compare 2 arrays
- int[] testArray1 = new int[4]
- {
- 1, 4, 7, 5
- };
- int[] testArray2 = (int[])new int[2]
- {
- 1, 4
- }.Add(new int[2]
- {
- 7, 5
- });
- Assert.True(Compare(testArray1, testArray2));
-
- // Compare 2 generic lists
- List<int> testList1 = new List<int>(new[]
- {
- 4, 8
- });
- List<int> testList2 = new List<int>();
- testList2.Add(4);
- testList2.Add(8);
- Assert.True(Compare(testList1, testList2));
- Assert.True(testList1.Contains(8));
- Assert.False(testList1.Contains(2));
- }
- #endregion
-
- #region IsNullOrEmpty (Static)
- /// <summary>
- /// Test the IsNullOrEmpty method.
- /// </summary>
- [Test]
- public static void IsNullOrEmpty()
- {
- string[] testArray = null;
- Assert.True(ArrayHelper.IsNullOrEmpty(testArray));
- testArray = new string[0];
- Assert.True(ArrayHelper.IsNullOrEmpty(testArray));
- testArray = new[]
- {
- "hello"
- };
- Assert.False(ArrayHelper.IsNullOrEmpty(testArray));
-
- List<string> testList = null;
- Assert.True(ArrayHelper.IsNullOrEmpty(testList));
- testList = new List<string>();
- Assert.True(ArrayHelper.IsNullOrEmpty(testList));
- testList.Add("hello");
- Assert.False(ArrayHelper.IsNullOrEmpty(testList));
- }
- #endregion
-
- #region SafeGet (Static)
- /// <summary>
- /// Test the SafeGet method.
- /// </summary>
- [Test]
- public static void SafeGet()
- {
- Dictionary<string, object> elements = new Dictionary<string, object>();
-
- Assert.Null(SafeGet<string, Array>(elements, "key1"));
-
- elements.Add("key2", 15);
- elements.Add("key3", 15.456f);
- elements.Add("key4", 15.1d);
-
- Assert.Equal(SafeGet<string, int>(elements, "key2"),
- 15);
- Assert.Equal(SafeGet<string, float>(elements, "key3"),
- 15.456f);
- Assert.Equal(SafeGet<string, double>(elements, "key4"),
- 15.1d);
- // Now we test an invalid cast which will return the default
- Assert.Equal(SafeGet<string, int>(elements, "key4"),
- 0.0d);
- }
- #endregion
-
- #region Resize
- /// <summary>
- /// Test resize
- /// </summary>
- [Test]
- public void Resize()
- {
- // Just resize a simple int array
- int[] testArray1 = {
- 0, 1
- };
- Assert.Equal(6, testArray1.Resize(6).Length);
-
- // Resize a generic list (returning an array, much faster)
- List<int> testArray3 = new List<int>(new[]
- {
- 0, 1
- });
- Assert.Equal(6, testArray3.Resize(6).Length);
- }
- #endregion
-
- #region IsEqual
- /// <summary>
- /// Is equal
- /// </summary>
- [Test]
- public void IsEqual()
- {
- int[] list = new[]
- {
- 1, 2, 3
- };
- int[] unorderedList = new[]
- {
- 2, 3, 1
- };
- int[] differentList = new[]
- {
- 11, 2, 3
- };
-
- Assert.True(list.IsEqualWithoutOrder(list));
- Assert.True(list.IsEqualWithoutOrder(unorderedList));
- Assert.False(list.IsEqualWithoutOrder(differentList));
- }
- #endregion
-
- #region AddArrays
- /// <summary>
- /// Test add arrays
- /// </summary>
- [Test]
- public void AddArrays()
- {
- // Test adding normal arrays
- int[] testArray1 = {
- 0, 1, 2, 3
- };
- int[] testArray2 = {
- 4, 5, 3, 1
- };
- int[] testArray3 = (int[])testArray1.Add(testArray2);
- Assert.Equal(testArray3.Length,
- testArray1.Length + testArray2.Length);
- Assert.True(Compare(testArray3,
- new[]
- {
- 0, 1, 2, 3, 4, 5, 3, 1
- }));
-
- // Test adding generic lists
- List<int> testList1 = new List<int>(new[]
- {
- 0, 1, 2
- });
- List<int> testList2 = new List<int>(new[]
- {
- 6, 5, 4
- });
- List<int> testList3 = testList1.Add(testList2);
- Assert.Equal(testList3.Count,
- testList1.Count + testList2.Count);
- Assert.True(Compare(testList3,
- new[]
- {
- 0, 1, 2, 6, 5, 4
- }));
- }
- #endregion
-
- #region RemoveIndex
- /// <summary>
- /// Test remove index
- /// </summary>
- [Test]
- public void RemoveIndex()
- {
- int[] someArray = new[]
- {
- 1, 2, 6, 3, 4
- };
- // Remove 6 and compare
- Assert.True(ArrayHelper.Compare(
- ArrayHelper.RemoveIndex(someArray, 2),
- new[]
- {
- 1, 2, 3, 4
- }));
- }
- #endregion
-
- #region GetSubArray
- /// <summary>
- /// Test get sub array
- /// </summary>
- [Test]
- public void GetSubArray()
- {
- // First test
- int[] someArray = new[]
- {
- 1, 2, 3, 100, 200, 300, 4, 5, 6
- };
- Assert.True(Compare(
- someArray.GetSubArray(3, 3),
- new[]
- {
- 100, 200, 300
- }));
-
- // Another test
- float[] testNumbers = new float[]
- {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
- };
- float[] extractedNumbers = testNumbers.GetSubArray(2, 4);
- Assert.Equal(new float[]
- {
- 2, 3, 4, 5
- }, extractedNumbers);
- }
- #endregion
-
- #region ConcatenateCollections
- /// <summary>
- /// Test concatenate collections
- /// </summary>
- [Test]
- public void ConcatenateCollections()
- {
- //int[] list1 = new int[] { 0, 1, 2, 3, 4 };
- //int[] list2 = new int[] { 5, 6, 7, 8, 9 };
- //int[] list3 = new int[] { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
- //int[] allLists = ArrayHelper.ToArray(ConcatenateCollections<int>(
- // new int[][] { list1, list2, list3 }));
- //Assert.Equal(list1.Length + list2.Length + list3.Length,
- // allLists.Length);
- //Assert.Equal("0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " +
- // "10, 20, 30, 40, 50, 60, 70, 80, 90, 100",
- // ArrayHelper.Write(allLists));
- }
- #endregion
-
- #region AreCollectionsEqual
- /// <summary>
- /// Test are collections equal
- /// </summary>
- [Test]
- public void AreCollectionsEqual()
- {
- int[] list1 = new[]
- {
- 1, 2, 3, 4, 5, 6
- };
- int[] list2 = new[]
- {
- 1, 2, 3, 4, 5, 6, 7
- };
- int[] list3 = new[]
- {
- 1, 2, 3, 4, 5, 6
- };
- Assert.False(ArrayHelper.AreCollectionsEqual(list1, list2));
- Assert.True(ArrayHelper.AreCollectionsEqual(list1, list3));
- }
- #endregion
-
- #region ToArray
- /// <summary>
- /// Test to array
- /// </summary>
- [Test]
- public void ToArray()
- {
- List<string> someStrings = new List<string>();
- someStrings.Add("hi");
- someStrings.Add("there");
- someStrings.Add("whats");
- someStrings.Add("up");
- string[] stringArray = ArrayHelper.ToArray(someStrings);
- Assert.Equal("hi, there, whats, up",
- stringArray.Write());
- Assert.True(
- ArrayHelper.AreCollectionsEqual(someStrings, stringArray));
- }
- #endregion
-
- #region IsValidIndex
- /// <summary>
- /// Is valid index
- /// </summary>
- [Test]
- public void IsValidIndex()
- {
- int[] testArray = new int[5];
-
- Assert.True(testArray.IsValidIndex(0));
- Assert.True(testArray.IsValidIndex(2));
- Assert.True(testArray.IsValidIndex(4));
- Assert.False(testArray.IsValidIndex(5));
- Assert.False(testArray.IsValidIndex(-1));
- }
- #endregion
-
- #region ConvertStringToIntArray
- /// <summary>
- /// Convert string to int array
- /// </summary>
- [Test]
- public void ConvertStringToIntArray()
- {
- string testInts = "1, 2, 3, 4, 10";
- int[] ints = ArrayHelper.ConvertStringToIntArray(testInts);
- Assert.Equal(ints.Length, 5);
- Assert.Equal(ints[0], 1);
- Assert.Equal(ints[1], 2);
- Assert.Equal(ints[4], 10);
- // Also make sure that converting back to a string works
- Assert.Equal(ints.Write(", "), testInts);
- }
- #endregion
- }
- }
- }