Reduce C# Dictionary internal calls

- Implements new `KeyValuePairs` and `KeyValuePairAt` internal calls
to get the `key` and the `value` in one call.
- Caches the `DictionaryEntry` to reuse properties without repeating
internal calls.
This commit is contained in:
Raul Santos 2021-07-28 16:55:29 +02:00
parent 0669ffcd15
commit 2deefd938f
2 changed files with 64 additions and 26 deletions

View file

@ -131,6 +131,14 @@ namespace Godot.Collections
}
}
private (Array keys, Array values, int count) GetKeyValuePairs()
{
int count = godot_icall_Dictionary_KeyValuePairs(GetPtr(), out IntPtr keysHandle, out IntPtr valuesHandle);
Array keys = new Array(new ArraySafeHandle(keysHandle));
Array values = new Array(new ArraySafeHandle(valuesHandle));
return (keys, values, count);
}
bool IDictionary.IsFixedSize => false;
bool IDictionary.IsReadOnly => false;
@ -198,17 +206,13 @@ namespace Godot.Collections
/// <param name="index">The index to start at.</param>
public void CopyTo(System.Array array, int index)
{
// TODO Can be done with single internal call
if (array == null)
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
if (index < 0)
throw new ArgumentOutOfRangeException(nameof(index), "Number was less than the array's lower bound in the first dimension.");
Array keys = (Array)Keys;
Array values = (Array)Values;
int count = Count;
var (keys, values, count) = GetKeyValuePairs();
if (array.Length < (index + count))
throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
@ -226,24 +230,39 @@ namespace Godot.Collections
private class DictionaryEnumerator : IDictionaryEnumerator
{
private readonly Array keys;
private readonly Array values;
private readonly Dictionary dictionary;
private readonly int count;
private int index = -1;
private bool dirty = true;
private DictionaryEntry entry;
public DictionaryEnumerator(Dictionary dictionary)
{
// TODO 3 internal calls, can reduce to 1
keys = (Array)dictionary.Keys;
values = (Array)dictionary.Values;
this.dictionary = dictionary;
count = dictionary.Count;
}
public object Current => Entry;
public DictionaryEntry Entry =>
// TODO 2 internal calls, can reduce to 1
new DictionaryEntry(keys[index], values[index]);
public DictionaryEntry Entry
{
get
{
if (dirty)
{
UpdateEntry();
}
return entry;
}
}
private void UpdateEntry()
{
dirty = false;
godot_icall_Dictionary_KeyValuePairAt(dictionary.GetPtr(), index, out object key, out object value);
entry = new DictionaryEntry(key, value);
}
public object Key => Entry.Key;
@ -252,12 +271,14 @@ namespace Godot.Collections
public bool MoveNext()
{
index++;
dirty = true;
return index < count;
}
public void Reset()
{
index = -1;
dirty = true;
}
}
@ -294,6 +315,12 @@ namespace Godot.Collections
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static int godot_icall_Dictionary_Count(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static int godot_icall_Dictionary_KeyValuePairs(IntPtr ptr, out IntPtr keys, out IntPtr values);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_Dictionary_KeyValuePairAt(IntPtr ptr, int index, out object key, out object value);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void godot_icall_Dictionary_Add(IntPtr ptr, object key, object value);
@ -459,6 +486,12 @@ namespace Godot.Collections
}
}
private KeyValuePair<TKey, TValue> GetKeyValuePair(int index)
{
Dictionary.godot_icall_Dictionary_KeyValuePairAt(GetPtr(), index, out object key, out object value);
return new KeyValuePair<TKey, TValue>((TKey)key, (TValue)value);
}
/// <summary>
/// Adds an object <paramref name="value"/> at key <paramref name="key"/>
/// to this <see cref="Dictionary{TKey, TValue}"/>.
@ -548,9 +581,6 @@ namespace Godot.Collections
if (arrayIndex < 0)
throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension.");
// TODO 3 internal calls, can reduce to 1
Array<TKey> keys = (Array<TKey>)Keys;
Array<TValue> values = (Array<TValue>)Values;
int count = Count;
if (array.Length < (arrayIndex + count))
@ -558,8 +588,7 @@ namespace Godot.Collections
for (int i = 0; i < count; i++)
{
// TODO 2 internal calls, can reduce to 1
array[arrayIndex] = new KeyValuePair<TKey, TValue>(keys[i], values[i]);
array[arrayIndex] = GetKeyValuePair(i);
arrayIndex++;
}
}
@ -578,15 +607,9 @@ namespace Godot.Collections
/// <returns>An enumerator.</returns>
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
// TODO 3 internal calls, can reduce to 1
Array<TKey> keys = (Array<TKey>)Keys;
Array<TValue> values = (Array<TValue>)Values;
int count = Count;
for (int i = 0; i < count; i++)
for (int i = 0; i < Count; i++)
{
// TODO 2 internal calls, can reduce to 1
yield return new KeyValuePair<TKey, TValue>(keys[i], values[i]);
yield return GetKeyValuePair(i);
}
}

View file

@ -230,6 +230,19 @@ int32_t godot_icall_Dictionary_Count(Dictionary *ptr) {
return ptr->size();
}
int32_t godot_icall_Dictionary_KeyValuePairs(Dictionary *ptr, Array **keys, Array **values) {
*keys = godot_icall_Dictionary_Keys(ptr);
*values = godot_icall_Dictionary_Values(ptr);
return godot_icall_Dictionary_Count(ptr);
}
void godot_icall_Dictionary_KeyValuePairAt(Dictionary *ptr, int index, MonoObject **key, MonoObject **value) {
Array *keys = godot_icall_Dictionary_Keys(ptr);
Array *values = godot_icall_Dictionary_Values(ptr);
*key = GDMonoMarshal::variant_to_mono_object(keys->get(index));
*value = GDMonoMarshal::variant_to_mono_object(values->get(index));
}
void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value) {
Variant varKey = GDMonoMarshal::mono_object_to_variant(key);
Variant *ret = ptr->getptr(varKey);
@ -338,6 +351,8 @@ void godot_register_collections_icalls() {
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Keys", godot_icall_Dictionary_Keys);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Values", godot_icall_Dictionary_Values);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Count", godot_icall_Dictionary_Count);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_KeyValuePairs", godot_icall_Dictionary_KeyValuePairs);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_KeyValuePairAt", godot_icall_Dictionary_KeyValuePairAt);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Add", godot_icall_Dictionary_Add);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Clear", godot_icall_Dictionary_Clear);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Contains", godot_icall_Dictionary_Contains);