diff --git a/Benchmark/Benchmark.csproj b/Benchmark/Benchmark.csproj
new file mode 100644
index 0000000..9752129
--- /dev/null
+++ b/Benchmark/Benchmark.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Exe
+ net8.0;net6.0;net7.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Benchmark/BenchmarkFlatHashMap.cs b/Benchmark/BenchmarkFlatHashMap.cs
new file mode 100644
index 0000000..200fdcc
--- /dev/null
+++ b/Benchmark/BenchmarkFlatHashMap.cs
@@ -0,0 +1,77 @@
+using BenchmarkDotNet.Attributes;
+using BetterCollections;
+
+namespace Benchmark;
+
+[MemoryDiagnoser]
+[RPlotExporter]
+public class BenchmarkFlatHashMap_Add1000
+{
+ [Benchmark]
+ public FlatHashMap FlatHashMap()
+ {
+ var map = new FlatHashMap();
+ for (var i = 0; i < 1000; i++)
+ {
+ map.Add(i, i);
+ }
+ return map;
+ }
+
+ [Benchmark(Baseline = true)]
+ public Dictionary Dictionary()
+ {
+ var map = new Dictionary();
+ for (var i = 0; i < 1000; i++)
+ {
+ map.Add(i, i);
+ }
+ return map;
+ }
+}
+
+[MemoryDiagnoser]
+[RPlotExporter]
+public class BenchmarkFlatHashMap_Get1000
+{
+ private FlatHashMap flatHashMap = new();
+ private Dictionary dictionary = new();
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ flatHashMap = new FlatHashMap();
+ for (var i = 0; i < 1000; i++)
+ {
+ flatHashMap.Add(i, i);
+ }
+
+ dictionary = new Dictionary();
+ for (var i = 0; i < 1000; i++)
+ {
+ dictionary.Add(i, i);
+ }
+ }
+
+ [Benchmark]
+ public int FlatHashMap()
+ {
+ var a = 0;
+ for (var i = 0; i < 1000; i++)
+ {
+ a += flatHashMap[i];
+ }
+ return a;
+ }
+
+ [Benchmark(Baseline = true)]
+ public int Dictionary()
+ {
+ var a = 0;
+ for (var i = 0; i < 1000; i++)
+ {
+ a += dictionary[i];
+ }
+ return a;
+ }
+}
diff --git a/Benchmark/Program.cs b/Benchmark/Program.cs
new file mode 100644
index 0000000..b986d99
--- /dev/null
+++ b/Benchmark/Program.cs
@@ -0,0 +1,3 @@
+using BenchmarkDotNet.Running;
+
+BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
diff --git a/BetterCollections.sln b/BetterCollections.sln
index 5de0d9f..51889ad 100644
--- a/BetterCollections.sln
+++ b/BetterCollections.sln
@@ -4,6 +4,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BetterCollections", "Better
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{95336675-5EC6-470C-A4A9-A0D44B28C987}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmark", "Benchmark\Benchmark.csproj", "{745D3703-B07D-4B10-95D5-A96AD07E6BE9}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -18,5 +20,9 @@ Global
{95336675-5EC6-470C-A4A9-A0D44B28C987}.Debug|Any CPU.Build.0 = Debug|Any CPU
{95336675-5EC6-470C-A4A9-A0D44B28C987}.Release|Any CPU.ActiveCfg = Release|Any CPU
{95336675-5EC6-470C-A4A9-A0D44B28C987}.Release|Any CPU.Build.0 = Release|Any CPU
+ {745D3703-B07D-4B10-95D5-A96AD07E6BE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {745D3703-B07D-4B10-95D5-A96AD07E6BE9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {745D3703-B07D-4B10-95D5-A96AD07E6BE9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {745D3703-B07D-4B10-95D5-A96AD07E6BE9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/BetterCollections/Exceptions/UnexpectedConcurrentException.cs b/BetterCollections/Exceptions/UnexpectedConcurrentException.cs
new file mode 100644
index 0000000..d85359f
--- /dev/null
+++ b/BetterCollections/Exceptions/UnexpectedConcurrentException.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace BetterCollections.Exceptions;
+
+public class UnexpectedConcurrentException : Exception
+{
+ public UnexpectedConcurrentException() : this(
+ "A concurrent call occurred on a container that does not allow concurrency, and the internal state may have been corrupted") { }
+
+ public UnexpectedConcurrentException(string message) : base(message) { }
+ public UnexpectedConcurrentException(string message, Exception inner) : base(message, inner) { }
+}
diff --git a/BetterCollections/Misc/IRefGet.cs b/BetterCollections/Misc/IRefGet.cs
new file mode 100644
index 0000000..21d8176
--- /dev/null
+++ b/BetterCollections/Misc/IRefGet.cs
@@ -0,0 +1,45 @@
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+
+namespace BetterCollections.Misc;
+
+public interface IGetter
+{
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public T Get(in S source);
+}
+
+public readonly struct IdentityGetter : IGetter
+{
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public T Get(in T source) => source;
+}
+
+public readonly struct KeyValueGetter : IGetter, (K Key, V Value)>
+{
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public KeyValuePair Get(in (K Key, V Value) source)
+ => new(source.Key, source.Value);
+}
+
+public readonly struct KeyGetter : IGetter, IGetter>
+{
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public K Get(in (K Key, V Value) source)
+ => source.Key;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public K Get(in KeyValuePair source)
+ => source.Key;
+}
+
+public readonly struct ValueGetter : IGetter, IGetter>
+{
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public V Get(in (K Key, V Value) source)
+ => source.Value;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public V Get(in KeyValuePair source)
+ => source.Value;
+}
diff --git a/BetterCollections/Misc/Utils.cs b/BetterCollections/Misc/Utils.cs
index a20c8d4..a61d7dc 100644
--- a/BetterCollections/Misc/Utils.cs
+++ b/BetterCollections/Misc/Utils.cs
@@ -138,4 +138,18 @@ public static string ToBinaryString(this ulong value)
return Regex.Replace(a, ".{8}(?!$)", "$0_");
#endif
}
+
+ ///
+ /// Evaluate whether a given integral value is a power of 2.
+ ///
+ /// The value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsPow2(long value) => (value & (value - 1)) == 0 && value > 0;
+
+ ///
+ /// Evaluate whether a given integral value is a power of 2.
+ ///
+ /// The value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsPow2(ulong value) => (value & (value - 1)) == 0 && value > 0;
}