Skip to content

Instantly share code, notes, and snippets.

@cjddmut
Last active March 19, 2026 12:02
Show Gist options
  • Select an option

  • Save cjddmut/cb43af3ee191af78363f41a3188c0f7b to your computer and use it in GitHub Desktop.

Select an option

Save cjddmut/cb43af3ee191af78363f41a3188c0f7b to your computer and use it in GitHub Desktop.

Revisions

  1. cjddmut revised this gist Feb 11, 2019. 1 changed file with 71 additions and 4 deletions.
    75 changes: 71 additions & 4 deletions ValueTypeLists.cs
    Original file line number Diff line number Diff line change
    @@ -40,6 +40,9 @@
    * then duplicate one of the structs and update the number of value fields, the MAX_SIZE constant, and the struct name
    * (including constructors and Equals method). The actual implementation will still work and should be left unchanged.
    *
    * Define DISABLE_VTL_BOUNDS_CHK to remove bounds checking for the list. Performance would be better but it WILL
    * write over other memory if you overstep the bounds.
    *
    * Each list implements IList so the methods defined in that interface are available to be used with these struct lists.
    *
    * EXAMPLE:
    @@ -113,10 +116,12 @@ public T this[int index]
    {
    get
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }
    #endif

    fixed (T* pFirst = &_value0)
    {
    @@ -126,10 +131,12 @@ public T this[int index]

    set
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }
    #endif

    fixed (T* pFirst = &_value0)
    {
    @@ -166,11 +173,13 @@ IEnumerator IEnumerable.GetEnumerator()

    public void Add(T item)
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (_count == MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }

    #endif

    fixed (T* pFirst = &_value0)
    {
    *(pFirst + _count) = item;
    @@ -181,10 +190,12 @@ public void Add(T item)

    public void AddRange(T[] arr)
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (_count + arr.Length > MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }
    #endif

    fixed (T* pMe = &_value0, pThem = arr)
    {
    @@ -198,10 +209,12 @@ public void AddRange(T[] arr)

    public void AddRange(List<T> list)
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (_count + list.Count > MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }
    #endif

    fixed (T* pFirst = &_value0)
    {
    @@ -273,10 +286,12 @@ public int IndexOf(T item)

    public void Insert(int index, T item)
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (_count == MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }
    #endif

    fixed (T* pFirst = &_value0)
    {
    @@ -293,10 +308,12 @@ public void Insert(int index, T item)

    public void RemoveAt(int index)
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }
    #endif

    fixed (T* pFirst = &_value0)
    {
    @@ -315,10 +332,12 @@ public void RemoveAt(int index)

    public void UnstableRemoveAt(int index)
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }
    #endif

    fixed (T* pFirst = &_value0)
    {
    @@ -394,10 +413,12 @@ public T this[int index]
    {
    get
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }
    #endif

    fixed (T* pFirst = &_value0)
    {
    @@ -407,10 +428,12 @@ public T this[int index]

    set
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }
    #endif

    fixed (T* pFirst = &_value0)
    {
    @@ -447,11 +470,13 @@ IEnumerator IEnumerable.GetEnumerator()

    public void Add(T item)
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (_count == MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }

    #endif

    fixed (T* pFirst = &_value0)
    {
    *(pFirst + _count) = item;
    @@ -462,10 +487,12 @@ public void Add(T item)

    public void AddRange(T[] arr)
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (_count + arr.Length > MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }
    #endif

    fixed (T* pMe = &_value0, pThem = arr)
    {
    @@ -479,10 +506,12 @@ public void AddRange(T[] arr)

    public void AddRange(List<T> list)
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (_count + list.Count > MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }
    #endif

    fixed (T* pFirst = &_value0)
    {
    @@ -554,10 +583,12 @@ public int IndexOf(T item)

    public void Insert(int index, T item)
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (_count == MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }
    #endif

    fixed (T* pFirst = &_value0)
    {
    @@ -574,10 +605,12 @@ public void Insert(int index, T item)

    public void RemoveAt(int index)
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }
    #endif

    fixed (T* pFirst = &_value0)
    {
    @@ -596,10 +629,12 @@ public void RemoveAt(int index)

    public void UnstableRemoveAt(int index)
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }
    #endif

    fixed (T* pFirst = &_value0)
    {
    @@ -683,10 +718,12 @@ public T this[int index]
    {
    get
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }
    #endif

    fixed (T* pFirst = &_value0)
    {
    @@ -696,10 +733,12 @@ public T this[int index]

    set
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }
    #endif

    fixed (T* pFirst = &_value0)
    {
    @@ -736,11 +775,13 @@ IEnumerator IEnumerable.GetEnumerator()

    public void Add(T item)
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (_count == MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }

    #endif

    fixed (T* pFirst = &_value0)
    {
    *(pFirst + _count) = item;
    @@ -751,10 +792,12 @@ public void Add(T item)

    public void AddRange(T[] arr)
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (_count + arr.Length > MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }
    #endif

    fixed (T* pMe = &_value0, pThem = arr)
    {
    @@ -768,10 +811,12 @@ public void AddRange(T[] arr)

    public void AddRange(List<T> list)
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (_count + list.Count > MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }
    #endif

    fixed (T* pFirst = &_value0)
    {
    @@ -843,10 +888,12 @@ public int IndexOf(T item)

    public void Insert(int index, T item)
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (_count == MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }
    #endif

    fixed (T* pFirst = &_value0)
    {
    @@ -863,10 +910,12 @@ public void Insert(int index, T item)

    public void RemoveAt(int index)
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }
    #endif

    fixed (T* pFirst = &_value0)
    {
    @@ -885,10 +934,12 @@ public void RemoveAt(int index)

    public void UnstableRemoveAt(int index)
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }
    #endif

    fixed (T* pFirst = &_value0)
    {
    @@ -988,10 +1039,12 @@ public T this[int index]
    {
    get
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }
    #endif

    fixed (T* pFirst = &_value0)
    {
    @@ -1001,10 +1054,12 @@ public T this[int index]

    set
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }
    #endif

    fixed (T* pFirst = &_value0)
    {
    @@ -1041,11 +1096,13 @@ IEnumerator IEnumerable.GetEnumerator()

    public void Add(T item)
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (_count == MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }

    #endif

    fixed (T* pFirst = &_value0)
    {
    *(pFirst + _count) = item;
    @@ -1056,10 +1113,12 @@ public void Add(T item)

    public void AddRange(T[] arr)
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (_count + arr.Length > MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }
    #endif

    fixed (T* pMe = &_value0, pThem = arr)
    {
    @@ -1073,10 +1132,12 @@ public void AddRange(T[] arr)

    public void AddRange(List<T> list)
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (_count + list.Count > MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }
    #endif

    fixed (T* pFirst = &_value0)
    {
    @@ -1148,10 +1209,12 @@ public int IndexOf(T item)

    public void Insert(int index, T item)
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (_count == MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }
    #endif

    fixed (T* pFirst = &_value0)
    {
    @@ -1168,10 +1231,12 @@ public void Insert(int index, T item)

    public void RemoveAt(int index)
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }
    #endif

    fixed (T* pFirst = &_value0)
    {
    @@ -1190,10 +1255,12 @@ public void RemoveAt(int index)

    public void UnstableRemoveAt(int index)
    {
    #if !DISABLE_VTL_BOUNDS_CHK
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }
    #endif

    fixed (T* pFirst = &_value0)
    {
  2. cjddmut revised this gist Feb 10, 2019. 1 changed file with 7 additions and 7 deletions.
    14 changes: 7 additions & 7 deletions ValueTypeLists.cs
    Original file line number Diff line number Diff line change
    @@ -36,9 +36,9 @@
    *
    * Also requires at least the .NET 4.x runtime which is available in Unity 2018.3
    *
    * There are four lists created below, each of different sizes (4, 8, 16, and 32). If a specific size is desired
    * then duplicate one of the structs and update the number of value fields and the MAX_SIZE constant.
    * The actual implementation can be left unchanged with the exception of constructor/Equals type rename.
    * There are four lists created below, each of different sizes (4, 8, 16, and 32). If a custom size is desired
    * then duplicate one of the structs and update the number of value fields, the MAX_SIZE constant, and the struct name
    * (including constructors and Equals method). The actual implementation will still work and should be left unchanged.
    *
    * Each list implements IList so the methods defined in that interface are available to be used with these struct lists.
    *
    @@ -342,7 +342,7 @@ public bool Equals(ValueTypeList4<T> other)
    // Below errors out with "You cannot use the fixed statement to take the address of an already fixed expression"
    // So I interpret that as I'm already safe? But I don't fully follow or understand so research is needed.
    // fixed(T* pFirstOther = &other._value0) {}
    return ValueTypeListUtil.MemoryCompare(pFirst, &other._value0, Count * sizeof(T));
    return ValueTypeListUtil.MemoryCompare(pFirst, &other._value0, _count * sizeof(T));
    }
    }

    @@ -623,7 +623,7 @@ public bool Equals(ValueTypeList8<T> other)
    // Below errors out with "You cannot use the fixed statement to take the address of an already fixed expression"
    // So I interpret that as I'm already safe? But I don't fully follow or understand so research is needed.
    // fixed(T* pFirstOther = &other._value0) {}
    return ValueTypeListUtil.MemoryCompare(pFirst, &other._value0, Count * sizeof(T));
    return ValueTypeListUtil.MemoryCompare(pFirst, &other._value0, _count * sizeof(T));
    }
    }

    @@ -912,7 +912,7 @@ public bool Equals(ValueTypeList16<T> other)
    // Below errors out with "You cannot use the fixed statement to take the address of an already fixed expression"
    // So I interpret that as I'm already safe? But I don't fully follow or understand so research is needed.
    // fixed(T* pFirstOther = &other._value0) {}
    return ValueTypeListUtil.MemoryCompare(pFirst, &other._value0, Count * sizeof(T));
    return ValueTypeListUtil.MemoryCompare(pFirst, &other._value0, _count * sizeof(T));
    }
    }

    @@ -1217,7 +1217,7 @@ public bool Equals(ValueTypeList32<T> other)
    // Below errors out with "You cannot use the fixed statement to take the address of an already fixed expression"
    // So I interpret that as I'm already safe? But I don't fully follow or understand so research is needed.
    // fixed(T* pFirstOther = &other._value0) {}
    return ValueTypeListUtil.MemoryCompare(pFirst, &other._value0, Count * sizeof(T));
    return ValueTypeListUtil.MemoryCompare(pFirst, &other._value0, _count * sizeof(T));
    }
    }

  3. cjddmut revised this gist Feb 10, 2019. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion ValueTypeLists.cs
    Original file line number Diff line number Diff line change
    @@ -33,10 +33,12 @@
    *
    * Uses unsafe pointer math so this file needs to be compiled using the /unsafe compiler flag. If working in Unity 3D
    * then enable unsafe code in 'Player Settings > allow 'unsafe' code'.
    *
    * Also requires at least the .NET 4.x runtime which is available in Unity 2018.3
    *
    * There are four lists created below, each of different sizes (4, 8, 16, and 32). If a specific size is desired
    * then duplicate one of the structs and update the number of value fields and the MAX_SIZE constant.
    * The actually implementation can be left unchanged with the exception of constructor/Equals type rename.
    * The actual implementation can be left unchanged with the exception of constructor/Equals type rename.
    *
    * Each list implements IList so the methods defined in that interface are available to be used with these struct lists.
    *
  4. cjddmut revised this gist Feb 10, 2019. 1 changed file with 5 additions and 4 deletions.
    9 changes: 5 additions & 4 deletions ValueTypeLists.cs
    Original file line number Diff line number Diff line change
    @@ -76,7 +76,9 @@
    using System.Collections;
    using System.Collections.Generic;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;

    [StructLayout(LayoutKind.Sequential)]
    public unsafe struct ValueTypeList4<T> : IEquatable<ValueTypeList4<T>>, IList<T> where T : unmanaged
    {
    // There are two routes I could take as far as I can tell. I can create a fixed byte array and fit as many
    @@ -349,7 +351,6 @@ public override int GetHashCode()

    unchecked
    {
    // PERF: Fixed form to avoid constant bounds check
    fixed (T* pFirst = &_value0)
    {
    for (int i = 0; i < _count; i++)
    @@ -365,6 +366,7 @@ public override int GetHashCode()
    #endregion
    }

    [StructLayout(LayoutKind.Sequential)]
    public unsafe struct ValueTypeList8<T> : IEquatable<ValueTypeList8<T>>, IList<T> where T : unmanaged
    {
    private T _value0;
    @@ -630,7 +632,6 @@ public override int GetHashCode()

    unchecked
    {
    // PERF: Fixed form to avoid constant bounds check
    fixed (T* pFirst = &_value0)
    {
    for (int i = 0; i < _count; i++)
    @@ -646,6 +647,7 @@ public override int GetHashCode()
    #endregion
    }

    [StructLayout(LayoutKind.Sequential)]
    public unsafe struct ValueTypeList16<T> : IEquatable<ValueTypeList16<T>>, IList<T> where T : unmanaged
    {
    private T _value0;
    @@ -919,7 +921,6 @@ public override int GetHashCode()

    unchecked
    {
    // PERF: Fixed form to avoid constant bounds check
    fixed (T* pFirst = &_value0)
    {
    for (int i = 0; i < _count; i++)
    @@ -935,6 +936,7 @@ public override int GetHashCode()
    #endregion
    }

    [StructLayout(LayoutKind.Sequential)]
    public unsafe struct ValueTypeList32<T> : IEquatable<ValueTypeList32<T>>, IList<T> where T : unmanaged
    {
    private T _value0;
    @@ -1224,7 +1226,6 @@ public override int GetHashCode()

    unchecked
    {
    // PERF: Fixed form to avoid constant bounds check
    fixed (T* pFirst = &_value0)
    {
    for (int i = 0; i < _count; i++)
  5. cjddmut renamed this gist Feb 10, 2019. 1 changed file with 5 additions and 5 deletions.
    10 changes: 5 additions & 5 deletions ValueTypeLists → ValueTypeLists.cs
    Original file line number Diff line number Diff line change
    @@ -1,8 +1,3 @@
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Runtime.CompilerServices;

    /*
    * Created by Galvanic Games (http://galvanicgames.com)
    *
    @@ -77,6 +72,11 @@
    * }
    */

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Runtime.CompilerServices;

    public unsafe struct ValueTypeList4<T> : IEquatable<ValueTypeList4<T>>, IList<T> where T : unmanaged
    {
    // There are two routes I could take as far as I can tell. I can create a fixed byte array and fit as many
  6. cjddmut created this gist Feb 10, 2019.
    1,270 changes: 1,270 additions & 0 deletions ValueTypeLists
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,1270 @@
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Runtime.CompilerServices;

    /*
    * Created by Galvanic Games (http://galvanicgames.com)
    *
    * The MIT License (MIT)
    *
    * Copyright (c) 2019
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy
    * of this software and associated documentation files (the "Software"), to deal
    * in the Software without restriction, including without limitation the rights
    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    * copies of the Software, and to permit persons to whom the Software is
    * furnished to do so, subject to the following conditions:
    *
    * The above copyright notice and this permission notice shall be included in all
    * copies or substantial portions of the Software.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    * SOFTWARE.
    *
    *
    *
    * ============= Description =============
    *
    * Create struct lists that contain unmanaged (NO REFERENCES) types that are passed around as value types. Behaves like
    * any other struct and will not generate garbage when created and unscoped. Since a value type, they can be passed
    * around without fear of a called function changing values unexpectedly.
    *
    * Uses unsafe pointer math so this file needs to be compiled using the /unsafe compiler flag. If working in Unity 3D
    * then enable unsafe code in 'Player Settings > allow 'unsafe' code'.
    *
    * There are four lists created below, each of different sizes (4, 8, 16, and 32). If a specific size is desired
    * then duplicate one of the structs and update the number of value fields and the MAX_SIZE constant.
    * The actually implementation can be left unchanged with the exception of constructor/Equals type rename.
    *
    * Each list implements IList so the methods defined in that interface are available to be used with these struct lists.
    *
    * EXAMPLE:
    *
    * public struct MyStruct
    * {
    * public int intValue;
    *
    * ...
    * }
    *
    * public class MyClass
    * {
    * public void MyFunction()
    * {
    * ValueTypeList4<MyStruct> list = new ValueTypeList4<MyStruct>();
    * list.Add(new MyStruct(1));
    * list.Add(new MyStruct(2));
    * list.Add(new MyStruct(3));
    * list.Add(new MyStruct(4));
    *
    * Print(list[0]) // 1
    * Print(list[2]) // 3
    *
    * list.RemoveAt(2);
    * Print(list[2]) // 4
    *
    *
    * MyOtherFunction(list); // If this function modifies doesn't matter cause list is a value type and is copied
    *
    * } // Won't generate garbage when is out of scope cause it's allocated on the stack
    * }
    */

    public unsafe struct ValueTypeList4<T> : IEquatable<ValueTypeList4<T>>, IList<T> where T : unmanaged
    {
    // There are two routes I could take as far as I can tell. I can create a fixed byte array and fit as many
    // items in as I can. Or just lay out a fixed number side by side. The former is cleaner code, the latter makes
    // better use of space and will be more intuitive to work with. So went with latter. If there's a way to get a fixed
    // array of items that's on the stack then that's the best of both worlds
    //
    // Unfortunately below doesn't work since sizeof(T) isn't known at compile time
    // private fixed byte _buffer[MAX_SIZE * sizeof(T)];
    //
    // And this isn't allowed in C# for unmanaged types, just primitives
    // private fixed T _items[MAX_SIZE];

    private T _value0;
    private T _value1;
    private T _value2;
    private T _value3;

    public const int MAX_SIZE = 4;

    #region Implementation

    private int _count;

    // Generated value by JetBrains
    private const int HASHCODE_MULTIPLIER = 397;
    private const int NO_INDEX = -1;

    public T this[int index]
    {
    get
    {
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }

    fixed (T* pFirst = &_value0)
    {
    return *(pFirst + index);
    }
    }

    set
    {
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }

    fixed (T* pFirst = &_value0)
    {
    *(pFirst + index) = value;
    }
    }
    }

    public int Count => _count;
    public bool IsReadOnly => false;

    public ValueTypeList4(T[] arr) : this()
    {
    AddRange(arr);
    }

    public ValueTypeList4(List<T> list) : this()
    {
    AddRange(list);
    }

    public IEnumerator<T> GetEnumerator()
    {
    for (int i = 0; i < _count; i++)
    {
    yield return this[i];
    }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
    return GetEnumerator();
    }

    public void Add(T item)
    {
    if (_count == MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }

    fixed (T* pFirst = &_value0)
    {
    *(pFirst + _count) = item;
    }

    _count++;
    }

    public void AddRange(T[] arr)
    {
    if (_count + arr.Length > MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }

    fixed (T* pMe = &_value0, pThem = arr)
    {
    long copySizeInBytes = arr.Length * sizeof(T);

    Buffer.MemoryCopy(pThem, pMe + _count, copySizeInBytes, copySizeInBytes);
    }

    _count += arr.Length;
    }

    public void AddRange(List<T> list)
    {
    if (_count + list.Count > MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }

    fixed (T* pFirst = &_value0)
    {
    int listCount = list.Count;

    for (int i = 0; i < listCount; i++)
    {
    *(pFirst + i + _count) = list[i];
    }
    }
    }

    public void Clear()
    {
    _count = 0;
    }

    public bool Contains(T item)
    {
    return IndexOf(item) != NO_INDEX;
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
    fixed (T* pMe = &_value0, pThem = array)
    {
    long sizeInBytes = _count * sizeof(T);

    Buffer.MemoryCopy(
    pMe,
    pThem + arrayIndex,
    sizeInBytes,
    sizeInBytes);
    }
    }

    public bool Remove(T item)
    {
    int index = IndexOf(item);

    if (index != NO_INDEX)
    {
    RemoveAt(index);
    return true;
    }

    return false;
    }

    public int IndexOf(T item)
    {
    fixed (T* pFirst = &_value0)
    {
    int size = sizeof(T);

    for (int i = 0; i < _count; i++)
    {
    // Similarly, if we later force T to implement IEquatable<T> then this should be replaced with
    // (pFirst + i)->Equals(item)
    if (ValueTypeListUtil.MemoryCompare(pFirst + i, &item, size))
    {
    return i;
    }
    }
    }

    return NO_INDEX;
    }

    public void Insert(int index, T item)
    {
    if (_count == MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }

    fixed (T* pFirst = &_value0)
    {
    _count++;

    for (int i = _count - 1; i >= index + 1; i--)
    {
    *(pFirst + i) = *(pFirst + i - 1);
    }

    *(pFirst + index) = item;
    }
    }

    public void RemoveAt(int index)
    {
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }

    fixed (T* pFirst = &_value0)
    {
    T* pItem = pFirst + index;
    long copyAmountBytes = sizeof(T) * (_count - (index + 1));

    Buffer.MemoryCopy(
    pItem + 1,
    pItem,
    copyAmountBytes,
    copyAmountBytes);
    }

    _count--;
    }

    public void UnstableRemoveAt(int index)
    {
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }

    fixed (T* pFirst = &_value0)
    {
    *(pFirst + index) = *(pFirst + _count - 1);
    }

    _count--;
    }

    public bool Equals(ValueTypeList4<T> other)
    {
    if (_count != other._count)
    {
    return false;
    }

    // We do a memory compare since T may not implement IEquatable<T>, if that is too rigid then change
    // to force T to implement IEquatable<T> and just call (pFirst + i)->Equals(item).
    fixed (T* pFirst = &_value0)
    {
    // Below errors out with "You cannot use the fixed statement to take the address of an already fixed expression"
    // So I interpret that as I'm already safe? But I don't fully follow or understand so research is needed.
    // fixed(T* pFirstOther = &other._value0) {}
    return ValueTypeListUtil.MemoryCompare(pFirst, &other._value0, Count * sizeof(T));
    }
    }

    public override int GetHashCode()
    {
    // If T doesn't implement IEquatable<T> this will generate garbage
    int hashCode = 0;

    unchecked
    {
    // PERF: Fixed form to avoid constant bounds check
    fixed (T* pFirst = &_value0)
    {
    for (int i = 0; i < _count; i++)
    {
    hashCode = (hashCode * HASHCODE_MULTIPLIER) ^ ((pFirst + i)->GetHashCode());
    }
    }

    }

    return hashCode;
    }
    #endregion
    }

    public unsafe struct ValueTypeList8<T> : IEquatable<ValueTypeList8<T>>, IList<T> where T : unmanaged
    {
    private T _value0;
    private T _value1;
    private T _value2;
    private T _value3;
    private T _value4;
    private T _value5;
    private T _value6;
    private T _value7;

    public const int MAX_SIZE = 8;

    #region Implementation

    private int _count;

    // Generated value by JetBrains
    private const int HASHCODE_MULTIPLIER = 397;
    private const int NO_INDEX = -1;

    public T this[int index]
    {
    get
    {
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }

    fixed (T* pFirst = &_value0)
    {
    return *(pFirst + index);
    }
    }

    set
    {
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }

    fixed (T* pFirst = &_value0)
    {
    *(pFirst + index) = value;
    }
    }
    }

    public int Count => _count;
    public bool IsReadOnly => false;

    public ValueTypeList8(T[] arr) : this()
    {
    AddRange(arr);
    }

    public ValueTypeList8(List<T> list) : this()
    {
    AddRange(list);
    }

    public IEnumerator<T> GetEnumerator()
    {
    for (int i = 0; i < _count; i++)
    {
    yield return this[i];
    }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
    return GetEnumerator();
    }

    public void Add(T item)
    {
    if (_count == MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }

    fixed (T* pFirst = &_value0)
    {
    *(pFirst + _count) = item;
    }

    _count++;
    }

    public void AddRange(T[] arr)
    {
    if (_count + arr.Length > MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }

    fixed (T* pMe = &_value0, pThem = arr)
    {
    long copySizeInBytes = arr.Length * sizeof(T);

    Buffer.MemoryCopy(pThem, pMe + _count, copySizeInBytes, copySizeInBytes);
    }

    _count += arr.Length;
    }

    public void AddRange(List<T> list)
    {
    if (_count + list.Count > MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }

    fixed (T* pFirst = &_value0)
    {
    int listCount = list.Count;

    for (int i = 0; i < listCount; i++)
    {
    *(pFirst + i + _count) = list[i];
    }
    }
    }

    public void Clear()
    {
    _count = 0;
    }

    public bool Contains(T item)
    {
    return IndexOf(item) != NO_INDEX;
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
    fixed (T* pMe = &_value0, pThem = array)
    {
    long sizeInBytes = _count * sizeof(T);

    Buffer.MemoryCopy(
    pMe,
    pThem + arrayIndex,
    sizeInBytes,
    sizeInBytes);
    }
    }

    public bool Remove(T item)
    {
    int index = IndexOf(item);

    if (index != NO_INDEX)
    {
    RemoveAt(index);
    return true;
    }

    return false;
    }

    public int IndexOf(T item)
    {
    fixed (T* pFirst = &_value0)
    {
    int size = sizeof(T);

    for (int i = 0; i < _count; i++)
    {
    // Similarly, if we later force T to implement IEquatable<T> then this should be replaced with
    // (pFirst + i)->Equals(item)
    if (ValueTypeListUtil.MemoryCompare(pFirst + i, &item, size))
    {
    return i;
    }
    }
    }

    return NO_INDEX;
    }

    public void Insert(int index, T item)
    {
    if (_count == MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }

    fixed (T* pFirst = &_value0)
    {
    _count++;

    for (int i = _count - 1; i >= index + 1; i--)
    {
    *(pFirst + i) = *(pFirst + i - 1);
    }

    *(pFirst + index) = item;
    }
    }

    public void RemoveAt(int index)
    {
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }

    fixed (T* pFirst = &_value0)
    {
    T* pItem = pFirst + index;
    long copyAmountBytes = sizeof(T) * (_count - (index + 1));

    Buffer.MemoryCopy(
    pItem + 1,
    pItem,
    copyAmountBytes,
    copyAmountBytes);
    }

    _count--;
    }

    public void UnstableRemoveAt(int index)
    {
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }

    fixed (T* pFirst = &_value0)
    {
    *(pFirst + index) = *(pFirst + _count - 1);
    }

    _count--;
    }

    public bool Equals(ValueTypeList8<T> other)
    {
    if (_count != other._count)
    {
    return false;
    }

    // We do a memory compare since T may not implement IEquatable<T>, if that is too rigid then change
    // to force T to implement IEquatable<T> and just call (pFirst + i)->Equals(item).
    fixed (T* pFirst = &_value0)
    {
    // Below errors out with "You cannot use the fixed statement to take the address of an already fixed expression"
    // So I interpret that as I'm already safe? But I don't fully follow or understand so research is needed.
    // fixed(T* pFirstOther = &other._value0) {}
    return ValueTypeListUtil.MemoryCompare(pFirst, &other._value0, Count * sizeof(T));
    }
    }

    public override int GetHashCode()
    {
    // If T doesn't implement IEquatable<T> this will generate garbage
    int hashCode = 0;

    unchecked
    {
    // PERF: Fixed form to avoid constant bounds check
    fixed (T* pFirst = &_value0)
    {
    for (int i = 0; i < _count; i++)
    {
    hashCode = (hashCode * HASHCODE_MULTIPLIER) ^ ((pFirst + i)->GetHashCode());
    }
    }

    }

    return hashCode;
    }
    #endregion
    }

    public unsafe struct ValueTypeList16<T> : IEquatable<ValueTypeList16<T>>, IList<T> where T : unmanaged
    {
    private T _value0;
    private T _value1;
    private T _value2;
    private T _value3;
    private T _value4;
    private T _value5;
    private T _value6;
    private T _value7;
    private T _value8;
    private T _value9;
    private T _value10;
    private T _value11;
    private T _value12;
    private T _value13;
    private T _value14;
    private T _value15;

    public const int MAX_SIZE = 16;

    #region Implementation

    private int _count;

    // Generated value by JetBrains
    private const int HASHCODE_MULTIPLIER = 397;
    private const int NO_INDEX = -1;

    public T this[int index]
    {
    get
    {
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }

    fixed (T* pFirst = &_value0)
    {
    return *(pFirst + index);
    }
    }

    set
    {
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }

    fixed (T* pFirst = &_value0)
    {
    *(pFirst + index) = value;
    }
    }
    }

    public int Count => _count;
    public bool IsReadOnly => false;

    public ValueTypeList16(T[] arr) : this()
    {
    AddRange(arr);
    }

    public ValueTypeList16(List<T> list) : this()
    {
    AddRange(list);
    }

    public IEnumerator<T> GetEnumerator()
    {
    for (int i = 0; i < _count; i++)
    {
    yield return this[i];
    }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
    return GetEnumerator();
    }

    public void Add(T item)
    {
    if (_count == MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }

    fixed (T* pFirst = &_value0)
    {
    *(pFirst + _count) = item;
    }

    _count++;
    }

    public void AddRange(T[] arr)
    {
    if (_count + arr.Length > MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }

    fixed (T* pMe = &_value0, pThem = arr)
    {
    long copySizeInBytes = arr.Length * sizeof(T);

    Buffer.MemoryCopy(pThem, pMe + _count, copySizeInBytes, copySizeInBytes);
    }

    _count += arr.Length;
    }

    public void AddRange(List<T> list)
    {
    if (_count + list.Count > MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }

    fixed (T* pFirst = &_value0)
    {
    int listCount = list.Count;

    for (int i = 0; i < listCount; i++)
    {
    *(pFirst + i + _count) = list[i];
    }
    }
    }

    public void Clear()
    {
    _count = 0;
    }

    public bool Contains(T item)
    {
    return IndexOf(item) != NO_INDEX;
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
    fixed (T* pMe = &_value0, pThem = array)
    {
    long sizeInBytes = _count * sizeof(T);

    Buffer.MemoryCopy(
    pMe,
    pThem + arrayIndex,
    sizeInBytes,
    sizeInBytes);
    }
    }

    public bool Remove(T item)
    {
    int index = IndexOf(item);

    if (index != NO_INDEX)
    {
    RemoveAt(index);
    return true;
    }

    return false;
    }

    public int IndexOf(T item)
    {
    fixed (T* pFirst = &_value0)
    {
    int size = sizeof(T);

    for (int i = 0; i < _count; i++)
    {
    // Similarly, if we later force T to implement IEquatable<T> then this should be replaced with
    // (pFirst + i)->Equals(item)
    if (ValueTypeListUtil.MemoryCompare(pFirst + i, &item, size))
    {
    return i;
    }
    }
    }

    return NO_INDEX;
    }

    public void Insert(int index, T item)
    {
    if (_count == MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }

    fixed (T* pFirst = &_value0)
    {
    _count++;

    for (int i = _count - 1; i >= index + 1; i--)
    {
    *(pFirst + i) = *(pFirst + i - 1);
    }

    *(pFirst + index) = item;
    }
    }

    public void RemoveAt(int index)
    {
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }

    fixed (T* pFirst = &_value0)
    {
    T* pItem = pFirst + index;
    long copyAmountBytes = sizeof(T) * (_count - (index + 1));

    Buffer.MemoryCopy(
    pItem + 1,
    pItem,
    copyAmountBytes,
    copyAmountBytes);
    }

    _count--;
    }

    public void UnstableRemoveAt(int index)
    {
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }

    fixed (T* pFirst = &_value0)
    {
    *(pFirst + index) = *(pFirst + _count - 1);
    }

    _count--;
    }

    public bool Equals(ValueTypeList16<T> other)
    {
    if (_count != other._count)
    {
    return false;
    }

    // We do a memory compare since T may not implement IEquatable<T>, if that is too rigid then change
    // to force T to implement IEquatable<T> and just call (pFirst + i)->Equals(item).
    fixed (T* pFirst = &_value0)
    {
    // Below errors out with "You cannot use the fixed statement to take the address of an already fixed expression"
    // So I interpret that as I'm already safe? But I don't fully follow or understand so research is needed.
    // fixed(T* pFirstOther = &other._value0) {}
    return ValueTypeListUtil.MemoryCompare(pFirst, &other._value0, Count * sizeof(T));
    }
    }

    public override int GetHashCode()
    {
    // If T doesn't implement IEquatable<T> this will generate garbage
    int hashCode = 0;

    unchecked
    {
    // PERF: Fixed form to avoid constant bounds check
    fixed (T* pFirst = &_value0)
    {
    for (int i = 0; i < _count; i++)
    {
    hashCode = (hashCode * HASHCODE_MULTIPLIER) ^ ((pFirst + i)->GetHashCode());
    }
    }

    }

    return hashCode;
    }
    #endregion
    }

    public unsafe struct ValueTypeList32<T> : IEquatable<ValueTypeList32<T>>, IList<T> where T : unmanaged
    {
    private T _value0;
    private T _value1;
    private T _value2;
    private T _value3;
    private T _value4;
    private T _value5;
    private T _value6;
    private T _value7;
    private T _value8;
    private T _value9;
    private T _value10;
    private T _value11;
    private T _value12;
    private T _value13;
    private T _value14;
    private T _value15;
    private T _value16;
    private T _value17;
    private T _value18;
    private T _value19;
    private T _value20;
    private T _value21;
    private T _value22;
    private T _value23;
    private T _value24;
    private T _value25;
    private T _value26;
    private T _value27;
    private T _value28;
    private T _value29;
    private T _value30;
    private T _value31;

    public const int MAX_SIZE = 32;

    #region Implementation

    private int _count;

    // Generated value by JetBrains
    private const int HASHCODE_MULTIPLIER = 397;
    private const int NO_INDEX = -1;

    public T this[int index]
    {
    get
    {
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }

    fixed (T* pFirst = &_value0)
    {
    return *(pFirst + index);
    }
    }

    set
    {
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }

    fixed (T* pFirst = &_value0)
    {
    *(pFirst + index) = value;
    }
    }
    }

    public int Count => _count;
    public bool IsReadOnly => false;

    public ValueTypeList32(T[] arr) : this()
    {
    AddRange(arr);
    }

    public ValueTypeList32(List<T> list) : this()
    {
    AddRange(list);
    }

    public IEnumerator<T> GetEnumerator()
    {
    for (int i = 0; i < _count; i++)
    {
    yield return this[i];
    }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
    return GetEnumerator();
    }

    public void Add(T item)
    {
    if (_count == MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }

    fixed (T* pFirst = &_value0)
    {
    *(pFirst + _count) = item;
    }

    _count++;
    }

    public void AddRange(T[] arr)
    {
    if (_count + arr.Length > MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }

    fixed (T* pMe = &_value0, pThem = arr)
    {
    long copySizeInBytes = arr.Length * sizeof(T);

    Buffer.MemoryCopy(pThem, pMe + _count, copySizeInBytes, copySizeInBytes);
    }

    _count += arr.Length;
    }

    public void AddRange(List<T> list)
    {
    if (_count + list.Count > MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }

    fixed (T* pFirst = &_value0)
    {
    int listCount = list.Count;

    for (int i = 0; i < listCount; i++)
    {
    *(pFirst + i + _count) = list[i];
    }
    }
    }

    public void Clear()
    {
    _count = 0;
    }

    public bool Contains(T item)
    {
    return IndexOf(item) != NO_INDEX;
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
    fixed (T* pMe = &_value0, pThem = array)
    {
    long sizeInBytes = _count * sizeof(T);

    Buffer.MemoryCopy(
    pMe,
    pThem + arrayIndex,
    sizeInBytes,
    sizeInBytes);
    }
    }

    public bool Remove(T item)
    {
    int index = IndexOf(item);

    if (index != NO_INDEX)
    {
    RemoveAt(index);
    return true;
    }

    return false;
    }

    public int IndexOf(T item)
    {
    fixed (T* pFirst = &_value0)
    {
    int size = sizeof(T);

    for (int i = 0; i < _count; i++)
    {
    // Similarly, if we later force T to implement IEquatable<T> then this should be replaced with
    // (pFirst + i)->Equals(item)
    if (ValueTypeListUtil.MemoryCompare(pFirst + i, &item, size))
    {
    return i;
    }
    }
    }

    return NO_INDEX;
    }

    public void Insert(int index, T item)
    {
    if (_count == MAX_SIZE)
    {
    throw new ValueTypeListFull();
    }

    fixed (T* pFirst = &_value0)
    {
    _count++;

    for (int i = _count - 1; i >= index + 1; i--)
    {
    *(pFirst + i) = *(pFirst + i - 1);
    }

    *(pFirst + index) = item;
    }
    }

    public void RemoveAt(int index)
    {
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }

    fixed (T* pFirst = &_value0)
    {
    T* pItem = pFirst + index;
    long copyAmountBytes = sizeof(T) * (_count - (index + 1));

    Buffer.MemoryCopy(
    pItem + 1,
    pItem,
    copyAmountBytes,
    copyAmountBytes);
    }

    _count--;
    }

    public void UnstableRemoveAt(int index)
    {
    if (index < 0 || index >= _count)
    {
    throw new IndexOutOfRangeException();
    }

    fixed (T* pFirst = &_value0)
    {
    *(pFirst + index) = *(pFirst + _count - 1);
    }

    _count--;
    }

    public bool Equals(ValueTypeList32<T> other)
    {
    if (_count != other._count)
    {
    return false;
    }

    // We do a memory compare since T may not implement IEquatable<T>, if that is too rigid then change
    // to force T to implement IEquatable<T> and just call (pFirst + i)->Equals(item).
    fixed (T* pFirst = &_value0)
    {
    // Below errors out with "You cannot use the fixed statement to take the address of an already fixed expression"
    // So I interpret that as I'm already safe? But I don't fully follow or understand so research is needed.
    // fixed(T* pFirstOther = &other._value0) {}
    return ValueTypeListUtil.MemoryCompare(pFirst, &other._value0, Count * sizeof(T));
    }
    }

    public override int GetHashCode()
    {
    // If T doesn't implement IEquatable<T> this will generate garbage
    int hashCode = 0;

    unchecked
    {
    // PERF: Fixed form to avoid constant bounds check
    fixed (T* pFirst = &_value0)
    {
    for (int i = 0; i < _count; i++)
    {
    hashCode = (hashCode * HASHCODE_MULTIPLIER) ^ ((pFirst + i)->GetHashCode());
    }
    }

    }

    return hashCode;
    }
    #endregion
    }

    public class ValueTypeListFull : Exception
    {
    }

    public static unsafe class ValueTypeListUtil
    {
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool MemoryCompare(void* p1, void* p2, int sizeInBytes)
    {
    byte* pByte1 = (byte*) p1;
    byte* pByte2 = (byte*) p2;

    // There are some interesting solutions here, possibly faster ones?
    // https://stackoverflow.com/questions/43289/comparing-two-byte-arrays-in-net
    for (int i = 0; i < sizeInBytes; i++)
    {
    if (*pByte1 != *pByte2)
    {
    return false;
    }

    pByte1++;
    pByte2++;
    }

    return true;
    }
    }