/* * Copyright 2019 by its authors. See AUTHORS. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma warning disable 1591 using System; using System.Runtime.InteropServices; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using static Eina.TraitFunctions; using static Eina.IteratorNativeFunctions; using static Eina.HashNativeFunctions; using Eina.Callbacks; namespace Eina { [StructLayout(LayoutKind.Sequential)] [EditorBrowsable(EditorBrowsableState.Never)] internal struct HashTupleNative : IEquatable { public IntPtr key; public IntPtr data; public uint key_length; /// /// Gets a hash for . /// Since EFL 1.24. /// /// A hash code. public override int GetHashCode() => key.GetHashCode() ^ data.GetHashCode() ^ key_length.GetHashCode(); /// Returns whether this /// is equal to the given . /// Since EFL 1.24. /// /// The to be compared to. /// true if is equal to other. public override bool Equals(object other) => (!(other is HashTupleNative)) ? false : Equals((HashTupleNative)other); /// Returns whether this is equal /// to the given . /// Since EFL 1.24. /// /// The to be compared to. /// true if is equal to other. public bool Equals(HashTupleNative other) => (key == other.key) && (data == other.data) && (key_length == other.key_length); /// Returns whether lhs is equal to rhs. /// Since EFL 1.24. /// /// The left hand side of the operator. /// The right hand side of the operator. /// true if lhs is equal /// to rhs. public static bool operator==(HashTupleNative lhs, HashTupleNative rhs) => lhs.Equals(rhs); /// Returns whether lhs is not equal to rhs. /// Since EFL 1.24. /// /// The left hand side of the operator. /// The right hand side of the operator. /// true if lhs is not equal /// to rhs. public static bool operator!=(HashTupleNative lhs, HashTupleNative rhs) => !(lhs == rhs); } [EditorBrowsable(EditorBrowsableState.Never)] internal static class HashNativeFunctions { [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_hash_new(IntPtr key_length_cb, IntPtr key_cmp_cb, IntPtr key_hash_cb, IntPtr data_free_cb, int buckets_power_size); [DllImport(efl.Libs.Eina)] internal static extern void eina_hash_free_cb_set(IntPtr hash, IntPtr data_free_cb); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_hash_string_djb2_new(IntPtr data_free_cb); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_hash_string_superfast_new(IntPtr data_free_cb); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_hash_string_small_new(IntPtr data_free_cb); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_hash_int32_new(IntPtr data_free_cb); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_hash_int64_new(IntPtr data_free_cb); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_hash_pointer_new(IntPtr data_free_cb); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_hash_stringshared_new(IntPtr data_free_cb); [DllImport(efl.Libs.Eina)] [return: MarshalAs(UnmanagedType.U1)] internal static extern bool eina_hash_add(IntPtr hash, IntPtr key, IntPtr data); [DllImport(efl.Libs.Eina)] [return: MarshalAs(UnmanagedType.U1)] internal static extern bool eina_hash_direct_add(IntPtr hash, IntPtr key, IntPtr data); [DllImport(efl.Libs.Eina)] [return: MarshalAs(UnmanagedType.U1)] internal static extern bool eina_hash_del(IntPtr hash, IntPtr key, IntPtr data); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_hash_find(IntPtr hash, IntPtr key); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_hash_modify(IntPtr hash, IntPtr key, IntPtr data); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_hash_set(IntPtr hash, IntPtr key, IntPtr data); [DllImport(efl.Libs.Eina)] [return: MarshalAs(UnmanagedType.U1)] internal static extern bool eina_hash_move(IntPtr hash, IntPtr old_key, IntPtr new_key); [DllImport(efl.Libs.Eina)] internal static extern void eina_hash_free(IntPtr hash); [DllImport(efl.Libs.Eina)] internal static extern void eina_hash_free_buckets(IntPtr hash); [DllImport(efl.Libs.Eina)] internal static extern int eina_hash_population(IntPtr hash); [DllImport(efl.Libs.Eina)] [return: MarshalAs(UnmanagedType.U1)] internal static extern bool eina_hash_add_by_hash(IntPtr hash, IntPtr key, int key_length, int key_hash, IntPtr data); [DllImport(efl.Libs.Eina)] [return: MarshalAs(UnmanagedType.U1)] internal static extern bool eina_hash_direct_add_by_hash(IntPtr hash, IntPtr key, int key_length, int key_hash, IntPtr data); [DllImport(efl.Libs.Eina)] [return: MarshalAs(UnmanagedType.U1)] internal static extern bool eina_hash_del_by_key_hash(IntPtr hash, IntPtr key, int key_length, int key_hash); [DllImport(efl.Libs.Eina)] [return: MarshalAs(UnmanagedType.U1)] internal static extern bool eina_hash_del_by_key(IntPtr hash, IntPtr key); [DllImport(efl.Libs.Eina)] [return: MarshalAs(UnmanagedType.U1)] internal static extern bool eina_hash_del_by_data(IntPtr hash, IntPtr data); [DllImport(efl.Libs.Eina)] [return: MarshalAs(UnmanagedType.U1)] internal static extern bool eina_hash_del_by_hash(IntPtr hash, IntPtr key, int key_length, int key_hash, IntPtr data); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_hash_find_by_hash(IntPtr hash, IntPtr key, int key_length, int key_hash); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_hash_modify_by_hash(IntPtr hash, IntPtr key, int key_length, int key_hash, IntPtr data); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_hash_iterator_key_new(IntPtr hash); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_hash_iterator_data_new(IntPtr hash); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_hash_iterator_tuple_new(IntPtr hash); [DllImport(efl.Libs.Eina)] internal static extern void eina_hash_foreach(IntPtr hash, IntPtr func, IntPtr fdata); [DllImport(efl.Libs.Eina)] internal static extern void eina_hash_list_append(IntPtr hash, IntPtr key, IntPtr data); [DllImport(efl.Libs.Eina)] internal static extern void eina_hash_list_prepend(IntPtr hash, IntPtr key, IntPtr data); [DllImport(efl.Libs.Eina)] internal static extern void eina_hash_list_remove(IntPtr hash, IntPtr key, IntPtr data); [DllImport(efl.Libs.Eina)] internal static extern int eina_hash_superfast(string key, int len); [DllImport(efl.Libs.CustomExports)] internal static extern IntPtr eina_hash_iterator_ptr_key_wrapper_new_custom_export_mono(IntPtr hash); } /// Wrapper around native dictionary mapping keys to values. /// /// Since EFL 1.23. /// [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification = "This is a generalized container mapping the native one.")] public class Hash : IEnumerable>, IDisposable { [EditorBrowsable(EditorBrowsableState.Never)] public IntPtr Handle {get; set;} = IntPtr.Zero; /// Whether this wrapper owns the native hash. /// Since EFL 1.23. /// public bool Own {get; set;} /// Whether this wrapper owns the key. /// Since EFL 1.23. /// public bool OwnKey {get; set;} /// Whether this wrapper owns the value. /// Since EFL 1.23. /// public bool OwnValue {get; set;} /// Quantity of elements in the hash. /// Since EFL 1.23. /// public int Count { get { return Population(); } } private void InitNew() { Handle = EinaHashNew(); SetOwn(true); SetOwnKey(true); SetOwnValue(true); } /// Default constructor. /// Since EFL 1.23. /// public Hash() { InitNew(); } [EditorBrowsable(EditorBrowsableState.Never)] public Hash(IntPtr handle, bool own) { Handle = handle; SetOwnership(own); } [EditorBrowsable(EditorBrowsableState.Never)] public Hash(IntPtr handle, bool own, bool ownKey, bool ownValue) { Handle = handle; SetOwnership(own, ownKey, ownValue); } /// Default destructor. /// Since EFL 1.23. /// ~Hash() { Dispose(false); } /// Disposes of this wrapper, releasing the native accessor if /// owned. /// Since EFL 1.23. /// /// True if this was called from public method. False if /// called from the C# finalizer. protected virtual void Dispose(bool disposing) { IntPtr h = Handle; Handle = IntPtr.Zero; if (h == IntPtr.Zero) { return; } if (Own) { if (disposing) { eina_hash_free(h); } else { Efl.Eo.Globals.ThreadSafeFreeCbExec(eina_hash_free, h); } } } /// Release the native resources held by this instance. /// Since EFL 1.23. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// Release the native resources held by this instance. /// Since EFL 1.23. /// public void Free() { Dispose(); } /// Release the pointer. /// Since EFL 1.23. /// /// The instance. public IntPtr Release() { IntPtr h = Handle; Handle = IntPtr.Zero; return h; } /// Sets ownership. /// Since EFL 1.23. /// /// If the hash own the object. public void SetOwn(bool own) { Own = own; } /// Sets key's ownership. /// Since EFL 1.23. /// /// If the hash own the key's object. public void SetOwnKey(bool ownKey) { OwnKey = ownKey; } /// Sets value's ownership. /// Since EFL 1.23. /// /// If the hash own the value's object. public void SetOwnValue(bool ownValue) { OwnValue = ownValue; if (ownValue) { eina_hash_free_cb_set(Handle, EinaFreeCb()); } } /// Sets all ownership. /// Since EFL 1.23. /// /// If the hash own for all ownerships. public void SetOwnership(bool ownAll) { SetOwn(ownAll); SetOwnKey(ownAll); SetOwnValue(ownAll); } /// Sets own individually. /// Since EFL 1.23. /// /// If the hash own the object. /// If the hash own the key's object. /// If the hash own the value's object. public void SetOwnership(bool own, bool ownKey, bool ownValue) { SetOwn(own); SetOwnKey(ownKey); SetOwnValue(ownValue); } /// /// Cleanup for the hash. /// Since EFL 1.23. /// public void UnSetFreeCb() { eina_hash_free_cb_set(Handle, IntPtr.Zero); } /// /// Adds an entry to the hash. /// Since EFL 1.23. /// /// A unique key. /// The value to associate with the key. /// false if an error occurred, true otherwise. public bool AddNew(TKey key, TValue val) { IntPtr gchnk = CopyNativeObject(key, ForceRefKey()); IntPtr nk = GetNativePtr(gchnk, ForceRefKey()); IntPtr gchnv = CopyNativeObject(val, false); IntPtr nv = GetNativePtr(gchnv, false); var r = eina_hash_add(Handle, nk, nv); FreeNativeIndirection(gchnk, ForceRefKey()); FreeNativeIndirection(gchnv, false); return r; } /// /// Modifies the entry at the specified key. /// Since EFL 1.23. /// /// The key of the entry to modify. /// The value to replace the previous entry. public void Add(TKey key, TValue val) { Set(key, val); } /// /// Removes the entry identified by a key from the hash. /// Since EFL 1.23. /// /// The key. public bool DelByKey(TKey key) { IntPtr gchnk = CopyNativeObject(key, ForceRefKey()); IntPtr nk = GetNativePtr(gchnk, ForceRefKey()); var r = eina_hash_del_by_key(Handle, nk); FreeNativeIndirection(gchnk, ForceRefKey()); return r; } /// Searches this hash for val and deletes it from the hash, /// also deleting it. /// Since EFL 1.23. /// /// The value to be deleted. /// true if the value was found and deleted, false if it was null or not found. public bool DelByValue(TValue val) { // We don't use the C version of `eina_hash_del_by_data` because it requires the exact pointer // we passed to add(). As our hashes store the data by pointer, this makes it harder to pass the // same value. if (val == null) { return false; } foreach (var pair in this) { if (pair.Value != null && val.Equals(pair.Value)) { return this.DelByKey(pair.Key); } } return false; } /// /// Removes the entry identified by a key from the hash. /// Since EFL 1.23. /// /// The key. public void Remove(TKey key) { DelByKey(key); } /// /// Retrieves a specific entry in the hash. /// Since EFL 1.23. /// /// The key of the entry to find. /// The value of the entry. public TValue Find(TKey key) { var gchnk = CopyNativeObject(key, ForceRefKey()); var nk = GetNativePtr(gchnk, ForceRefKey()); var found = eina_hash_find(Handle, nk); //NativeFreeRef(nk); FreeNativeIndirection(gchnk, ForceRefKey()); if (found == IntPtr.Zero) { throw new KeyNotFoundException(); } return NativeToManaged(IndirectNative(found, false)); } /// /// Check if key is present. if not, a default value is setted. /// Since EFL 1.23. /// /// The key to be checked. /// [out] The value of the entry. /// true if key exists, false otherwise. public bool TryGetValue(TKey key, out TValue val) { var gchnk = CopyNativeObject(key, ForceRefKey()); var nk = GetNativePtr(gchnk, ForceRefKey()); var found = eina_hash_find(Handle, nk); FreeNativeIndirection(gchnk, ForceRefKey()); if (found == IntPtr.Zero) { val = default(TValue); return false; } val = NativeToManaged(IndirectNative(found, false)); return true; } /// /// Check if key is present. /// Since EFL 1.23. /// /// The key to be checked. /// true if key exists, false otherwise. public bool ContainsKey(TKey key) { var gchnk = CopyNativeObject(key, ForceRefKey()); var nk = GetNativePtr(gchnk, ForceRefKey()); // var nk = ManagedToNativeAllocRef(key); var found = eina_hash_find(Handle, nk); // NativeFreeRef(nk); FreeNativeIndirection(gchnk, ForceRefKey()); return found != IntPtr.Zero; } /// /// Modifies the speficied key if exists. /// Since EFL 1.23. /// /// The key to modify. /// The new value. /// False if key do not exists, true otherwise. public bool Modify(TKey key, TValue val) { var gchnk = CopyNativeObject(key, ForceRefKey()); var nk = GetNativePtr(gchnk, ForceRefKey()); var gchnv = CopyNativeObject(val, false); var nv = GetNativePtr(gchnv, false); var old = eina_hash_modify(Handle, nk, nv); FreeNativeIndirection(gchnk, ForceRefKey()); // NativeFreeRef(nk); if (old == IntPtr.Zero) { NativeFree(nv); return false; } if (OwnValue) { NativeFree(old); } return true; } private static bool ForceRefKey() { return (!typeof(T).IsValueType) && (typeof(T) != typeof(string)) && (typeof(T) != typeof(Eina.Stringshare)); } private static IntPtr CopyNativeObject(T value, bool forceRef) { if (!IsEflObject(typeof(T)) && forceRef) { GCHandle gch = GCHandle.Alloc(new byte[Marshal.SizeOf()], GCHandleType.Pinned); IntPtr pin = gch.AddrOfPinnedObject(); ManagedToNativeCopyTo(value, pin); return GCHandle.ToIntPtr(gch); } else if (IsEflObject(typeof(T)) && forceRef) { GCHandle gch = GCHandle.Alloc(new byte[Marshal.SizeOf()], GCHandleType.Pinned); IntPtr pin = gch.AddrOfPinnedObject(); ManagedToNativeCopyTo(value, pin); return GCHandle.ToIntPtr(gch); } else { return ManagedToNativeAlloc(value); } } private static IntPtr GetNativePtr(IntPtr gchptr, bool forceRef) { if (forceRef) { GCHandle gch = GCHandle.FromIntPtr(gchptr); IntPtr pin = gch.AddrOfPinnedObject(); return pin; } else { return gchptr; } } private static void FreeNativeIndirection(IntPtr gchptr, bool forceRef) { if (forceRef) { GCHandle gch = GCHandle.FromIntPtr(gchptr); gch.Free(); } } private static IntPtr IndirectNative(IntPtr ptr, bool forceRef) { if (forceRef) { IntPtr val = Marshal.ReadIntPtr(ptr); return val; } else { return ptr; } } /// /// Modifies the entry at the specified key. Adds if key is not found. /// Since EFL 1.23. /// /// The key to modify. /// The value to replace the previous entry. public void Set(TKey key, TValue val) { IntPtr gchnk = CopyNativeObject(key, ForceRefKey()); IntPtr nk = GetNativePtr(gchnk, ForceRefKey()); IntPtr gchnv = CopyNativeObject(val, false); IntPtr nv = GetNativePtr(gchnv, false); IntPtr old = eina_hash_set(Handle, nk, nv); FreeNativeIndirection(gchnk, ForceRefKey()); FreeNativeIndirection(gchnv, false); if (OwnValue && old != IntPtr.Zero) { NativeFree(old); } } /// /// Accessor by key to the hash. /// Since EFL 1.23. /// public TValue this[TKey key] { get { return Find(key); } set { Set(key, value); } } /// /// Changes the keys of an entry in the hash. /// Since EFL 1.23. /// /// The current key with data. /// The new key with data. /// false in any case but success, true on success. public bool Move(TKey key_old, TKey key_new) { IntPtr gchnko = CopyNativeObject(key_old, ForceRefKey()); IntPtr nko = GetNativePtr(gchnko, ForceRefKey()); IntPtr gchnk = CopyNativeObject(key_new, ForceRefKey()); IntPtr nk = GetNativePtr(gchnk, ForceRefKey()); // var nk_old = ManagedToNativeAllocRef(key_old); // var nk_new = ManagedToNativeAllocRef(key_new, true); var r = eina_hash_move(Handle, nko, nk); FreeNativeIndirection(gchnko, ForceRefKey()); FreeNativeIndirection(gchnk, ForceRefKey()); // NativeFreeRef(nk_old, OwnKey && r); // NativeFreeRef(nk_new, !r); return r; } /// /// Frees the hash buckets. /// Since EFL 1.23. /// public void FreeBuckets() { eina_hash_free_buckets(Handle); } /// /// Returns the number of entries in the hash. /// Since EFL 1.23. /// /// The number of entries, 0 on error. public int Population() { return eina_hash_population(Handle); } /// /// Gets an Iterator for keys. /// Since EFL 1.23. /// public Eina.Iterator Keys() { return new Eina.Iterator(EinaHashIteratorKeyNew(Handle), true); } /// /// Gets An Iterator for values. /// Since EFL 1.23. /// public Eina.Iterator Values() { return new Eina.Iterator(eina_hash_iterator_data_new(Handle), true); } /// /// Gets an Iterator for hask. /// Since EFL 1.23. /// public IEnumerator> GetEnumerator() { IntPtr itr = eina_hash_iterator_tuple_new(Handle); try { for (IntPtr tuplePtr; eina_iterator_next(itr, out tuplePtr);) { var tuple = Marshal.PtrToStructure(tuplePtr); IntPtr ikey = IndirectNative(tuple.key, ForceRefKey()); var key = NativeToManaged(ikey); var val = NativeToManaged(tuple.data); yield return new KeyValuePair(key, val); } } finally { eina_iterator_free(itr); } } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return this.GetEnumerator(); } } }