JaggedArray.cs

Jagged Array Collection Class (No Copy Resize)

Updated: Jun-21,2021

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
/// <summary>
///     Uses a multidimensional array to create a growable array with out copy or re-allocation.
/// </summary>
[DebuggerDisplay("Count = {Count}")]
[Serializable]
public struct JaggedArray<T> : IEnumerable<T>, IDisposable
{
    private const int   FirstDimensionMax = 4096;
    public const  int   ShiftCount        = 19;
    public const  int   Granularity       = 1 << ShiftCount;
    private       bool  _disposed;
    private       T[][] _array;
    public        int   Count;
    public        int   Length;
    public        int   NumberOfActiveArrays;
    public        bool  Allocated;
    public JaggedArray(int size)
    {
        if (size < Granularity)
            size = Granularity;
        try
        {
            NumberOfActiveArrays = (size + (Granularity - 1)) / Granularity;
            Length               = NumberOfActiveArrays       * Granularity;
            _array               = new T[FirstDimensionMax][];
            for (var i = 0; i < NumberOfActiveArrays; ++i)
                _array[i] = new T[Granularity];
        }
        catch (Exception ex)
        {
            throw new Exception($"Exception: {ex.Message}");
        }
        Count     = 0;
        _disposed = false;
        Allocated = true;
    }
    public T this[int index]
    {
        get
        {
            if (index >= Length)
                throw new Exception($"Getter: Index out of bounds, Index: '{index}' must be less than the Length: '{Length}'.");
            return _array[index >> ShiftCount][index & (Granularity - 1)];
        }
        set
        {
            if (index >= Length)
                ResizeArray();
            _array[index >> ShiftCount][index & (Granularity - 1)] = value;
            Count++;
        }
    }
    public void Dispose()
    {
        Dispose(true);
    }
    IEnumerator<T> IEnumerable<T>.GetEnumerator()
    {
        return GetEnumerator();
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
    public void Add(T Item)
    {
        if (Count >= Length)
            ResizeArray();
        var x = Count >> ShiftCount;
        var y = Count & (Granularity - 1);
        _array[x][y] = Item;
        Count++;
    }
    private void ResizeArray()
    {
        try
        {
            _array[NumberOfActiveArrays] = new T[Granularity];
            NumberOfActiveArrays++;
        }
        catch (Exception ex)
        {
            throw new Exception($"Exception: {ex.Message}");
        }
    }
    public void Zero()
    {
        for (var a = 0L; a < NumberOfActiveArrays; a++)
            _array[a] = Array.Empty<T>();
        Count = 0;
    }
    public void Clear()
    {
        for (var a = 0L; a < NumberOfActiveArrays; a++)
            Array.Clear(_array[a], 0, Granularity);
        Count = 0;
    }
    public int IndexOf(T item)
    {
        var i = 0;
        for (; i < NumberOfActiveArrays; i++)
        {
            var pos = Array.IndexOf(_array[i], item, 0);
            if (pos != -1)
                return i * Granularity + pos;
        }
        return -1;
    }
    public JaggedArray<T> Copy(int newsize)
    {
        var temp = new JaggedArray<T>(newsize);
        for (var a = 0L; a < NumberOfActiveArrays; a++)
            Array.Copy(_array[a], temp._array[a], Granularity);
        temp.Count = Count;
        return temp;
    }
    public void FromArray(T[][] array)
    {
        NumberOfActiveArrays = array.GetUpperBound(0) + 1;
        Length               = NumberOfActiveArrays * Granularity;
        _array               = new T[NumberOfActiveArrays][];
        for (var i = 0; i < NumberOfActiveArrays; ++i)
            _array[i] = new T[Granularity];
        for (var a = 0L; a < NumberOfActiveArrays; a++)
            Array.Copy(array[a], _array[a], Granularity);
    }
    public T[][] ToRawArray()
    {
        var ta = new T[NumberOfActiveArrays][];
        for (var i = 0; i < NumberOfActiveArrays; ++i)
            ta[i] = new T[Granularity];
        for (var a = 0L; a < NumberOfActiveArrays; a++)
            Array.Copy(_array[a], ta[a], Granularity);
        return ta;
    }
    public T[] ToArray()
    {
        var newArray = new T[Count];
        using (var en = GetEnumerator())
        {
            var ptr = 0;
            while (en.MoveNext())
            {
                var value = en.Current;
                if (value == null)
                    break;
                newArray[ptr++] = value;
            }
            return newArray;
        }
    }
    private void Dispose(bool disposing)
    {
        if (!_disposed)
            if (disposing)
                _array = null;
        _disposed = true;
    }
    public IEnumerator<T> GetEnumerator()
    {
        return GetEnum();
    }
    public IEnumerator<T> GetEnum()
    {
        for (var i = 0; i < Count; i++)
            yield return this[i];
    }
}