1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-07 12:30:27 +00:00

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

View File

@@ -230,6 +230,19 @@ int32_t godot_icall_Dictionary_Count(Dictionary *ptr) {
return ptr->size(); 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) { void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value) {
Variant varKey = GDMonoMarshal::mono_object_to_variant(key); Variant varKey = GDMonoMarshal::mono_object_to_variant(key);
Variant *ret = ptr->getptr(varKey); 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_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_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_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_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_Clear", godot_icall_Dictionary_Clear);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Contains", godot_icall_Dictionary_Contains); GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Contains", godot_icall_Dictionary_Contains);