Browse Source

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.

(cherry picked from commit 2deefd938fbbaf6c8472031de8b631ab505b3cfc)
Raul Santos 4 years ago
parent
commit
332e31260e

+ 49 - 26
modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs

@@ -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);
             }
         }
 

+ 15 - 0
modules/mono/glue/collections_glue.cpp

@@ -229,6 +229,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);
@@ -337,6 +350,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);