Initial BTree version implemented.
This commit is contained in:
1 parent
e8cca74f68
commit
bca059c4f2
2 files changed
+592
No files matched your search
@@ -0,0 +1,321 @@
|
|||||||
|
using BenchmarkDotNet.Attributes;
|
||||||
|
using BenchmarkDotNet.Jobs;
|
||||||
|
using ILGPU;
|
||||||
|
using Open.Numeric.Primes;
|
||||||
|
using PerfectNumbers.Core;
|
||||||
|
|
||||||
|
namespace EvenPerfectBitScanner.Benchmarks;
|
||||||
|
|
||||||
|
[ShortRunJob(RuntimeMoniker.Net80)]
|
||||||
|
[MemoryDiagnoser]
|
||||||
|
public class BTreeBenchmarks
|
||||||
|
{
|
||||||
|
private ulong[] _candidates = [];
|
||||||
|
private int[] _candidatesInt = [];
|
||||||
|
|
||||||
|
[ParamsSource(nameof(GetBenchmarkCases))]
|
||||||
|
public PrimeBenchmarkCase BenchmarkCase { get; set; }
|
||||||
|
|
||||||
|
[GlobalSetup]
|
||||||
|
public void GlobalSetup()
|
||||||
|
{
|
||||||
|
_candidates = BenchmarkCase.Candidates;
|
||||||
|
_candidatesInt = [.. _candidates.Select(x => (int)x)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds all candidate ints into the keyed B-tree (key = value), using tiny/inline and mid-size fast paths; count comes from the benchmark case or defaults to 4,096 when unspecified.
|
||||||
|
/// </summary>
|
||||||
|
[Benchmark]
|
||||||
|
public BTree<int, int> BTreeAddInt()
|
||||||
|
{
|
||||||
|
int[] values = _candidatesInt;
|
||||||
|
int prime;
|
||||||
|
BTree<int, int> result = new();
|
||||||
|
for (int i = 0; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
prime = values[i];
|
||||||
|
result.Add(prime, prime);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds all candidate ints into a dictionary keyed by their hash code (baseline); count comes from the benchmark case or defaults to 4,096 when unspecified.
|
||||||
|
/// </summary>
|
||||||
|
[Benchmark(Baseline = true)]
|
||||||
|
public Dictionary<int, int> DictionaryAddInt()
|
||||||
|
{
|
||||||
|
int[] values = _candidatesInt;
|
||||||
|
int prime;
|
||||||
|
Dictionary<int, int> result = [];
|
||||||
|
for (int i = 0; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
prime = values[i];
|
||||||
|
result.Add(prime.GetHashCode(), prime);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds all candidate ulongs into the keyed B-tree (key = value), using tiny/inline and mid-size fast paths; count comes from the benchmark case or defaults to 4,096 when unspecified.
|
||||||
|
/// </summary>
|
||||||
|
[Benchmark]
|
||||||
|
public BTree<ulong, ulong> BTreeAddUlong()
|
||||||
|
{
|
||||||
|
ulong[] values = _candidates;
|
||||||
|
ulong prime;
|
||||||
|
BTree<ulong, ulong> result = new();
|
||||||
|
for (int i = 0; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
prime = values[i];
|
||||||
|
result.Add(prime, prime);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds all candidate ulongs into a dictionary keyed by their hash code; count comes from the benchmark case or defaults to 4,096 when unspecified.
|
||||||
|
/// </summary>
|
||||||
|
[Benchmark]
|
||||||
|
public Dictionary<int, ulong> DictionaryAddUlong()
|
||||||
|
{
|
||||||
|
ulong[] values = _candidates;
|
||||||
|
ulong prime;
|
||||||
|
Dictionary<int, ulong> result = [];
|
||||||
|
for (int i = 0; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
prime = values[i];
|
||||||
|
result.Add(prime.GetHashCode(), prime);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<PrimeBenchmarkCase> GetBenchmarkCases()
|
||||||
|
{
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"≤100",
|
||||||
|
"Admissible odd values ≤ 100 (k ≤ 5, not divisible by 5)",
|
||||||
|
3UL,
|
||||||
|
100UL,
|
||||||
|
int.MaxValue);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"4x ≤4_000_000",
|
||||||
|
"4x sample admissible odd values ≤ 4,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
3UL,
|
||||||
|
4_000_000UL,
|
||||||
|
4);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"12x ≤4_000_000",
|
||||||
|
"12x sample admissible odd values ≤ 4,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
3UL,
|
||||||
|
4_000_000UL,
|
||||||
|
12);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"16x ≤4_000_000",
|
||||||
|
"16x sample admissible odd values ≤ 4,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
3UL,
|
||||||
|
4_000_000UL,
|
||||||
|
16);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"32x ≤4_000_000",
|
||||||
|
"16x sample admissible odd values ≤ 4,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
3UL,
|
||||||
|
4_000_000UL,
|
||||||
|
32);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"64x ≤4_000_000",
|
||||||
|
"16x sample admissible odd values ≤ 4,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
3UL,
|
||||||
|
4_000_000UL,
|
||||||
|
64);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"128x ≤4_000_000",
|
||||||
|
"128x sample admissible odd values ≤ 4,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
3UL,
|
||||||
|
4_000_000UL,
|
||||||
|
128);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"256x ≤4_000_000",
|
||||||
|
"256x sample admissible odd values ≤ 4,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
3UL,
|
||||||
|
4_000_000UL,
|
||||||
|
256);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"512x ≤4_000_000",
|
||||||
|
"512x sample admissible odd values ≤ 4,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
3UL,
|
||||||
|
4_000_000UL,
|
||||||
|
512);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"1024x ≤4_000_000",
|
||||||
|
"1024x sample admissible odd values ≤ 4,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
3UL,
|
||||||
|
4_000_000UL,
|
||||||
|
1024);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"2048x ≤4_000_000",
|
||||||
|
"2048x sample admissible odd values ≤ 4,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
3UL,
|
||||||
|
4_000_000UL,
|
||||||
|
2048);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"≤4_000_000",
|
||||||
|
"Sampled admissible odd values ≤ 4,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
3UL,
|
||||||
|
4_000_000UL,
|
||||||
|
4_096);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"≥16_000_000",
|
||||||
|
"Sampled admissible odd values ≥ 16,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
16_000_001UL,
|
||||||
|
16_500_000UL,
|
||||||
|
4_096);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"≥32_000_000",
|
||||||
|
"Sampled admissible odd values ≥ 32,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
32_000_001UL,
|
||||||
|
32_500_000UL,
|
||||||
|
4_096);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"≥64_000_000",
|
||||||
|
"Sampled admissible odd values ≥ 64,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
64_000_001UL,
|
||||||
|
64_500_000UL,
|
||||||
|
4_096);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"≥100_000_000",
|
||||||
|
"Sampled admissible odd values ≥ 100,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
100_000_001UL,
|
||||||
|
100_500_000UL,
|
||||||
|
4_096);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"≥138_000_000",
|
||||||
|
"Sampled admissible odd values ≥ 138,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
138_000_001UL,
|
||||||
|
138_500_000UL,
|
||||||
|
4_096);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"≥276_000_000",
|
||||||
|
"Sampled admissible odd values ≥ 276,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
276_000_001UL,
|
||||||
|
276_500_000UL,
|
||||||
|
4_096);
|
||||||
|
|
||||||
|
yield return PrimeBenchmarkCase.FromRange(
|
||||||
|
"≥552_000_000",
|
||||||
|
"Sampled admissible odd values ≥ 552,000,000 (k ≤ 5, not divisible by 5)",
|
||||||
|
552_000_001UL,
|
||||||
|
552_500_000UL,
|
||||||
|
4_096);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly record struct PrimeBenchmarkCase(string Name, string Description, ulong[] Candidates)
|
||||||
|
{
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrimeBenchmarkCase FromRange(
|
||||||
|
string name,
|
||||||
|
string description,
|
||||||
|
ulong startInclusive,
|
||||||
|
ulong endInclusive,
|
||||||
|
int maxCount)
|
||||||
|
{
|
||||||
|
ulong[] values = CollectRange(startInclusive, endInclusive, maxCount);
|
||||||
|
return new PrimeBenchmarkCase(name, description, values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ulong[] CollectRange(ulong startInclusive, ulong endInclusive, int maxCount)
|
||||||
|
{
|
||||||
|
maxCount = maxCount < 0 ? 0 : maxCount;
|
||||||
|
if (maxCount == 0)
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
int estimatedLength = (int)Math.Min((ulong)maxCount, endInclusive - startInclusive + 1UL);
|
||||||
|
ulong[] buffer = new ulong[estimatedLength];
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for (ulong value = startInclusive; value <= endInclusive && count < maxCount; value++)
|
||||||
|
{
|
||||||
|
if (!IsAdmissibleMersenneDivisorCandidate(value))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == buffer.Length)
|
||||||
|
{
|
||||||
|
Array.Resize(ref buffer, buffer.Length * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[count++] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == buffer.Length)
|
||||||
|
{
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong[] result = new ulong[count];
|
||||||
|
Array.Copy(buffer, result, count);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsAdmissibleMersenneDivisorCandidate(ulong value)
|
||||||
|
{
|
||||||
|
if ((value & 1UL) == 0UL || value % 5UL == 0UL || value <= 1UL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong qMinusOne = value - 1UL;
|
||||||
|
|
||||||
|
for (ulong k = 1; k <= 5; k++)
|
||||||
|
{
|
||||||
|
ulong denominator = k << 1;
|
||||||
|
if (qMinusOne % denominator != 0UL)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong p = qMinusOne / denominator;
|
||||||
|
if (p < 2UL)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Prime.Numbers.IsPrime(p))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,271 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace PerfectNumbers.Core;
|
||||||
|
|
||||||
|
[method: MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public sealed class BTree<TKey, TValue>()
|
||||||
|
{
|
||||||
|
private const int HashByteCount = sizeof(int);
|
||||||
|
private const int DefaultCapacity = 64;
|
||||||
|
private const int SmallBufferCapacity = 4;
|
||||||
|
private const int RootInlineThreshold = 2048;
|
||||||
|
private const int RootInlineInitialCapacity = 64;
|
||||||
|
private const int ShallowTreeThreshold = 1024;
|
||||||
|
private const int ShallowHashByteCount = 1;
|
||||||
|
private const int LeafInitialCapacity = 8;
|
||||||
|
private static readonly EqualityComparer<TKey> _defaultComparer = EqualityComparer<TKey>.Default;
|
||||||
|
|
||||||
|
private readonly TKey[] _smallKeys = new TKey[SmallBufferCapacity];
|
||||||
|
private readonly TValue[] _smallValues = new TValue[SmallBufferCapacity];
|
||||||
|
private int _smallCount;
|
||||||
|
|
||||||
|
private Node[] _nodes = [];
|
||||||
|
private int _nodeCount;
|
||||||
|
|
||||||
|
public int Count;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public void Add(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
TKey[] smallKeys = _smallKeys;
|
||||||
|
TValue[] smallValues = _smallValues;
|
||||||
|
int smallCount = _smallCount;
|
||||||
|
if (smallCount < SmallBufferCapacity)
|
||||||
|
{
|
||||||
|
smallKeys[smallCount] = key;
|
||||||
|
smallValues[smallCount] = value;
|
||||||
|
_smallCount = smallCount + 1;
|
||||||
|
Count++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureRoot();
|
||||||
|
|
||||||
|
Node[] nodes = _nodes;
|
||||||
|
if (smallCount > 0)
|
||||||
|
{
|
||||||
|
ref Node rootNode = ref nodes[0];
|
||||||
|
for (int i = 0; i < smallCount; i++)
|
||||||
|
{
|
||||||
|
rootNode.AddKeyValue(smallKeys[i], smallValues[i], RootInlineInitialCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
_smallCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Count < RootInlineThreshold)
|
||||||
|
{
|
||||||
|
ref Node root = ref nodes[0];
|
||||||
|
root.AddKeyValue(key, value, RootInlineInitialCapacity);
|
||||||
|
Count++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InsertIntoTree(GetHash(key), key, value, Count);
|
||||||
|
Count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public int CopyTo(Span<TValue> destination)
|
||||||
|
{
|
||||||
|
ArgumentOutOfRangeException.ThrowIfLessThan(destination.Length, Count);
|
||||||
|
|
||||||
|
var written = 0;
|
||||||
|
|
||||||
|
int smallCount = _smallCount;
|
||||||
|
TValue[] smallValues = _smallValues;
|
||||||
|
for (int i = 0; i < smallCount; i++)
|
||||||
|
{
|
||||||
|
destination[written++] = smallValues[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
int nodeCount = _nodeCount;
|
||||||
|
if (nodeCount == 0)
|
||||||
|
{
|
||||||
|
return written;
|
||||||
|
}
|
||||||
|
|
||||||
|
FixedCapacityStack<int> stack = new(nodeCount);
|
||||||
|
stack.Push(0);
|
||||||
|
|
||||||
|
Node[]? nodes = _nodes;
|
||||||
|
while (stack.Count > 0)
|
||||||
|
{
|
||||||
|
int index = stack.Pop();
|
||||||
|
ref readonly Node node = ref nodes[index];
|
||||||
|
written += node.CopyValues(destination[written..]);
|
||||||
|
|
||||||
|
for (int childIndex = 0; childIndex < Node.ChildCount; childIndex++)
|
||||||
|
{
|
||||||
|
index = node.GetChild(childIndex);
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
stack.Push(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return written;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
private static uint GetHash(TKey key)
|
||||||
|
{
|
||||||
|
return key is not null
|
||||||
|
? unchecked((uint)_defaultComparer.GetHashCode(key))
|
||||||
|
: 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
private void EnsureRoot()
|
||||||
|
{
|
||||||
|
if (_nodeCount != 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodes = new Node[DefaultCapacity];
|
||||||
|
_nodes[0] = new Node();
|
||||||
|
_nodeCount = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
|
||||||
|
private void InsertIntoTree(uint hash, TKey key, TValue value, int currentCount)
|
||||||
|
{
|
||||||
|
var nodes = _nodes;
|
||||||
|
var currentIndex = 0;
|
||||||
|
int depth = currentCount < ShallowTreeThreshold ? ShallowHashByteCount : HashByteCount;
|
||||||
|
|
||||||
|
int nodeCount = _nodeCount;
|
||||||
|
for (int i = 0; i < depth; i++)
|
||||||
|
{
|
||||||
|
ref Node node = ref nodes[currentIndex];
|
||||||
|
int bucket = (byte)(hash >> (i << 3)) >> 5;
|
||||||
|
int nextIndex = node.GetChild(bucket);
|
||||||
|
if (nextIndex == -1)
|
||||||
|
{
|
||||||
|
nextIndex = nodeCount;
|
||||||
|
nodes = EnsureNodeCapacity(nodes, nextIndex, nodeCount);
|
||||||
|
node = ref nodes[currentIndex];
|
||||||
|
nodes[nextIndex] = new Node();
|
||||||
|
node.SetChild(bucket, nextIndex);
|
||||||
|
nodeCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentIndex = nextIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
_nodes = nodes;
|
||||||
|
_nodeCount = nodeCount;
|
||||||
|
ref Node leaf = ref nodes[currentIndex];
|
||||||
|
leaf.AddKeyValue(key, value, LeafInitialCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
private static Node[] EnsureNodeCapacity(in Node[] nodes, int requiredIndex, int liveNodeCount)
|
||||||
|
{
|
||||||
|
int currentLength = nodes.Length;
|
||||||
|
if (requiredIndex < currentLength)
|
||||||
|
{
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
int targetLength = currentLength == 0 ? DefaultCapacity : currentLength;
|
||||||
|
while (targetLength <= requiredIndex)
|
||||||
|
{
|
||||||
|
targetLength <<= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node[] newNodes = new Node[targetLength];
|
||||||
|
if (liveNodeCount > currentLength)
|
||||||
|
{
|
||||||
|
liveNodeCount = currentLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.Copy(nodes, newNodes, liveNodeCount);
|
||||||
|
return newNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct Node
|
||||||
|
{
|
||||||
|
public const int ChildCount = 8;
|
||||||
|
|
||||||
|
private TKey[]? _keys;
|
||||||
|
private TValue[]? _values;
|
||||||
|
private int _entryCount;
|
||||||
|
private int[]? _children;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public readonly int GetChild(int slot)
|
||||||
|
{
|
||||||
|
int[]? children = _children;
|
||||||
|
return children == null ? -1 : children[slot];
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public void SetChild(int slot, int index)
|
||||||
|
{
|
||||||
|
int[]? children = _children;
|
||||||
|
if (children == null)
|
||||||
|
{
|
||||||
|
children = new int[ChildCount];
|
||||||
|
for (int i = 0; i < ChildCount; i++)
|
||||||
|
{
|
||||||
|
children[i] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_children = children;
|
||||||
|
}
|
||||||
|
|
||||||
|
children[slot] = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public void AddKeyValue(TKey key, TValue value, int desiredInitialCapacity)
|
||||||
|
{
|
||||||
|
TKey[]? keys = _keys;
|
||||||
|
TValue[]? values = _values;
|
||||||
|
int entryCount = _entryCount;
|
||||||
|
|
||||||
|
int newLength;
|
||||||
|
if (keys == null)
|
||||||
|
{
|
||||||
|
newLength = desiredInitialCapacity < 4 ? 4 : desiredInitialCapacity;
|
||||||
|
keys = new TKey[newLength];
|
||||||
|
values = new TValue[newLength];
|
||||||
|
_keys = keys;
|
||||||
|
_values = values;
|
||||||
|
}
|
||||||
|
else if (entryCount == keys.Length)
|
||||||
|
{
|
||||||
|
newLength = keys.Length << 1;
|
||||||
|
TKey[] newKeys = new TKey[newLength];
|
||||||
|
TValue[] newValues = new TValue[newLength];
|
||||||
|
new Span<TKey>(keys, 0, entryCount).CopyTo(newKeys);
|
||||||
|
new Span<TValue>(values, 0, entryCount).CopyTo(newValues);
|
||||||
|
keys = newKeys;
|
||||||
|
values = newValues;
|
||||||
|
_keys = newKeys;
|
||||||
|
_values = newValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
keys[entryCount] = key;
|
||||||
|
values![entryCount] = value;
|
||||||
|
_entryCount = entryCount + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
public readonly int CopyValues(Span<TValue> destination)
|
||||||
|
{
|
||||||
|
TValue[]? values = _values;
|
||||||
|
if (values == null)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int entryCount = _entryCount;
|
||||||
|
new Span<TValue>(values, 0, entryCount).CopyTo(destination);
|
||||||
|
return entryCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user