2019-08-23 11:56:54 -07:00
|
|
|
// This file contains portions of code released by Microsoft under the MIT license as part
|
|
|
|
// of an open-sourcing initiative in 2014 of the C# core libraries.
|
|
|
|
// The original source was submitted to https://github.com/Microsoft/referencesource
|
|
|
|
|
|
|
|
#if PARANOID
|
|
|
|
#define VERSION_DICTIONARY
|
|
|
|
#endif
|
|
|
|
|
2023-05-03 15:09:20 -03:00
|
|
|
using System;
|
|
|
|
using System.Collections;
|
|
|
|
using System.Diagnostics;
|
|
|
|
using System.Diagnostics.Contracts;
|
|
|
|
using System.Threading;
|
|
|
|
|
2020-04-29 06:40:03 -07:00
|
|
|
namespace System.Collections
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
2023-05-03 15:09:20 -03:00
|
|
|
interface IDictionary
|
|
|
|
{
|
|
|
|
Variant this[Variant key]
|
|
|
|
{
|
|
|
|
get;
|
|
|
|
set;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ContainsKey(Variant key);
|
|
|
|
bool ContainsValue(Variant value);
|
|
|
|
void Add(Variant key, Variant value);
|
|
|
|
void Clear();
|
|
|
|
void Remove(Variant key);
|
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
|
2020-05-01 16:29:12 -07:00
|
|
|
public class Dictionary<TKey, TValue> :
|
2023-05-03 15:09:20 -03:00
|
|
|
IDictionary,
|
2020-05-01 16:29:12 -07:00
|
|
|
ICollection<(TKey key, TValue value)>,
|
|
|
|
IEnumerable<(TKey key, TValue value)>,
|
|
|
|
IRefEnumerable<(TKey key, TValue* valueRef)> where TKey : IHashable
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
2020-05-01 09:12:50 -07:00
|
|
|
typealias KeyValuePair=(TKey key, TValue value);
|
2020-05-01 16:29:12 -07:00
|
|
|
typealias KeyRefValuePair=(TKey key, TValue* valueRef);
|
2020-05-01 14:42:54 +08:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
private struct Entry
|
|
|
|
{
|
|
|
|
public TKey mKey; // Key of entry
|
|
|
|
public TValue mValue; // Value of entry
|
2021-08-27 10:06:32 -07:00
|
|
|
public int_cosize mHashCode; // some bits of hash code, -1 if unused
|
2019-08-23 11:56:54 -07:00
|
|
|
public int_cosize mNext; // Index of next entry, -1 if last
|
|
|
|
}
|
|
|
|
|
2020-05-11 10:17:45 -07:00
|
|
|
int_cosize* mBuckets;
|
|
|
|
Entry* mEntries;
|
2019-11-17 09:25:50 -08:00
|
|
|
int_cosize mAllocSize;
|
|
|
|
int_cosize mCount;
|
2021-08-27 10:06:32 -07:00
|
|
|
int_cosize mFreeList;
|
2019-11-17 09:25:50 -08:00
|
|
|
int_cosize mFreeCount;
|
2019-08-23 11:56:54 -07:00
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
private int32 mVersion;
|
|
|
|
const String cVersionError = "Dictionary changed during enumeration";
|
|
|
|
#endif
|
|
|
|
|
|
|
|
public this(): this(0) { }
|
|
|
|
|
|
|
|
public this(int_cosize capacity)
|
|
|
|
{
|
|
|
|
//if (capacity < 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity);
|
|
|
|
if (capacity > 0) Initialize(capacity);
|
|
|
|
//TODO: this.comparer = comparer ?? EqualityComparer<TKey>.Default;
|
2022-01-01 21:53:17 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
public this(IEnumerator<KeyValuePair> enumerator)
|
|
|
|
{
|
|
|
|
for (var kv in enumerator)
|
|
|
|
this[kv.key] = kv.value;
|
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
|
2020-05-11 10:17:45 -07:00
|
|
|
public ~this()
|
|
|
|
{
|
|
|
|
if (mEntries != null)
|
|
|
|
{
|
|
|
|
var entries = mEntries;
|
|
|
|
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
|
|
|
// To avoid scanning items being deleted
|
|
|
|
mEntries = null;
|
|
|
|
Interlocked.Fence();
|
|
|
|
#endif
|
|
|
|
Free(entries);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
public int Count
|
|
|
|
{
|
|
|
|
get { return mCount - mFreeCount; }
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool IsEmpty
|
|
|
|
{
|
|
|
|
get { return mCount - mFreeCount == 0; }
|
|
|
|
}
|
|
|
|
|
|
|
|
public ValueEnumerator Values
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
//Contract.Ensures(Contract.Result<ValueCollection>() != null);
|
|
|
|
return ValueEnumerator(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public KeyEnumerator Keys
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
//Contract.Ensures(Contract.Result<ValueCollection>() != null);
|
|
|
|
return KeyEnumerator(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*ICollection<TValue> IDictionary<TKey, TValue>.Values
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
if (values == null) values = new ValueCollection(this);
|
|
|
|
return values;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
if (values == null) values = new ValueCollection(this);
|
|
|
|
return values;
|
|
|
|
}
|
|
|
|
}*/
|
|
|
|
|
|
|
|
public ref TValue this[TKey key]
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
int_cosize i = (int_cosize)FindEntry(key);
|
|
|
|
if (i >= 0) return ref mEntries[i].mValue;
|
|
|
|
Runtime.FatalError("Key not found");
|
|
|
|
}
|
|
|
|
set
|
|
|
|
{
|
|
|
|
Insert(key, value, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-03 15:09:20 -03:00
|
|
|
|
|
|
|
Variant IDictionary.this[Variant key]
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return [Unbound]Variant.Create(this[key.Get<TKey>()]);
|
|
|
|
}
|
|
|
|
set
|
|
|
|
{
|
|
|
|
this[key.Get<TKey>()] = value.Get<TValue>();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
public void Add(TKey key, TValue value)
|
|
|
|
{
|
|
|
|
Insert(key, value, true);
|
|
|
|
}
|
2020-05-01 14:42:54 +08:00
|
|
|
|
2023-05-03 15:09:20 -03:00
|
|
|
void IDictionary.Add(Variant key, Variant value)
|
|
|
|
{
|
|
|
|
Add(key.Get<TKey>(), value.Get<TValue>());
|
|
|
|
}
|
|
|
|
|
2020-05-01 14:42:54 +08:00
|
|
|
public void Add(KeyValuePair kvPair)
|
2020-04-28 19:45:31 +08:00
|
|
|
{
|
2020-05-01 09:12:50 -07:00
|
|
|
Insert(kvPair.key, kvPair.value, true);
|
2020-04-28 19:45:31 +08:00
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
|
|
|
|
public bool TryAdd(TKey key, TValue value)
|
|
|
|
{
|
|
|
|
TKey* keyPtr;
|
|
|
|
TValue* valuePtr;
|
2020-05-11 10:17:45 -07:00
|
|
|
bool inserted = Insert(key, false, out keyPtr, out valuePtr, null);
|
2019-08-23 11:56:54 -07:00
|
|
|
if (!inserted)
|
|
|
|
return false;
|
|
|
|
*keyPtr = key;
|
|
|
|
*valuePtr = value;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool TryAdd(TKey key, out TKey* keyPtr, out TValue* valuePtr)
|
|
|
|
{
|
2020-05-11 10:17:45 -07:00
|
|
|
return Insert(key, false, out keyPtr, out valuePtr, null);
|
|
|
|
}
|
|
|
|
|
2020-08-10 14:45:11 -07:00
|
|
|
public bool TryAddAlt<TAltKey>(TAltKey key, out TKey* keyPtr, out TValue* valuePtr) where TAltKey : IHashable where bool : operator TKey == TAltKey
|
2020-06-19 06:41:50 -07:00
|
|
|
{
|
|
|
|
return InsertAlt(key, false, out keyPtr, out valuePtr, null);
|
|
|
|
}
|
|
|
|
|
2020-05-11 10:17:45 -07:00
|
|
|
protected virtual (Entry*, int_cosize*) Alloc(int size)
|
|
|
|
{
|
|
|
|
int byteSize = size * (strideof(Entry) + sizeof(int_cosize));
|
|
|
|
uint8* allocPtr = new uint8[byteSize]*;
|
|
|
|
return ((Entry*)allocPtr, (int_cosize*)(allocPtr + size * strideof(Entry)));
|
|
|
|
}
|
|
|
|
|
|
|
|
protected virtual void Free(Entry* entryPtr)
|
|
|
|
{
|
|
|
|
delete (void*)entryPtr;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void GCMarkMembers()
|
|
|
|
{
|
|
|
|
if (mEntries == null)
|
|
|
|
return;
|
|
|
|
let type = typeof(Entry);
|
|
|
|
if ((type.[Friend]mTypeFlags & .WantsMark) == 0)
|
|
|
|
return;
|
|
|
|
for (int i < mCount)
|
|
|
|
{
|
2020-08-04 10:31:13 -07:00
|
|
|
mEntries[i].[Friend]GCMarkMembers();
|
2020-05-11 10:17:45 -07:00
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
public enum AddResult
|
|
|
|
{
|
|
|
|
case Added(TKey* keyPtr, TValue* valuePtr);
|
|
|
|
case Exists(TKey* keyPtr, TValue* valuePtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
public AddResult TryAdd(TKey key)
|
|
|
|
{
|
|
|
|
TKey* keyPtr;
|
2020-05-11 10:17:45 -07:00
|
|
|
TValue* valuePtr;
|
|
|
|
if (Insert(key, false, out keyPtr, out valuePtr, null))
|
2019-08-23 11:56:54 -07:00
|
|
|
return .Added(keyPtr, valuePtr);
|
|
|
|
return .Exists(keyPtr, valuePtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> keyValuePair)
|
|
|
|
{
|
|
|
|
Add(keyValuePair.Key, keyValuePair.Value);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> keyValuePair)
|
|
|
|
{
|
|
|
|
int i = FindEntry(keyValuePair.Key);
|
|
|
|
if (i >= 0 && EqualityComparer<TValue>.Default.Equals(entries[i].value, keyValuePair.Value))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> keyValuePair)
|
|
|
|
{
|
|
|
|
int i = FindEntry(keyValuePair.Key);
|
|
|
|
if (i >= 0 && EqualityComparer<TValue>.Default.Equals(entries[i].value, keyValuePair.Value))
|
|
|
|
{
|
|
|
|
Remove(keyValuePair.Key);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}*/
|
|
|
|
|
|
|
|
public Result<TValue> GetValue(TKey key)
|
|
|
|
{
|
|
|
|
int_cosize i = (int_cosize)FindEntry(key);
|
|
|
|
if (i >= 0) return mEntries[i].mValue;
|
|
|
|
return .Err;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Clear()
|
|
|
|
{
|
|
|
|
if (mCount > 0)
|
|
|
|
{
|
2019-11-17 09:25:50 -08:00
|
|
|
for (int_cosize i = 0; i < mAllocSize; i++) mBuckets[i] = -1;
|
2019-08-23 11:56:54 -07:00
|
|
|
//for (int_cosize i = 0; i < mCount; i++)
|
|
|
|
//mEntries[i] = default(Entry);
|
|
|
|
//Array.Clear(entries, 0, count);
|
|
|
|
mFreeList = -1;
|
|
|
|
mCount = 0;
|
|
|
|
mFreeCount = 0;
|
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
mVersion++;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool ContainsKey(TKey key)
|
|
|
|
{
|
|
|
|
return FindEntry(key) >= 0;
|
|
|
|
}
|
|
|
|
|
2023-05-03 15:09:20 -03:00
|
|
|
bool IDictionary.ContainsKey(Variant key)
|
|
|
|
{
|
|
|
|
return ContainsKey(key.Get<TKey>());
|
|
|
|
}
|
|
|
|
|
2020-08-10 14:45:11 -07:00
|
|
|
public bool ContainsKeyAlt<TAltKey>(TAltKey key) where TAltKey : IHashable where bool : operator TKey == TAltKey
|
2020-06-19 06:41:50 -07:00
|
|
|
{
|
|
|
|
return FindEntryAlt(key) >= 0;
|
|
|
|
}
|
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
public bool ContainsValue(TValue value)
|
|
|
|
{
|
2021-05-20 06:33:07 -04:00
|
|
|
for (int_cosize i = 0; i < mCount; i++)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
2021-05-20 16:25:26 -04:00
|
|
|
if (mEntries[i].mHashCode >= 0 && mEntries[i].mValue == value) return true;
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2023-05-03 15:09:20 -03:00
|
|
|
|
|
|
|
bool IDictionary.ContainsValue(Variant value)
|
|
|
|
{
|
|
|
|
return ContainsValue(value.Get<TValue>());
|
|
|
|
}
|
2020-04-28 19:45:31 +08:00
|
|
|
|
2020-05-01 14:42:54 +08:00
|
|
|
public bool Contains(KeyValuePair kvPair)
|
2020-04-28 19:45:31 +08:00
|
|
|
{
|
|
|
|
TValue value;
|
2020-05-01 09:12:50 -07:00
|
|
|
if (TryGetValue(kvPair.key, out value))
|
|
|
|
{
|
|
|
|
return value == kvPair.value;
|
|
|
|
}
|
|
|
|
else
|
2020-04-28 19:45:31 +08:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2021-10-27 23:37:09 -03:00
|
|
|
|
|
|
|
public bool ContainsAlt<TAltKey>((TAltKey key, TValue value) kvPair) where TAltKey : IHashable where bool : operator TKey == TAltKey
|
|
|
|
{
|
|
|
|
TValue value;
|
|
|
|
if (TryGetValueAlt(kvPair.key, out value))
|
|
|
|
{
|
|
|
|
return value == kvPair.value;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2020-04-28 19:45:31 +08:00
|
|
|
|
2021-01-05 05:56:11 -08:00
|
|
|
public void CopyTo(Span<KeyValuePair> kvPair)
|
2020-04-28 19:45:31 +08:00
|
|
|
{
|
2022-02-19 21:45:45 -03:00
|
|
|
Debug.Assert(kvPair.Length >= Count);
|
2021-01-05 05:56:11 -08:00
|
|
|
int idx = 0;
|
|
|
|
for (var kv in this)
|
2020-04-28 19:45:31 +08:00
|
|
|
{
|
2021-01-05 05:56:11 -08:00
|
|
|
kvPair[idx] = kv;
|
|
|
|
++idx;
|
2020-04-28 19:45:31 +08:00
|
|
|
}
|
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
|
|
|
|
public Enumerator GetEnumerator()
|
|
|
|
{
|
2020-03-09 06:34:16 -07:00
|
|
|
return Enumerator(this, Enumerator.[Friend]KeyValuePair);
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
2021-08-27 10:06:32 -07:00
|
|
|
static int_cosize GetKeyHash(int hashCode)
|
|
|
|
{
|
|
|
|
if (sizeof(int) == 4)
|
|
|
|
return (int32)hashCode & 0x7FFFFFFF;
|
2021-12-27 12:33:32 -05:00
|
|
|
#unwarn
|
2021-08-27 10:06:32 -07:00
|
|
|
if (sizeof(int_cosize) == 8)
|
|
|
|
return (int_cosize)(hashCode & 0x7FFFFFFF'FFFFFFFFL);
|
2021-08-27 10:47:29 -07:00
|
|
|
return ((int32)hashCode ^ (int32)((int64)hashCode >> 33)) & 0x7FFFFFFF;
|
2021-08-27 10:06:32 -07:00
|
|
|
}
|
|
|
|
|
2019-11-17 09:25:50 -08:00
|
|
|
[DisableObjectAccessChecks]
|
2019-08-23 11:56:54 -07:00
|
|
|
private int FindEntry(TKey key)
|
|
|
|
{
|
|
|
|
if (mBuckets != null)
|
|
|
|
{
|
2021-08-27 10:06:32 -07:00
|
|
|
int_cosize hashCode = GetKeyHash(key.GetHashCode());
|
2020-06-19 06:41:50 -07:00
|
|
|
for (int i = mBuckets[hashCode % mAllocSize]; i >= 0; i = mEntries[i].mNext)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
|
|
|
if (mEntries[i].mHashCode == hashCode && (mEntries[i].mKey == key)) return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-11-17 09:25:50 -08:00
|
|
|
public bool CheckEq(TKey key, TKey key2)
|
|
|
|
{
|
|
|
|
return key == key2;
|
|
|
|
}
|
|
|
|
|
2020-08-10 14:45:11 -07:00
|
|
|
private int FindEntryAlt<TAltKey>(TAltKey key) where TAltKey : IHashable where bool : operator TKey == TAltKey
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
|
|
|
if (mBuckets != null)
|
|
|
|
{
|
2021-08-27 10:06:32 -07:00
|
|
|
int_cosize hashCode = GetKeyHash(key.GetHashCode());
|
2019-11-17 09:25:50 -08:00
|
|
|
for (int_cosize i = mBuckets[hashCode % mAllocSize]; i >= 0; i = mEntries[i].mNext)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
|
|
|
if (mEntries[i].mHashCode == hashCode && (mEntries[i].mKey == key)) return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void Initialize(int capacity)
|
|
|
|
{
|
|
|
|
int_cosize size = GetPrimeish((int_cosize)capacity);
|
2020-05-11 10:17:45 -07:00
|
|
|
(mEntries, mBuckets) = Alloc(size);
|
2019-11-17 09:25:50 -08:00
|
|
|
for (int_cosize i < (int_cosize)size) mBuckets[i] = -1;
|
|
|
|
mAllocSize = size;
|
2019-08-23 11:56:54 -07:00
|
|
|
mFreeList = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void Insert(TKey key, TValue value, bool add)
|
|
|
|
{
|
|
|
|
if (mBuckets == null) Initialize(0);
|
2021-08-27 10:06:32 -07:00
|
|
|
int_cosize hashCode = GetKeyHash(key.GetHashCode());
|
|
|
|
int targetBucket = hashCode % mAllocSize;
|
2019-08-23 11:56:54 -07:00
|
|
|
|
2021-08-27 10:06:32 -07:00
|
|
|
for (int i = mBuckets[targetBucket]; i >= 0; i = mEntries[i].mNext)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
|
|
|
if (mEntries[i].mHashCode == hashCode && (mEntries[i].mKey == key))
|
|
|
|
{
|
|
|
|
if (add)
|
|
|
|
{
|
|
|
|
Runtime.FatalError("Adding duplicate");
|
|
|
|
}
|
|
|
|
mEntries[i].mValue = value;
|
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
mVersion++;
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int_cosize index;
|
2020-05-11 10:17:45 -07:00
|
|
|
Entry* oldData = null;
|
2019-08-23 11:56:54 -07:00
|
|
|
if (mFreeCount > 0)
|
|
|
|
{
|
|
|
|
index = mFreeList;
|
|
|
|
mFreeList = mEntries[index].mNext;
|
|
|
|
mFreeCount--;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-11-17 09:25:50 -08:00
|
|
|
if (mCount == mAllocSize)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
2020-05-11 10:17:45 -07:00
|
|
|
oldData = Resize(false);
|
2019-11-17 09:25:50 -08:00
|
|
|
targetBucket = hashCode % (int_cosize)mAllocSize;
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
index = mCount;
|
|
|
|
mCount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
mEntries[index].mHashCode = hashCode;
|
|
|
|
mEntries[index].mNext = mBuckets[targetBucket];
|
|
|
|
mEntries[index].mKey = key;
|
|
|
|
mEntries[index].mValue = value;
|
|
|
|
mBuckets[targetBucket] = index;
|
2020-05-11 10:17:45 -07:00
|
|
|
if (oldData != null)
|
|
|
|
Free(oldData);
|
2019-08-23 11:56:54 -07:00
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
mVersion++;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2020-05-11 10:17:45 -07:00
|
|
|
private bool Insert(TKey key, bool add, out TKey* keyPtr, out TValue* valuePtr, Entry** outOldData)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
|
|
|
if (mBuckets == null) Initialize(0);
|
2021-08-27 10:06:32 -07:00
|
|
|
int_cosize hashCode = GetKeyHash(key.GetHashCode());
|
2019-11-17 09:25:50 -08:00
|
|
|
int_cosize targetBucket = hashCode % (int_cosize)mAllocSize;
|
2019-08-23 11:56:54 -07:00
|
|
|
for (int_cosize i = mBuckets[targetBucket]; i >= 0; i = mEntries[i].mNext)
|
|
|
|
{
|
|
|
|
if (mEntries[i].mHashCode == hashCode && (mEntries[i].mKey == key))
|
|
|
|
{
|
|
|
|
if (add)
|
|
|
|
{
|
|
|
|
Runtime.FatalError("Adding duplicate entry");
|
|
|
|
}
|
|
|
|
keyPtr = &mEntries[i].mKey;
|
|
|
|
valuePtr = &mEntries[i].mValue;
|
2020-05-11 10:17:45 -07:00
|
|
|
if (outOldData != null)
|
|
|
|
*outOldData = null;
|
2019-08-23 11:56:54 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int_cosize index;
|
2020-05-11 10:17:45 -07:00
|
|
|
Entry* oldData = null;
|
2019-08-23 11:56:54 -07:00
|
|
|
if (mFreeCount > 0)
|
|
|
|
{
|
|
|
|
index = mFreeList;
|
|
|
|
mFreeList = mEntries[index].mNext;
|
|
|
|
mFreeCount--;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-11-17 09:25:50 -08:00
|
|
|
if (mCount == mAllocSize)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
2020-05-11 10:17:45 -07:00
|
|
|
oldData = Resize(false);
|
2019-11-17 09:25:50 -08:00
|
|
|
targetBucket = hashCode % (int_cosize)mAllocSize;
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
index = mCount;
|
|
|
|
mCount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
mEntries[index].mHashCode = hashCode;
|
|
|
|
mEntries[index].mNext = mBuckets[targetBucket];
|
|
|
|
mEntries[index].mKey = key;
|
|
|
|
mBuckets[targetBucket] = index;
|
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
mVersion++;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
keyPtr = &mEntries[index].mKey;
|
|
|
|
valuePtr = &mEntries[index].mValue;
|
2020-05-11 10:17:45 -07:00
|
|
|
if (outOldData != null)
|
|
|
|
*outOldData = oldData;
|
|
|
|
else
|
|
|
|
Free(oldData);
|
2019-08-23 11:56:54 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-08-10 14:45:11 -07:00
|
|
|
private bool InsertAlt<TAltKey>(TAltKey key, bool add, out TKey* keyPtr, out TValue* valuePtr, Entry** outOldData) where TAltKey : IHashable where bool : operator TKey == TAltKey
|
2020-06-19 06:41:50 -07:00
|
|
|
{
|
|
|
|
if (mBuckets == null) Initialize(0);
|
2021-08-27 10:06:32 -07:00
|
|
|
int_cosize hashCode = GetKeyHash(key.GetHashCode());
|
|
|
|
int targetBucket = hashCode % (int_cosize)mAllocSize;
|
|
|
|
for (int i = mBuckets[targetBucket]; i >= 0; i = mEntries[i].mNext)
|
2020-06-19 06:41:50 -07:00
|
|
|
{
|
|
|
|
if (mEntries[i].mHashCode == hashCode && (mEntries[i].mKey == key))
|
|
|
|
{
|
|
|
|
if (add)
|
|
|
|
{
|
|
|
|
Runtime.FatalError("Adding duplicate entry");
|
|
|
|
}
|
|
|
|
keyPtr = &mEntries[i].mKey;
|
|
|
|
valuePtr = &mEntries[i].mValue;
|
|
|
|
if (outOldData != null)
|
|
|
|
*outOldData = null;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int_cosize index;
|
|
|
|
Entry* oldData = null;
|
|
|
|
if (mFreeCount > 0)
|
|
|
|
{
|
|
|
|
index = mFreeList;
|
|
|
|
mFreeList = mEntries[index].mNext;
|
|
|
|
mFreeCount--;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (mCount == mAllocSize)
|
|
|
|
{
|
|
|
|
oldData = Resize(false);
|
|
|
|
targetBucket = hashCode % (int_cosize)mAllocSize;
|
|
|
|
}
|
|
|
|
index = mCount;
|
|
|
|
mCount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
mEntries[index].mHashCode = hashCode;
|
|
|
|
mEntries[index].mNext = mBuckets[targetBucket];
|
|
|
|
mBuckets[targetBucket] = index;
|
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
mVersion++;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
keyPtr = &mEntries[index].mKey;
|
|
|
|
valuePtr = &mEntries[index].mValue;
|
|
|
|
if (outOldData != null)
|
|
|
|
*outOldData = oldData;
|
|
|
|
else
|
|
|
|
Free(oldData);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
// Close to prime but faster to compute
|
|
|
|
int_cosize GetPrimeish(int_cosize min)
|
|
|
|
{
|
|
|
|
// This is a minimal effort to help address-aligned data
|
|
|
|
return (min | 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int_cosize ExpandSize(int_cosize oldSize)
|
|
|
|
{
|
|
|
|
int_cosize newSize = 2 * oldSize;
|
|
|
|
return GetPrimeish(newSize);
|
|
|
|
}
|
|
|
|
|
2020-05-11 10:17:45 -07:00
|
|
|
private Entry* Resize(bool autoFree)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
2020-05-11 10:17:45 -07:00
|
|
|
return Resize(ExpandSize(mCount), false, autoFree);
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
2020-05-11 10:17:45 -07:00
|
|
|
private Entry* Resize(int newSize, bool forceNewHashCodes, bool autoFree)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
2019-11-17 09:25:50 -08:00
|
|
|
Contract.Assert(newSize >= mAllocSize);
|
2020-05-11 10:17:45 -07:00
|
|
|
(var newEntries, var newBuckets) = Alloc(newSize);
|
2019-11-17 09:25:50 -08:00
|
|
|
for (int_cosize i = 0; i < newSize; i++) newBuckets[i] = -1;
|
2019-11-21 08:23:18 -08:00
|
|
|
Internal.MemCpy(newEntries, mEntries, mCount * strideof(Entry), alignof(Entry));
|
2019-11-17 09:25:50 -08:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
if (forceNewHashCodes)
|
|
|
|
{
|
|
|
|
for (int_cosize i = 0; i < mCount; i++)
|
|
|
|
{
|
|
|
|
if (newEntries[i].mHashCode != -1)
|
|
|
|
{
|
2021-08-27 10:06:32 -07:00
|
|
|
newEntries[i].mHashCode = GetKeyHash(newEntries[i].mKey.GetHashCode());
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (int_cosize i = 0; i < mCount; i++)
|
|
|
|
{
|
|
|
|
if (newEntries[i].mHashCode >= 0)
|
|
|
|
{
|
|
|
|
int_cosize bucket = (int_cosize)(newEntries[i].mHashCode % newSize);
|
|
|
|
newEntries[i].mNext = newBuckets[bucket];
|
|
|
|
newBuckets[bucket] = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-11 10:17:45 -07:00
|
|
|
let oldPtr = mEntries;
|
2019-08-23 11:56:54 -07:00
|
|
|
mBuckets = newBuckets;
|
|
|
|
mEntries = newEntries;
|
2019-11-17 09:25:50 -08:00
|
|
|
mAllocSize = (int_cosize)newSize;
|
2020-05-11 10:17:45 -07:00
|
|
|
|
|
|
|
if (autoFree)
|
|
|
|
{
|
|
|
|
Free(oldPtr);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return oldPtr;
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
2021-12-14 12:21:53 -03:00
|
|
|
private bool RemoveEntry(int32 hashCode, int_cosize index)
|
|
|
|
{
|
|
|
|
if (mBuckets != null)
|
|
|
|
{
|
|
|
|
int bucket = hashCode % (int_cosize)mAllocSize;
|
|
|
|
int lastIndex = -1;
|
|
|
|
|
|
|
|
for (int_cosize i = mBuckets[bucket]; i >= 0; lastIndex = i, i = mEntries[i].mNext)
|
|
|
|
{
|
|
|
|
if (i == index)
|
|
|
|
{
|
|
|
|
if (lastIndex < 0)
|
|
|
|
{
|
|
|
|
mBuckets[bucket] = mEntries[index].mNext;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mEntries[lastIndex].mNext = mEntries[index].mNext;
|
|
|
|
}
|
|
|
|
mEntries[index].mHashCode = -1;
|
|
|
|
mEntries[index].mNext = mFreeList;
|
|
|
|
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
|
|
|
mEntries[index].mKey = default;
|
|
|
|
mEntries[index].mValue = default;
|
|
|
|
#endif
|
|
|
|
mFreeList = index;
|
|
|
|
mFreeCount++;
|
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
mVersion++;
|
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
public bool Remove(TKey key)
|
|
|
|
{
|
2020-06-19 06:41:50 -07:00
|
|
|
if (mBuckets != null)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
2021-08-27 10:06:32 -07:00
|
|
|
int_cosize hashCode = GetKeyHash(key.GetHashCode());
|
2020-06-19 06:41:50 -07:00
|
|
|
int bucket = hashCode % (int_cosize)mAllocSize;
|
|
|
|
int last = -1;
|
|
|
|
for (int_cosize i = mBuckets[bucket]; i >= 0; last = i,i = mEntries[i].mNext)
|
|
|
|
{
|
|
|
|
if ((mEntries[i].mHashCode == hashCode) && (mEntries[i].mKey == key))
|
|
|
|
{
|
|
|
|
if (last < 0)
|
|
|
|
{
|
|
|
|
mBuckets[bucket] = mEntries[i].mNext;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mEntries[last].mNext = mEntries[i].mNext;
|
|
|
|
}
|
|
|
|
mEntries[i].mHashCode = -1;
|
|
|
|
mEntries[i].mNext = mFreeList;
|
|
|
|
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
|
|
|
mEntries[i].mKey = default;
|
|
|
|
mEntries[i].mValue = default;
|
|
|
|
#endif
|
|
|
|
mFreeList = i;
|
|
|
|
mFreeCount++;
|
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
mVersion++;
|
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
2020-06-19 06:41:50 -07:00
|
|
|
return false;
|
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
|
2023-05-03 15:09:20 -03:00
|
|
|
void IDictionary.Remove(Variant key)
|
|
|
|
{
|
|
|
|
Remove(key.Get<TKey>());
|
|
|
|
}
|
|
|
|
|
2020-08-10 14:45:11 -07:00
|
|
|
public bool RemoveAlt<TAltKey>(TAltKey key) where TAltKey : IHashable where bool : operator TKey == TAltKey
|
2020-06-19 06:41:50 -07:00
|
|
|
{
|
2019-08-23 11:56:54 -07:00
|
|
|
if (mBuckets != null)
|
|
|
|
{
|
2021-08-27 10:06:32 -07:00
|
|
|
int_cosize hashCode = GetKeyHash(key.GetHashCode());
|
2020-06-19 06:41:50 -07:00
|
|
|
int bucket = hashCode % (int_cosize)mAllocSize;
|
|
|
|
int last = -1;
|
2019-08-23 11:56:54 -07:00
|
|
|
for (int_cosize i = mBuckets[bucket]; i >= 0; last = i,i = mEntries[i].mNext)
|
|
|
|
{
|
|
|
|
if ((mEntries[i].mHashCode == hashCode) && (mEntries[i].mKey == key))
|
|
|
|
{
|
|
|
|
if (last < 0)
|
|
|
|
{
|
|
|
|
mBuckets[bucket] = mEntries[i].mNext;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mEntries[last].mNext = mEntries[i].mNext;
|
|
|
|
}
|
|
|
|
mEntries[i].mHashCode = -1;
|
|
|
|
mEntries[i].mNext = mFreeList;
|
|
|
|
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
|
|
|
mEntries[i].mKey = default;
|
|
|
|
mEntries[i].mValue = default;
|
|
|
|
#endif
|
|
|
|
mFreeList = i;
|
|
|
|
mFreeCount++;
|
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
mVersion++;
|
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2020-04-28 19:45:31 +08:00
|
|
|
|
|
|
|
[Inline]
|
2020-05-01 14:42:54 +08:00
|
|
|
public bool Remove(KeyValuePair kvPair)
|
2020-04-28 19:45:31 +08:00
|
|
|
{
|
2020-05-01 14:42:54 +08:00
|
|
|
return Remove(kvPair.key);
|
2020-04-28 19:45:31 +08:00
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
|
|
|
|
public Result<(TKey key, TValue value)> GetAndRemove(TKey key)
|
|
|
|
{
|
|
|
|
if (mBuckets != null)
|
|
|
|
{
|
|
|
|
|
2021-08-27 10:06:32 -07:00
|
|
|
int_cosize hashCode = GetKeyHash(key.GetHashCode());
|
|
|
|
int bucket = hashCode % (int_cosize)mAllocSize;
|
2019-08-23 11:56:54 -07:00
|
|
|
int_cosize last = -1;
|
|
|
|
for (int_cosize i = mBuckets[bucket]; i >= 0; last = i,i = mEntries[i].mNext)
|
|
|
|
{
|
|
|
|
if ((mEntries[i].mHashCode == hashCode) && (mEntries[i].mKey == key))
|
|
|
|
{
|
|
|
|
if (last < 0)
|
|
|
|
{
|
|
|
|
mBuckets[bucket] = mEntries[i].mNext;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mEntries[last].mNext = mEntries[i].mNext;
|
|
|
|
}
|
|
|
|
TKey entryKey = mEntries[i].mKey;
|
|
|
|
TValue result = mEntries[i].mValue;
|
|
|
|
|
|
|
|
mEntries[i].mHashCode = -1;
|
|
|
|
mEntries[i].mNext = mFreeList;
|
|
|
|
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
|
|
|
mEntries[i].mKey = default(TKey);
|
|
|
|
mEntries[i].mValue = default(TValue);
|
|
|
|
#endif
|
|
|
|
mFreeList = i;
|
|
|
|
mFreeCount++;
|
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
mVersion++;
|
|
|
|
#endif
|
|
|
|
return .Ok((entryKey, result));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return .Err;
|
|
|
|
}
|
|
|
|
|
2020-08-10 14:45:11 -07:00
|
|
|
public Result<(TKey key, TValue value)> GetAndRemoveAlt<TAltKey>(TAltKey key) where TAltKey : IHashable where bool : operator TKey == TAltKey
|
2020-06-19 06:41:50 -07:00
|
|
|
{
|
|
|
|
if (mBuckets != null)
|
|
|
|
{
|
|
|
|
|
2021-08-27 10:06:32 -07:00
|
|
|
int_cosize hashCode = GetKeyHash(key.GetHashCode());
|
|
|
|
int bucket = hashCode % (int_cosize)mAllocSize;
|
2020-06-19 06:41:50 -07:00
|
|
|
int_cosize last = -1;
|
|
|
|
for (int_cosize i = mBuckets[bucket]; i >= 0; last = i,i = mEntries[i].mNext)
|
|
|
|
{
|
|
|
|
if ((mEntries[i].mHashCode == hashCode) && (mEntries[i].mKey == key))
|
|
|
|
{
|
|
|
|
if (last < 0)
|
|
|
|
{
|
|
|
|
mBuckets[bucket] = mEntries[i].mNext;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mEntries[last].mNext = mEntries[i].mNext;
|
|
|
|
}
|
|
|
|
TKey entryKey = mEntries[i].mKey;
|
|
|
|
TValue result = mEntries[i].mValue;
|
|
|
|
|
|
|
|
mEntries[i].mHashCode = -1;
|
|
|
|
mEntries[i].mNext = mFreeList;
|
|
|
|
#if BF_ENABLE_REALTIME_LEAK_CHECK
|
|
|
|
mEntries[i].mKey = default(TKey);
|
|
|
|
mEntries[i].mValue = default(TValue);
|
|
|
|
#endif
|
|
|
|
mFreeList = i;
|
|
|
|
mFreeCount++;
|
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
mVersion++;
|
|
|
|
#endif
|
|
|
|
return .Ok((entryKey, result));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return .Err;
|
|
|
|
}
|
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
public bool TryGetValue(TKey key, out TValue value)
|
|
|
|
{
|
|
|
|
int_cosize i = (int_cosize)FindEntry(key);
|
|
|
|
if (i >= 0)
|
|
|
|
{
|
|
|
|
value = mEntries[i].mValue;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
value = default(TValue);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-10-27 23:29:03 -03:00
|
|
|
public bool TryGetValueAlt<TAltKey>(TAltKey key, out TValue value) where TAltKey : IHashable where bool : operator TKey == TAltKey
|
|
|
|
{
|
|
|
|
int_cosize i = (int_cosize)FindEntryAlt<TAltKey>(key);
|
|
|
|
if (i >= 0)
|
|
|
|
{
|
|
|
|
value = mEntries[i].mValue;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
value = default(TValue);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-06-19 06:41:50 -07:00
|
|
|
public bool TryGet(TKey key, out TKey matchKey, out TValue value)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
|
|
|
int_cosize i = (int_cosize)FindEntry(key);
|
|
|
|
if (i >= 0)
|
|
|
|
{
|
|
|
|
matchKey = mEntries[i].mKey;
|
2019-08-27 08:04:41 -07:00
|
|
|
value = mEntries[i].mValue;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
matchKey = default(TKey);
|
|
|
|
value = default(TValue);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-02-28 11:27:12 -08:00
|
|
|
public bool TryGetRef(TKey key, out TKey* matchKey, out TValue* value)
|
|
|
|
{
|
|
|
|
int_cosize i = (int_cosize)FindEntry(key);
|
|
|
|
if (i >= 0)
|
|
|
|
{
|
|
|
|
matchKey = &mEntries[i].mKey;
|
|
|
|
value = &mEntries[i].mValue;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
matchKey = null;
|
|
|
|
value = null;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-08-10 14:45:11 -07:00
|
|
|
public bool TryGetAlt<TAltKey>(TAltKey key, out TKey matchKey, out TValue value) where TAltKey : IHashable where bool : operator TKey == TAltKey
|
2019-08-27 08:04:41 -07:00
|
|
|
{
|
2020-06-19 06:41:50 -07:00
|
|
|
int_cosize i = (int_cosize)FindEntryAlt(key);
|
2019-08-27 08:04:41 -07:00
|
|
|
if (i >= 0)
|
|
|
|
{
|
|
|
|
matchKey = mEntries[i].mKey;
|
2019-08-23 11:56:54 -07:00
|
|
|
value = mEntries[i].mValue;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
matchKey = default(TKey);
|
|
|
|
value = default(TValue);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-02-28 11:27:12 -08:00
|
|
|
public bool TryGetRefAlt<TAltKey>(TAltKey key, out TKey* matchKey, out TValue* value) where TAltKey : IHashable where bool : operator TKey == TAltKey
|
|
|
|
{
|
|
|
|
int_cosize i = (int_cosize)FindEntryAlt(key);
|
|
|
|
if (i >= 0)
|
|
|
|
{
|
|
|
|
matchKey = &mEntries[i].mKey;
|
|
|
|
value = &mEntries[i].mValue;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
matchKey = null;
|
|
|
|
value = null;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
public TValue GetValueOrDefault(TKey key)
|
|
|
|
{
|
|
|
|
int_cosize i = (int_cosize)FindEntry(key);
|
|
|
|
if (i >= 0)
|
|
|
|
{
|
|
|
|
return mEntries[i].mValue;
|
|
|
|
}
|
|
|
|
return default(TValue);
|
|
|
|
}
|
|
|
|
|
2020-05-01 16:29:12 -07:00
|
|
|
public struct Enumerator : IEnumerator<KeyValuePair>, IRefEnumerator<KeyRefValuePair>
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
2022-02-28 11:27:12 -08:00
|
|
|
private Dictionary<TKey, TValue> mDictionary;
|
2019-08-23 11:56:54 -07:00
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
private int_cosize mVersion;
|
|
|
|
#endif
|
|
|
|
private int_cosize mIndex;
|
|
|
|
private int_cosize mCurrentIndex;
|
|
|
|
//private KeyValuePair<TKey, TValue> current;
|
|
|
|
private int_cosize mGetEnumeratorRetType; // What should Enumerator.Current return?
|
|
|
|
|
2020-03-09 06:34:16 -07:00
|
|
|
const int_cosize DictEntry = 1;
|
|
|
|
const int_cosize KeyValuePair = 2;
|
2019-08-23 11:56:54 -07:00
|
|
|
|
2020-03-09 06:34:16 -07:00
|
|
|
public this(Dictionary<TKey, TValue> dictionary, int_cosize getEnumeratorRetType)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
|
|
|
mDictionary = dictionary;
|
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
mVersion = dictionary.mVersion;
|
|
|
|
#endif
|
|
|
|
mIndex = 0;
|
|
|
|
mGetEnumeratorRetType = getEnumeratorRetType;
|
|
|
|
//current = KeyValuePair<TKey, TValue>();
|
|
|
|
//current = default(KeyValuePair<TKey, TValue>);
|
|
|
|
mCurrentIndex = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
void CheckVersion()
|
|
|
|
{
|
|
|
|
if (mVersion != mDictionary.mVersion)
|
|
|
|
Runtime.FatalError(cVersionError);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
public bool MoveNext() mut
|
|
|
|
{
|
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
CheckVersion();
|
|
|
|
#endif
|
|
|
|
// Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends.
|
|
|
|
// dictionary.count+1 could be negative if dictionary.count is Int32.MaxValue
|
|
|
|
while ((uint)mIndex < (uint)mDictionary.mCount)
|
|
|
|
{
|
|
|
|
if (mDictionary.mEntries[mIndex].mHashCode >= 0)
|
|
|
|
{
|
|
|
|
mCurrentIndex = mIndex;
|
|
|
|
//current = KeyValuePair<TKey, TValue>(dictionary.entries[index].key, dictionary.entries[index].value);
|
|
|
|
mIndex++;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
mIndex++;
|
|
|
|
}
|
|
|
|
|
|
|
|
mIndex = mDictionary.mCount + 1;
|
|
|
|
//current = default(KeyValuePair<TKey, TValue>);
|
|
|
|
mCurrentIndex = -1;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public ref TKey Key
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return ref mDictionary.mEntries[mCurrentIndex].mKey;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public ref TValue Value
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return ref mDictionary.mEntries[mCurrentIndex].mValue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-01 14:42:54 +08:00
|
|
|
public KeyValuePair Current
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
2020-05-01 09:12:50 -07:00
|
|
|
get { return (mDictionary.mEntries[mCurrentIndex].mKey, mDictionary.mEntries[mCurrentIndex].mValue); }
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
2020-05-01 16:29:12 -07:00
|
|
|
public KeyRefValuePair CurrentRef
|
|
|
|
{
|
|
|
|
get { return (mDictionary.mEntries[mCurrentIndex].mKey, &mDictionary.mEntries[mCurrentIndex].mValue); }
|
|
|
|
}
|
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
public void Dispose()
|
|
|
|
{
|
2020-05-01 14:42:54 +08:00
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
public void SetValue(TValue value)
|
|
|
|
{
|
|
|
|
mDictionary.mEntries[mCurrentIndex].mValue = value;
|
|
|
|
}
|
|
|
|
|
2021-06-24 20:04:56 -03:00
|
|
|
public void Remove() mut
|
|
|
|
{
|
|
|
|
int_cosize curIdx = mIndex - 1;
|
2021-12-14 12:21:53 -03:00
|
|
|
mDictionary.RemoveEntry(mDictionary.mEntries[curIdx].mHashCode, curIdx);
|
2021-06-24 20:19:47 -03:00
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
mVersion = mDictionary.mVersion;
|
|
|
|
#endif
|
2021-06-24 20:04:56 -03:00
|
|
|
mIndex = curIdx;
|
|
|
|
}
|
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
public void Reset() mut
|
|
|
|
{
|
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
CheckVersion();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
mIndex = 0;
|
|
|
|
//current = default(KeyValuePair<TKey, TValue>);
|
|
|
|
mCurrentIndex = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*DictionaryEntry IDictionaryEnumerator.Entry
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
if (index == 0 || (index == dictionary.count + 1))
|
|
|
|
{
|
|
|
|
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
|
|
|
|
}
|
|
|
|
|
|
|
|
return new DictionaryEntry(current.Key, current.Value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
object IDictionaryEnumerator.Key
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
if (index == 0 || (index == dictionary.count + 1))
|
|
|
|
{
|
|
|
|
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
|
|
|
|
}
|
|
|
|
|
|
|
|
return current.Key;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
object IDictionaryEnumerator.Value
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
if (index == 0 || (index == dictionary.count + 1))
|
|
|
|
{
|
|
|
|
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
|
|
|
|
}
|
|
|
|
|
|
|
|
return current.Value;
|
|
|
|
}
|
|
|
|
}*/
|
|
|
|
|
2020-05-01 14:42:54 +08:00
|
|
|
public Result<KeyValuePair> GetNext() mut
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
|
|
|
if (!MoveNext())
|
|
|
|
return .Err;
|
|
|
|
return Current;
|
|
|
|
}
|
2020-05-01 16:29:12 -07:00
|
|
|
|
|
|
|
public Result<KeyRefValuePair> GetNextRef() mut
|
|
|
|
{
|
|
|
|
if (!MoveNext())
|
|
|
|
return .Err;
|
|
|
|
return CurrentRef;
|
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
2020-05-01 16:29:12 -07:00
|
|
|
public struct ValueEnumerator : IRefEnumerator<TValue*>, IEnumerator<TValue>, IResettable
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
|
|
|
private Dictionary<TKey, TValue> mDictionary;
|
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
private int_cosize mVersion;
|
|
|
|
#endif
|
|
|
|
private int_cosize mIndex;
|
2022-02-28 11:27:12 -08:00
|
|
|
private TValue* mCurrent;
|
2019-08-23 11:56:54 -07:00
|
|
|
|
2020-03-09 06:34:16 -07:00
|
|
|
const int_cosize cDictEntry = 1;
|
|
|
|
const int_cosize cKeyValuePair = 2;
|
2019-08-23 11:56:54 -07:00
|
|
|
|
2020-03-09 06:34:16 -07:00
|
|
|
public this(Dictionary<TKey, TValue> dictionary)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
|
|
|
mDictionary = dictionary;
|
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
mVersion = dictionary.mVersion;
|
|
|
|
#endif
|
|
|
|
mIndex = 0;
|
|
|
|
mCurrent = default;
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool MoveNext() mut
|
|
|
|
{
|
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
CheckVersion();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends.
|
|
|
|
// dictionary.count+1 could be negative if dictionary.count is Int32.MaxValue
|
|
|
|
while ((uint)mIndex < (uint)mDictionary.mCount)
|
|
|
|
{
|
|
|
|
if (mDictionary.mEntries[mIndex].mHashCode >= 0)
|
|
|
|
{
|
2022-02-28 11:27:12 -08:00
|
|
|
mCurrent = &mDictionary.mEntries[mIndex].mValue;
|
2019-08-23 11:56:54 -07:00
|
|
|
mIndex++;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
mIndex++;
|
|
|
|
}
|
|
|
|
|
|
|
|
mIndex = mDictionary.mCount + 1;
|
|
|
|
mCurrent = default;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public TValue Current
|
|
|
|
{
|
2022-02-28 11:27:12 -08:00
|
|
|
get { return *mCurrent; }
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
|
2020-02-08 06:11:11 -08:00
|
|
|
public ref TValue CurrentRef
|
|
|
|
{
|
2022-02-28 11:27:12 -08:00
|
|
|
get mut { return ref *mCurrent; }
|
2020-02-08 06:11:11 -08:00
|
|
|
}
|
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
public ref TKey Key
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
2022-02-28 22:42:46 -03:00
|
|
|
return ref mDictionary.mEntries[mIndex - 1].mKey;
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
void CheckVersion()
|
|
|
|
{
|
|
|
|
if (mVersion != mDictionary.mVersion)
|
|
|
|
Runtime.FatalError(cVersionError);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-06-24 20:04:56 -03:00
|
|
|
public void Remove() mut
|
|
|
|
{
|
|
|
|
int_cosize curIdx = mIndex - 1;
|
2021-12-14 12:21:53 -03:00
|
|
|
mDictionary.RemoveEntry(mDictionary.mEntries[curIdx].mHashCode, curIdx);
|
2021-06-24 20:19:47 -03:00
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
mVersion = mDictionary.mVersion;
|
|
|
|
#endif
|
2021-06-24 20:04:56 -03:00
|
|
|
mIndex = curIdx;
|
|
|
|
}
|
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
public void Reset() mut
|
|
|
|
{
|
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
CheckVersion();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
mIndex = 0;
|
|
|
|
mCurrent = default;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Result<TValue> GetNext() mut
|
|
|
|
{
|
|
|
|
if (!MoveNext())
|
|
|
|
return .Err;
|
|
|
|
return Current;
|
|
|
|
}
|
|
|
|
|
2020-02-08 06:11:11 -08:00
|
|
|
public Result<TValue*> GetNextRef() mut
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
|
|
|
if (!MoveNext())
|
|
|
|
return .Err;
|
2020-02-08 06:11:11 -08:00
|
|
|
return &CurrentRef;
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-28 11:27:12 -08:00
|
|
|
public struct KeyEnumerator : IEnumerator<TKey>, IRefEnumerator<TKey*>, IResettable
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
|
|
|
private Dictionary<TKey, TValue> mDictionary;
|
2020-05-11 10:17:45 -07:00
|
|
|
#if VERSION_DICTIONARY
|
2019-08-23 11:56:54 -07:00
|
|
|
private int32 mVersion;
|
|
|
|
#endif
|
|
|
|
private int_cosize mIndex;
|
|
|
|
private TKey* mCurrent;
|
|
|
|
|
2020-03-09 06:34:16 -07:00
|
|
|
const int_cosize DictEntry = 1;
|
|
|
|
const int_cosize KeyValuePair = 2;
|
2019-08-23 11:56:54 -07:00
|
|
|
|
2020-03-09 06:34:16 -07:00
|
|
|
public this(Dictionary<TKey, TValue> dictionary)
|
2019-08-23 11:56:54 -07:00
|
|
|
{
|
|
|
|
mDictionary = dictionary;
|
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
mVersion = dictionary.mVersion;
|
|
|
|
#endif
|
|
|
|
mIndex = 0;
|
|
|
|
mCurrent = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
void CheckVersion()
|
|
|
|
{
|
|
|
|
if (mVersion != mDictionary.mVersion)
|
|
|
|
Runtime.FatalError(cVersionError);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
public bool MoveNext() mut
|
|
|
|
{
|
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
CheckVersion();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Use unsigned comparison since we set index to dictionary.count+1 when the enumeration ends.
|
|
|
|
// dictionary.count+1 could be negative if dictionary.count is Int32.MaxValue
|
|
|
|
while ((uint32)mIndex < (uint32)mDictionary.mCount)
|
|
|
|
{
|
|
|
|
if (mDictionary.mEntries[mIndex].mHashCode >= 0)
|
|
|
|
{
|
|
|
|
mCurrent = &mDictionary.mEntries[mIndex].mKey;
|
|
|
|
mIndex++;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
mIndex++;
|
|
|
|
}
|
|
|
|
|
|
|
|
mIndex = mDictionary.mCount + 1;
|
|
|
|
mCurrent = null;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public TKey Current
|
|
|
|
{
|
|
|
|
get { return *mCurrent; }
|
|
|
|
}
|
|
|
|
|
2022-02-28 11:27:12 -08:00
|
|
|
public ref TKey CurrentRef
|
|
|
|
{
|
|
|
|
get { return ref *mCurrent; }
|
|
|
|
}
|
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
public ref TValue Value
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return ref mDictionary.mEntries[mIndex - 1].mValue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-06-24 20:04:56 -03:00
|
|
|
public void Remove() mut
|
|
|
|
{
|
|
|
|
int_cosize curIdx = mIndex - 1;
|
2021-12-14 12:21:53 -03:00
|
|
|
mDictionary.RemoveEntry(mDictionary.mEntries[curIdx].mHashCode, curIdx);
|
2021-06-24 20:19:47 -03:00
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
mVersion = mDictionary.mVersion;
|
|
|
|
#endif
|
2021-06-24 20:04:56 -03:00
|
|
|
mIndex = curIdx;
|
|
|
|
}
|
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
public void Reset() mut
|
|
|
|
{
|
|
|
|
#if VERSION_DICTIONARY
|
|
|
|
CheckVersion();
|
|
|
|
#endif
|
|
|
|
mIndex = 0;
|
|
|
|
mCurrent = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Result<TKey> GetNext() mut
|
|
|
|
{
|
|
|
|
if (!MoveNext())
|
|
|
|
return .Err;
|
|
|
|
return Current;
|
|
|
|
}
|
2022-02-28 11:27:12 -08:00
|
|
|
|
|
|
|
public Result<TKey*> GetNextRef() mut
|
|
|
|
{
|
|
|
|
if (!MoveNext())
|
|
|
|
return .Err;
|
|
|
|
return &CurrentRef;
|
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|