blob: c2acf776347c9c00ffe3158464147b633d3191ac [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
using System.Numerics;
using System.Runtime.CompilerServices;
namespace Apache.Fory;
internal static class TypeMapKey
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static ulong Get(Type type)
{
long value = type.TypeHandle.Value.ToInt64();
return unchecked((ulong)value);
}
}
/// <summary>
/// Open-addressed map keyed by <see cref="ulong"/>.
/// <para><see cref="ulong.MaxValue"/> is reserved as the empty-slot marker and must never be inserted.</para>
/// </summary>
internal sealed class UInt64Map<TValue>
{
private const ulong EmptyKey = ulong.MaxValue;
private const double DefaultLoadFactor = 0.5;
private const ulong GoldenRatio = 0x9E3779B97F4A7C15;
private struct Slot
{
public ulong Key;
public TValue Value;
}
private Slot[] _entries;
private int _tableCapacity;
private int _mask;
private int _shift;
private int _count;
private readonly double _loadFactor;
private int _growThreshold;
internal UInt64Map(int initialCapacity = 2, double loadFactor = DefaultLoadFactor)
{
_loadFactor = loadFactor;
int capacity = NextPowerOfTwo(Math.Max(initialCapacity, 2));
_entries = new Slot[capacity];
_tableCapacity = capacity;
_mask = capacity - 1;
_shift = 64 - BitOperations.TrailingZeroCount((uint)capacity);
_growThreshold = (int)(capacity * loadFactor);
ClearEntries(_entries, capacity);
}
internal int Count => _count;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool TryGetValue(ulong key, out TValue value)
{
int index = Place(key);
while (true)
{
ref Slot slot = ref _entries[index];
ulong slotKey = slot.Key;
if (slotKey == key)
{
value = slot.Value;
return true;
}
if (slotKey == EmptyKey)
{
value = default!;
return false;
}
index = (index + 1) & _mask;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Set(ulong key, TValue value)
{
if (_count >= _growThreshold)
{
Grow();
}
int index = FindSlotForInsert(key);
ref Slot slot = ref _entries[index];
if (slot.Key == EmptyKey)
{
slot.Key = key;
_count += 1;
}
slot.Value = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool Remove(ulong key)
{
int index = Place(key);
while (true)
{
ref Slot slot = ref _entries[index];
ulong slotKey = slot.Key;
if (slotKey == key)
{
RemoveEntry(index);
return true;
}
if (slotKey == EmptyKey)
{
return false;
}
index = (index + 1) & _mask;
}
}
internal void Clear()
{
if (_count == 0)
{
return;
}
ClearEntries(_entries, _tableCapacity);
_count = 0;
}
internal void ClearKeys()
{
if (_count == 0)
{
return;
}
ClearEntryKeys(_entries, _tableCapacity);
_count = 0;
}
internal void AddValuesTo(List<TValue> values)
{
for (int i = 0; i < _tableCapacity; i++)
{
if (_entries[i].Key != EmptyKey)
{
values.Add(_entries[i].Value);
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int FindSlotForInsert(ulong key)
{
int index = Place(key);
while (true)
{
ulong slotKey = _entries[index].Key;
if (slotKey == EmptyKey || slotKey == key)
{
return index;
}
index = (index + 1) & _mask;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int Place(ulong key)
{
return (int)((key * GoldenRatio) >> _shift);
}
private void Grow()
{
Slot[] oldEntries = _entries;
int oldCapacity = _tableCapacity;
int newCapacity = oldCapacity << 1;
Slot[] newEntries = new Slot[newCapacity];
ClearEntries(newEntries, newCapacity);
_entries = newEntries;
_tableCapacity = newCapacity;
_mask = newCapacity - 1;
_shift = 64 - BitOperations.TrailingZeroCount((uint)newCapacity);
_growThreshold = (int)(newCapacity * _loadFactor);
_count = 0;
for (int i = 0; i < oldCapacity; i++)
{
Slot oldSlot = oldEntries[i];
if (oldSlot.Key != EmptyKey)
{
int newIndex = FindSlotForInsert(oldSlot.Key);
_entries[newIndex] = oldSlot;
_count += 1;
}
}
}
private void RemoveEntry(int index)
{
int hole = index;
int cursor = (hole + 1) & _mask;
while (_entries[cursor].Key != EmptyKey)
{
int idealSlot = Place(_entries[cursor].Key);
if (ShouldMoveEntry(idealSlot, cursor, hole))
{
_entries[hole] = _entries[cursor];
hole = cursor;
}
cursor = (cursor + 1) & _mask;
}
_entries[hole].Key = EmptyKey;
_entries[hole].Value = default!;
_count -= 1;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool ShouldMoveEntry(int idealSlot, int candidateSlot, int emptySlot)
{
if (emptySlot <= candidateSlot)
{
return idealSlot <= emptySlot || idealSlot > candidateSlot;
}
return idealSlot <= emptySlot && idealSlot > candidateSlot;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ClearEntryKeys(Slot[] entries, int count)
{
for (int i = 0; i < count; i++)
{
entries[i].Key = EmptyKey;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ClearEntries(Slot[] entries, int count)
{
for (int i = 0; i < count; i++)
{
entries[i].Key = EmptyKey;
entries[i].Value = default!;
}
}
private static int NextPowerOfTwo(int value)
{
if (value <= 1)
{
return 1;
}
return 1 << (32 - BitOperations.LeadingZeroCount((uint)(value - 1)));
}
}