Last active
March 19, 2026 12:02
-
-
Save cjddmut/cb43af3ee191af78363f41a3188c0f7b to your computer and use it in GitHub Desktop.
Revisions
-
cjddmut revised this gist
Feb 11, 2019 . 1 changed file with 71 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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) { -
cjddmut revised this gist
Feb 10, 2019 . 1 changed file with 7 additions and 7 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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 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)); } } @@ -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)); } } @@ -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)); } } @@ -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)); } } -
cjddmut revised this gist
Feb 10, 2019 . 1 changed file with 3 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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 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. * -
cjddmut revised this gist
Feb 10, 2019 . 1 changed file with 5 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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 { 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 { 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 { 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 { fixed (T* pFirst = &_value0) { for (int i = 0; i < _count; i++) -
cjddmut renamed this gist
Feb 10, 2019 . 1 changed file with 5 additions and 5 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,8 +1,3 @@ /* * 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 -
cjddmut created this gist
Feb 10, 2019 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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; } }