/*
* 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 static Eina.TraitFunctions;
using static Eina.IteratorNativeFunctions;
using static Eina.HashNativeFunctions;
using Eina.Callbacks;
namespace Eina
{
[StructLayout(LayoutKind.Sequential)]
[EditorBrowsable(EditorBrowsableState.Never)]
public struct HashTupleNative
{
public IntPtr key;
public IntPtr data;
public uint key_length;
}
[EditorBrowsable(EditorBrowsableState.Never)]
public 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.
///
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();
}
}
}