178 lines
6.9 KiB
C#
178 lines
6.9 KiB
C#
using mbts.utils;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
|
|
namespace bts.utils {
|
|
public static class ArrayHelper {
|
|
public static void trim<T>(ref T[] array, int size) {
|
|
if (array.Length > size) {
|
|
T[] trimmed = new T[size];
|
|
Array.Copy(array, trimmed, size);
|
|
array = trimmed;
|
|
}
|
|
}
|
|
|
|
public static void ensure_capacity<T>(ref T[] array, int min) {
|
|
if (array.Length < min) {
|
|
int twice_cap = array.Length * 2;
|
|
expand(ref array, min > twice_cap ? min : twice_cap);
|
|
}
|
|
}
|
|
|
|
public static void expand<T>(ref T[] array) => expand(ref array, array.Length * 2);
|
|
|
|
public static void expand<T>(ref T[] array, int new_capacity) {
|
|
if (array.Length < new_capacity) {
|
|
T[] expanded = new T[new_capacity];
|
|
Array.Copy(array, expanded, array.Length);
|
|
array = expanded;
|
|
}
|
|
}
|
|
|
|
public static TResult[] safe_convert<T, TResult>(T[] array, Func<T, TResult> convert) =>
|
|
array == null ? Array.Empty<TResult>() : Array.ConvertAll(array, new Converter<T, TResult>(convert));
|
|
|
|
public static TResult[] convert<T, TResult>(ICollection<T> source, Func<T, TResult> convert) {
|
|
var array = new TResult[source.Count];
|
|
int i = 0;
|
|
foreach (var v in source)
|
|
array[i++] = convert(v);
|
|
return array;
|
|
}
|
|
|
|
public static T[] clone<T>(T[] array) {
|
|
T[] rv = new T[array.Length];
|
|
Array.Copy(array, rv, array.Length);
|
|
return rv;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attemps to get an exact item count (to initialize the resultant array with an exact length, thus avoiding unneccessary allocations) by checking if the specified source implements <see cref="ICollection{T}"/>, <see cref="IReadOnlyCollection{T}"/>, or <see cref="ICollection"/>.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of elements.</typeparam>
|
|
/// <param name="source">The source that will be copied to the resultant array.</param>
|
|
/// <returns>An array containing the elements from the specified source.</returns>
|
|
public static T[] clone_optimized<T>(IEnumerable<T> source) {
|
|
if (source == null)
|
|
return Array.Empty<T>();
|
|
T[] result;
|
|
if (source is ICollection<T> collection) {
|
|
result = new T[collection.Count];
|
|
collection.CopyTo(result, 0);
|
|
} else if (source is IReadOnlyCollection<T> ro_collection) {
|
|
result = new T[ro_collection.Count];
|
|
int i = 0;
|
|
foreach (var v in ro_collection)
|
|
result[i++] = v;
|
|
} else if (source is ICollection ng_collection) {
|
|
result = new T[ng_collection.Count];
|
|
int i = 0;
|
|
foreach (var v in source)
|
|
result[i++] = v;
|
|
} else {
|
|
int i = 0;
|
|
using (var en = source.GetEnumerator()) {
|
|
if (en.MoveNext()) {
|
|
result = new[] { en.Current };
|
|
i = 1;
|
|
while (en.MoveNext()) {
|
|
ensure_capacity(ref result, i + 1);
|
|
result[i++] = en.Current;
|
|
}
|
|
trim(ref result, i);
|
|
} else {
|
|
return Array.Empty<T>();
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public static void insert<T>(ref T[] array, int index, T value, ref int size) {
|
|
// validate size/index
|
|
if (size < 0)
|
|
throw ExceptionBuilder.ArgumentOutOfRange.lt_zero(nameof(size));
|
|
if (size > array.Length)
|
|
throw ExceptionBuilder.ArgumentOutOfRange.gt_length(nameof(size));
|
|
if (index < 0)
|
|
throw ExceptionBuilder.IndexOutOfRange.lt_zero();
|
|
if (index > size)
|
|
throw ExceptionBuilder.IndexOutOfRange.gt_length();
|
|
T[] old_array = array;
|
|
checked {
|
|
if (size == array.Length) {
|
|
array = new T[array.Length * 2];
|
|
Array.Copy(old_array, 0, array, 0, index);
|
|
}
|
|
if (index < size)
|
|
Array.Copy(old_array, index, array, index + 1, size - index);
|
|
array[index] = value;
|
|
size++;
|
|
}
|
|
}
|
|
|
|
public static void remove_at<T>(ref T[] array, int index) {
|
|
int sz = array.Length;
|
|
remove_at(ref array, ref sz, index);
|
|
}
|
|
|
|
public static void remove_at<T>(ref T[] array, ref int size, int index) {
|
|
// validate size/index
|
|
if (size < 0)
|
|
throw ExceptionBuilder.ArgumentOutOfRange.lt_zero(nameof(size));
|
|
if (size > array.Length)
|
|
throw ExceptionBuilder.ArgumentOutOfRange.gte_length(nameof(size));
|
|
if (index < 0)
|
|
throw ExceptionBuilder.IndexOutOfRange.lt_zero();
|
|
if (index >= size)
|
|
throw ExceptionBuilder.IndexOutOfRange.gte_length();
|
|
// shift [any]items right of 'index' left 1 pos.
|
|
if (index < size - 1) {
|
|
Array.Copy(array, index + 1, array, index, size - index - 1);
|
|
}
|
|
// clear last item, and decrement size
|
|
Array.Clear(array, --size, 1);
|
|
}
|
|
|
|
public static void remove_sorted<T>(ref T[] array, ref int size, T value, IComparer<T> comparer) =>
|
|
remove_sorted(ref array, ref size, value, comparer);
|
|
|
|
public static void remove_sorted<T>(ref T[] array, ref int size, T value, IComparer<T> comparer, out int? pos) {
|
|
int p = Array.BinarySearch(array, 0, size, value, comparer);
|
|
if (p < 0) {
|
|
pos = null;
|
|
} else {
|
|
pos = p;
|
|
size--;
|
|
Array.Copy(array, p + 1, array, p, size - p);
|
|
array[size] = default;
|
|
}
|
|
}
|
|
|
|
public static T[] add<T>(T[] array, T value) {
|
|
T[] clone = new T[array.Length + 1];
|
|
Array.Copy(array, clone, array.Length);
|
|
clone[array.Length] = value;
|
|
return clone;
|
|
}
|
|
|
|
public static void add_sorted<T>(ref T[] array, ref int size, T value, IComparer<T> comparer) =>
|
|
add_sorted(ref array, ref size, value, comparer, out int pos);
|
|
|
|
public static void add_sorted<T>(ref T[] array, ref int size, T value, IComparer<T> comparer, out int pos) {
|
|
pos = peek_sorted_index(array, size, value, comparer);
|
|
ensure_capacity(ref array, size + 1);
|
|
Array.Copy(array, pos, array, pos + 1, size - pos);
|
|
array[pos] = value;
|
|
size++;
|
|
}
|
|
|
|
public static int peek_sorted_index<T>(T[] array, int size, T value, IComparer<T> comparer) {
|
|
int index = Array.BinarySearch(array, 0, size, value, comparer);
|
|
if (index < 0)
|
|
index = ~index;
|
|
return index;
|
|
}
|
|
}
|
|
} |