From 0669ffcd157dffb3e284457e968e617431badfd7 Mon Sep 17 00:00:00 2001 From: Raul Santos Date: Wed, 28 Jul 2021 15:44:09 +0200 Subject: Add documentation to Dictionary in C# Adds documentation to `Godot.Collections.Dictionary` in C#. --- .../glue/GodotSharp/GodotSharp/Core/Dictionary.cs | 188 ++++++++++++++++++--- 1 file changed, 168 insertions(+), 20 deletions(-) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index 213fc181c1..3d98445b14 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Diagnostics.CodeAnalysis; namespace Godot.Collections { @@ -25,6 +26,11 @@ namespace Godot.Collections } } + /// + /// Wrapper around Godot's Dictionary class, a dictionary of Variant + /// typed elements allocated in the engine in C++. Useful when + /// interfacing with the engine. + /// public class Dictionary : IDictionary, IDisposable @@ -32,11 +38,19 @@ namespace Godot.Collections DictionarySafeHandle safeHandle; bool disposed = false; + /// + /// Constructs a new empty . + /// public Dictionary() { safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor()); } + /// + /// Constructs a new from the given dictionary's elements. + /// + /// The dictionary to construct from. + /// A new Godot Dictionary. public Dictionary(IDictionary dictionary) : this() { if (dictionary == null) @@ -64,6 +78,9 @@ namespace Godot.Collections return safeHandle.DangerousGetHandle(); } + /// + /// Disposes of this . + /// public void Dispose() { if (disposed) @@ -78,6 +95,11 @@ namespace Godot.Collections disposed = true; } + /// + /// Duplicates this . + /// + /// If , performs a deep copy. + /// A new Godot Dictionary. public Dictionary Duplicate(bool deep = false) { return new Dictionary(godot_icall_Dictionary_Duplicate(GetPtr(), deep)); @@ -85,6 +107,9 @@ namespace Godot.Collections // IDictionary + /// + /// Gets the collection of keys in this . + /// public ICollection Keys { get @@ -94,6 +119,9 @@ namespace Godot.Collections } } + /// + /// Gets the collection of elements in this . + /// public ICollection Values { get @@ -103,34 +131,71 @@ namespace Godot.Collections } } - public bool IsFixedSize => false; + bool IDictionary.IsFixedSize => false; - public bool IsReadOnly => false; + bool IDictionary.IsReadOnly => false; + /// + /// Returns the object at the given . + /// + /// The object at the given . public object this[object key] { get => godot_icall_Dictionary_GetValue(GetPtr(), key); set => godot_icall_Dictionary_SetValue(GetPtr(), key, value); } + /// + /// Adds an object at key + /// to this . + /// + /// The key at which to add the object. + /// The object to add. public void Add(object key, object value) => godot_icall_Dictionary_Add(GetPtr(), key, value); + /// + /// Erases all items from this . + /// public void Clear() => godot_icall_Dictionary_Clear(GetPtr()); + /// + /// Checks if this contains the given key. + /// + /// The key to look for. + /// Whether or not this dictionary contains the given key. public bool Contains(object key) => godot_icall_Dictionary_ContainsKey(GetPtr(), key); + /// + /// Gets an enumerator for this . + /// + /// An enumerator. public IDictionaryEnumerator GetEnumerator() => new DictionaryEnumerator(this); + /// + /// Removes an element from this by key. + /// + /// The key of the element to remove. public void Remove(object key) => godot_icall_Dictionary_RemoveKey(GetPtr(), key); // ICollection - public object SyncRoot => this; + object ICollection.SyncRoot => this; - public bool IsSynchronized => false; + bool ICollection.IsSynchronized => false; + /// + /// Returns the number of elements in this . + /// This is also known as the size or length of the dictionary. + /// + /// The number of elements. public int Count => godot_icall_Dictionary_Count(GetPtr()); + /// + /// Copies the elements of this to the given + /// untyped C# array, starting at the given index. + /// + /// The array to copy to. + /// The index to start at. public void CopyTo(System.Array array, int index) { // TODO Can be done with single internal call @@ -161,10 +226,10 @@ namespace Godot.Collections private class DictionaryEnumerator : IDictionaryEnumerator { - Array keys; - Array values; - int count; - int index = -1; + private readonly Array keys; + private readonly Array values; + private readonly int count; + private int index = -1; public DictionaryEnumerator(Dictionary dictionary) { @@ -196,6 +261,10 @@ namespace Godot.Collections } } + /// + /// Converts this to a string. + /// + /// A string representation of this dictionary. public override string ToString() { return godot_icall_Dictionary_ToString(GetPtr()); @@ -259,10 +328,18 @@ namespace Godot.Collections internal extern static string godot_icall_Dictionary_ToString(IntPtr ptr); } + /// + /// Typed wrapper around Godot's Dictionary class, a dictionary of Variant + /// typed elements allocated in the engine in C++. Useful when + /// interfacing with the engine. Otherwise prefer .NET collections + /// such as . + /// + /// The type of the dictionary's keys. + /// The type of the dictionary's values. public class Dictionary : IDictionary { - Dictionary objectDict; + private readonly Dictionary objectDict; internal static int valTypeEncoding; internal static IntPtr valTypeClass; @@ -272,11 +349,19 @@ namespace Godot.Collections Dictionary.godot_icall_Dictionary_Generic_GetValueTypeInfo(typeof(TValue), out valTypeEncoding, out valTypeClass); } + /// + /// Constructs a new empty . + /// public Dictionary() { objectDict = new Dictionary(); } + /// + /// Constructs a new from the given dictionary's elements. + /// + /// The dictionary to construct from. + /// A new Godot Dictionary. public Dictionary(IDictionary dictionary) { objectDict = new Dictionary(); @@ -294,6 +379,11 @@ namespace Godot.Collections } } + /// + /// Constructs a new from the given dictionary's elements. + /// + /// The dictionary to construct from. + /// A new Godot Dictionary. public Dictionary(Dictionary dictionary) { objectDict = dictionary; @@ -309,6 +399,10 @@ namespace Godot.Collections objectDict = new Dictionary(handle); } + /// + /// Converts this typed to an untyped . + /// + /// The typed dictionary to convert. public static explicit operator Dictionary(Dictionary from) { return from.objectDict; @@ -319,6 +413,11 @@ namespace Godot.Collections return objectDict.GetPtr(); } + /// + /// Duplicates this . + /// + /// If , performs a deep copy. + /// A new Godot Dictionary. public Dictionary Duplicate(bool deep = false) { return new Dictionary(objectDict.Duplicate(deep)); @@ -326,12 +425,19 @@ namespace Godot.Collections // IDictionary + /// + /// Returns the value at the given . + /// + /// The value at the given . public TValue this[TKey key] { get { return (TValue)Dictionary.godot_icall_Dictionary_GetValue_Generic(objectDict.GetPtr(), key, valTypeEncoding, valTypeClass); } set { objectDict[key] = value; } } + /// + /// Gets the collection of keys in this . + /// public ICollection Keys { get @@ -341,6 +447,9 @@ namespace Godot.Collections } } + /// + /// Gets the collection of elements in this . + /// public ICollection Values { get @@ -350,56 +459,87 @@ namespace Godot.Collections } } + /// + /// Adds an object at key + /// to this . + /// + /// The key at which to add the object. + /// The object to add. public void Add(TKey key, TValue value) { objectDict.Add(key, value); } + /// + /// Checks if this contains the given key. + /// + /// The key to look for. + /// Whether or not this dictionary contains the given key. public bool ContainsKey(TKey key) { return objectDict.Contains(key); } + /// + /// Removes an element from this by key. + /// + /// The key of the element to remove. public bool Remove(TKey key) { return Dictionary.godot_icall_Dictionary_RemoveKey(GetPtr(), key); } - public bool TryGetValue(TKey key, out TValue value) + /// + /// Gets the object at the given . + /// + /// The key of the element to get. + /// The value at the given . + /// If an object was found for the given . + public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) { - object retValue; - bool found = Dictionary.godot_icall_Dictionary_TryGetValue_Generic(GetPtr(), key, out retValue, valTypeEncoding, valTypeClass); - value = found ? (TValue)retValue : default(TValue); + bool found = Dictionary.godot_icall_Dictionary_TryGetValue_Generic(GetPtr(), key, out object retValue, valTypeEncoding, valTypeClass); + value = found ? (TValue)retValue : default; return found; } // ICollection> + /// + /// Returns the number of elements in this . + /// This is also known as the size or length of the dictionary. + /// + /// The number of elements. public int Count { get { return objectDict.Count; } } - public bool IsReadOnly - { - get { return objectDict.IsReadOnly; } - } + bool ICollection>.IsReadOnly => false; - public void Add(KeyValuePair item) + void ICollection>.Add(KeyValuePair item) { objectDict.Add(item.Key, item.Value); } + /// + /// Erases all the items from this . + /// public void Clear() { objectDict.Clear(); } - public bool Contains(KeyValuePair item) + bool ICollection>.Contains(KeyValuePair item) { return objectDict.Contains(new KeyValuePair(item.Key, item.Value)); } + /// + /// Copies the elements of this to the given + /// untyped C# array, starting at the given index. + /// + /// The array to copy to. + /// The index to start at. public void CopyTo(KeyValuePair[] array, int arrayIndex) { if (array == null) @@ -424,7 +564,7 @@ namespace Godot.Collections } } - public bool Remove(KeyValuePair item) + bool ICollection>.Remove(KeyValuePair item) { return Dictionary.godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value); ; @@ -432,6 +572,10 @@ namespace Godot.Collections // IEnumerable> + /// + /// Gets an enumerator for this . + /// + /// An enumerator. public IEnumerator> GetEnumerator() { // TODO 3 internal calls, can reduce to 1 @@ -451,6 +595,10 @@ namespace Godot.Collections return GetEnumerator(); } + /// + /// Converts this to a string. + /// + /// A string representation of this dictionary. public override string ToString() => objectDict.ToString(); } } -- cgit v1.2.3 From 2deefd938fbbaf6c8472031de8b631ab505b3cfc Mon Sep 17 00:00:00 2001 From: Raul Santos Date: Wed, 28 Jul 2021 16:55:29 +0200 Subject: 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. --- .../glue/GodotSharp/GodotSharp/Core/Dictionary.cs | 75 ++++++++++++++-------- modules/mono/glue/collections_glue.cpp | 15 +++++ 2 files changed, 64 insertions(+), 26 deletions(-) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index 3d98445b14..61a34bfc87 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/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 /// The index to start at. 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 GetKeyValuePair(int index) + { + Dictionary.godot_icall_Dictionary_KeyValuePairAt(GetPtr(), index, out object key, out object value); + return new KeyValuePair((TKey)key, (TValue)value); + } + /// /// Adds an object at key /// to this . @@ -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 keys = (Array)Keys; - Array values = (Array)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(keys[i], values[i]); + array[arrayIndex] = GetKeyValuePair(i); arrayIndex++; } } @@ -578,15 +607,9 @@ namespace Godot.Collections /// An enumerator. public IEnumerator> GetEnumerator() { - // TODO 3 internal calls, can reduce to 1 - Array keys = (Array)Keys; - Array values = (Array)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(keys[i], values[i]); + yield return GetKeyValuePair(i); } } diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp index 191f863350..86976de244 100644 --- a/modules/mono/glue/collections_glue.cpp +++ b/modules/mono/glue/collections_glue.cpp @@ -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); -- cgit v1.2.3