Stashbox by Peter Csajtai

<PackageReference Include="Stashbox" Version="3.2.0-preview-564" />

 ImmutableArray<TKey, TValue>

class ImmutableArray<TKey, TValue> : IImmutableArray<TKey, TValue>, IEnumerable<TValue>, IEnumerable
using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; namespace Stashbox.Utils.Data.Immutable { internal class ImmutableArray<TKey, TValue> : IImmutableArray<TKey, TValue>, IEnumerable<TValue>, IEnumerable { private class ImmutableBucket : IImmutableArray<TKey, TValue>, IEnumerable<TValue>, IEnumerable { public readonly int Length; private readonly KeyValue<TKey, TValue>[] repository; public ref KeyValue<TKey, TValue> this[int i] { get { return ref repository[i]; } } public ImmutableBucket(KeyValue<TKey, TValue>[] repository) { this.repository = repository; Length = repository.Length; } public IImmutableArray<TKey, TValue> Add(TKey key, TValue value) { if (Length < 32) return AddInternal(key, value); return new ImmutableArray<TKey, TValue>(new ImmutableBucket[1] { this }).AddInternal(key, value); } public IImmutableArray<TKey, TValue> AddOrUpdate(TKey key, TValue value, bool byRef, Func<TValue, TValue, TValue> update = null) { if (Length >= 32) return new ImmutableArray<TKey, TValue>(new ImmutableBucket[1] { this }).AddOrUpdate(key, value, byRef, update); if (!TryUpdate(key, value, byRef, out ImmutableBucket updated, update)) return AddInternal(key, value); return updated; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TValue GetOrDefault(TKey key, bool byRef) { int num = repository.Length; for (int i = 0; i < num; i++) { ref KeyValue<TKey, TValue> reference = ref repository[i]; if ((byRef && (object)reference.Key == (object)key) || (!byRef && object.Equals(reference.Key, key))) return reference.Value; } return default(TValue); } internal ImmutableBucket AddInternal(TKey key, TValue value) { KeyValue<TKey, TValue>[] array = new KeyValue<TKey, TValue>[Length + 1]; Array.Copy(repository, array, Length); array[Length] = new KeyValue<TKey, TValue>(key, value); return new ImmutableBucket(array); } internal bool TryUpdate(TKey key, TValue value, bool byRef, out ImmutableBucket updated, Func<TValue, TValue, TValue> update = null) { updated = null; int length = Length; int num; for (num = length - 1; num >= 0; num--) { ref KeyValue<TKey, TValue> reference = ref repository[num]; if ((byRef && (object)reference.Key == (object)key) || (!byRef && object.Equals(reference.Key, key))) break; } if (num == -1) return false; KeyValue<TKey, TValue>[] array = new KeyValue<TKey, TValue>[length]; Array.Copy(repository, array, length); value = ((update == null) ? value : update(array[num].Value, value)); array[num] = new KeyValue<TKey, TValue>(key, value); updated = new ImmutableBucket(array); return true; } public IEnumerable<KeyValue<TKey, TValue>> Walk() { for (int i = 0; i < this.Length; i++) { yield return this.repository[i]; } } public IEnumerator<TValue> GetEnumerator() { for (int i = 0; i < this.Length; i++) { yield return this.repository[i].Value; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } public static readonly IImmutableArray<TKey, TValue> Empty = new ImmutableArray<TKey, TValue>(Constants.EmptyArray<ImmutableBucket>()); private const int BucketLength = 32; public readonly int Length; private readonly ImmutableBucket[] repository; private ImmutableArray(ImmutableBucket[] repository) { this.repository = repository; Length = ((repository.Length != 0) ? ((repository.Length - 1) * 32 + repository[repository.Length - 1].Length) : 0); } public IImmutableArray<TKey, TValue> Add(TKey key, TValue value) { if (Length == 0) return new ImmutableBucket(new KeyValue<TKey, TValue>[1] { new KeyValue<TKey, TValue>(key, value) }); return AddInternal(key, value); } public IImmutableArray<TKey, TValue> AddOrUpdate(TKey key, TValue value, bool byRef, Func<TValue, TValue, TValue> update = null) { if (Length == 0) return new ImmutableBucket(new KeyValue<TKey, TValue>[1] { new KeyValue<TKey, TValue>(key, value) }); int num = repository.Length; int num2 = num - 1; ImmutableBucket updated = null; while (num2 >= 0 && !repository[num2].TryUpdate(key, value, byRef, out updated, update)) { num2--; } if (num2 == -1) return AddInternal(key, value); ImmutableBucket[] array = new ImmutableBucket[num]; Array.Copy(repository, array, num); array[num2] = updated; return new ImmutableArray<TKey, TValue>(array); } private IImmutableArray<TKey, TValue> AddInternal(TKey key, TValue value) { int num = repository.Length; int num2 = num - 1; ImmutableBucket immutableBucket = repository[num2]; if (immutableBucket.Length >= 32) { ImmutableBucket[] array = new ImmutableBucket[num + 1]; Array.Copy(repository, array, num); array[num] = new ImmutableBucket(new KeyValue<TKey, TValue>[1] { new KeyValue<TKey, TValue>(key, value) }); return new ImmutableArray<TKey, TValue>(array); } ImmutableBucket[] array2 = new ImmutableBucket[num]; Array.Copy(repository, array2, num); array2[num2] = immutableBucket.AddInternal(key, value); return new ImmutableArray<TKey, TValue>(array2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public TValue GetOrDefault(TKey key, bool byRef) { int num = repository.Length; for (int i = 0; i < num; i++) { int length = repository[i].Length; for (int j = 0; j < length; j++) { ref KeyValue<TKey, TValue> reference = ref repository[i][j]; if ((byRef && (object)reference.Key == (object)key) || (!byRef && object.Equals(reference.Key, key))) return reference.Value; } } return default(TValue); } public IEnumerable<KeyValue<TKey, TValue>> Walk() { int length = this.repository.Length; for (int j = 0; j < length; j++) { int bucketLength = this.repository[j].Length; for (int i = 0; i < bucketLength; i++) { yield return this.repository[j][i]; } } } public IEnumerator<TValue> GetEnumerator() { int length = this.repository.Length; for (int j = 0; j < length; j++) { int bucketLength = this.repository[j].Length; for (int i = 0; i < bucketLength; i++) { yield return this.repository[j][i].Value; } } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } }