/* * 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 System.Diagnostics.Contracts; using static Eina.TraitFunctions; using static Eina.ListNativeFunctions; using Eina.Callbacks; namespace Eina { [EditorBrowsable(EditorBrowsableState.Never)] internal static class ListNativeFunctions { [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_append(IntPtr list, IntPtr data); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_prepend(IntPtr list, IntPtr data); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_append_relative(IntPtr list, IntPtr data, IntPtr relative); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_append_relative_list(IntPtr list, IntPtr data, IntPtr relative); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_prepend_relative(IntPtr list, IntPtr data, IntPtr relative); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_prepend_relative_list(IntPtr list, IntPtr data, IntPtr relative); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_sorted_insert(IntPtr list, IntPtr func, IntPtr data); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_remove(IntPtr list, IntPtr data); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_remove_list(IntPtr list, IntPtr remove_list); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_promote_list(IntPtr list, IntPtr move_list); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_demote_list(IntPtr list, IntPtr move_list); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_data_find(IntPtr list, IntPtr data); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_data_find_list(IntPtr list, IntPtr data); [DllImport(efl.Libs.Eina)] [return: MarshalAs(UnmanagedType.U1)] internal static extern bool eina_list_move(ref IntPtr to, ref IntPtr from, IntPtr data); [DllImport(efl.Libs.Eina)] [return: MarshalAs(UnmanagedType.U1)] internal static extern bool eina_list_move_list(ref IntPtr to, ref IntPtr from, IntPtr data); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_free(IntPtr list); [DllImport(efl.Libs.CustomExports)] internal static extern void efl_mono_thread_safe_eina_list_free(IntPtr list); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_nth(IntPtr list, uint n); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_nth_list(IntPtr list, uint n); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_reverse(IntPtr list); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_reverse_clone(IntPtr list); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_clone(IntPtr list); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_sort(IntPtr list, uint limit, IntPtr func); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_shuffle(IntPtr list, IntPtr func); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_merge(IntPtr left, IntPtr right); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_sorted_merge(IntPtr left, IntPtr right, IntPtr func); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_split_list(IntPtr list, IntPtr relative, ref IntPtr right); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_search_sorted_near_list(IntPtr list, IntPtr func, IntPtr data, IntPtr result_cmp); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_search_sorted_list(IntPtr list, IntPtr func, IntPtr data); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_search_sorted(IntPtr list, IntPtr func, IntPtr data); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_search_unsorted_list(IntPtr list, IntPtr func, IntPtr data); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_search_unsorted(IntPtr list, IntPtr func, IntPtr data); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_iterator_new(IntPtr list); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_iterator_reversed_new(IntPtr list); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_list_accessor_new(IntPtr list); [DllImport(efl.Libs.Eina)] internal static extern int eina_list_data_idx(IntPtr list, IntPtr data); [DllImport(efl.Libs.CustomExports)] internal static extern IntPtr eina_list_last_custom_export_mono(IntPtr list); [DllImport(efl.Libs.CustomExports)] internal static extern IntPtr eina_list_next_custom_export_mono(IntPtr list); [DllImport(efl.Libs.CustomExports)] internal static extern IntPtr eina_list_prev_custom_export_mono(IntPtr list); [DllImport(efl.Libs.CustomExports)] internal static extern IntPtr eina_list_data_get_custom_export_mono(IntPtr list); [DllImport(efl.Libs.CustomExports)] internal static extern IntPtr eina_list_data_set_custom_export_mono(IntPtr list, IntPtr data); [DllImport(efl.Libs.CustomExports)] internal static extern uint eina_list_count_custom_export_mono(IntPtr list); [DllImport(efl.Libs.CustomExports)] internal static extern IntPtr eina_list_last_data_get_custom_export_mono(IntPtr list); } /// Native wrapper around a linked list of items. /// Since EFL 1.23. /// [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification="This is a generalized container mapping the native one.")] public class List : IList, IEnumerable, IDisposable { [EditorBrowsable(EditorBrowsableState.Never)] public IntPtr Handle {get; set; } = IntPtr.Zero; /// Whether this managed list owns the native one. /// Since EFL 1.23. /// internal bool Own { get; set; } /// Whether the native list wrapped owns the content it points to. /// Since EFL 1.23. /// internal bool OwnContent { get; set; } /// Delegate for comparing two elements of this list. /// Since EFL 1.23. /// /// First element. /// Second element. /// -1, 0 or 1 for respectively smaller, equal or larger. public delegate int Compare(T a, T b); public bool IsReadOnly { get => !OwnContent; } /// The number of elements in this list. /// Since EFL 1.24. /// public int Count { get => (int)eina_list_count_custom_export_mono(Handle); } private void InitNew() { Handle = IntPtr.Zero; Own = true; OwnContent = true; } private IntPtr InternalLast() => eina_list_last_custom_export_mono(Handle); private static IntPtr InternalNext(IntPtr list) => eina_list_next_custom_export_mono(list); private static IntPtr InternalPrev(IntPtr list) => eina_list_prev_custom_export_mono(list); private static IntPtr InternalDataGet(IntPtr list) => eina_list_data_get_custom_export_mono(list); private static IntPtr InternalDataSet(IntPtr list, IntPtr data) => eina_list_data_set_custom_export_mono(list, data); private IntPtr GetNative(int idx, Func f) { if (!(0 <= idx && idx < Count)) { throw new ArgumentOutOfRangeException(nameof(idx), $"{nameof(idx)} cannot be negative, neither smaller than {nameof(Count)}"); } var ele = f(Handle, (uint)idx); if (ele == IntPtr.Zero) { throw new ArgumentOutOfRangeException(nameof(idx), $"There is no position {nameof(idx)}"); } return ele; } private IntPtr GetNativeDataPointer(int idx) => GetNative(idx, eina_list_nth); private IntPtr GetNativePointer(int idx) => GetNative(idx, eina_list_nth_list); private void RequireWritable() { if (IsReadOnly) { throw new NotSupportedException("Cannot modify read-only container."); } } private void CheckOwnerships() { if ((Own == false) && (OwnContent == true)) { throw new InvalidOperationException(nameof(Own) + "/" + nameof(OwnContent)); } } private void DeleteData(IntPtr ele) { if (OwnContent) { NativeFree(InternalDataGet(ele)); } } private U LoopingThrough(T val, Func f1, Func f2) { int i = 0; IntPtr cur = Handle; for (; cur != IntPtr.Zero; ++i, cur = InternalNext(cur)) { if (NativeToManaged(InternalDataGet(cur)).Equals(val)) { return f1(i); } } return f2(); } /// Creates a new empty list. /// Since EFL 1.23. /// public List() => InitNew(); /// Creates a new list wrapping the given handle. [EditorBrowsable(EditorBrowsableState.Never)] public List(IntPtr handle, bool own) { Handle = handle; Own = own; OwnContent = own; CheckOwnerships(); } /// Creates a new list wrapping the given handle. [EditorBrowsable(EditorBrowsableState.Never)] public List(IntPtr handle, bool own, bool ownContent) { Handle = handle; Own = own; OwnContent = ownContent; CheckOwnerships(); } /// Finalizes this list. /// Since EFL 1.23. /// ~List() => Dispose(false); /// Disposes of this list. /// Since EFL 1.23. /// /// Whether this was called from the finalizer (false) or from the /// method. protected virtual void Dispose(bool disposing) { IntPtr h = Handle; Handle = IntPtr.Zero; if (h == IntPtr.Zero) { return; } for (IntPtr curr = h; curr != IntPtr.Zero; curr = InternalNext(curr)) { if (!OwnContent) { break; } DeleteData(curr); } if (Own) { if (disposing) { eina_list_free(h); } else { efl_mono_thread_safe_eina_list_free(h); } } } /// Disposes of this list. /// Since EFL 1.23. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// Disposes of this list. /// Since EFL 1.23. /// public void Free() => Dispose(); /// Sets whether this wrapper should own the native list or not. /// Since EFL 1.23. /// /// If the hash own for all ownerships. internal void SetOwnership(bool ownAll) { Own = ownAll; OwnContent = ownAll; CheckOwnerships(); } /// Sets whether this wrapper should own the native list and /// its content or not. /// Since EFL 1.23. /// /// If own the object. /// If own the content's object. internal void SetOwnership(bool own, bool ownContent) { Own = own; OwnContent = ownContent; CheckOwnerships(); } /// Appends val to the list. /// Since EFL 1.23. /// /// The item to be appended. public void Append(T val) { RequireWritable(); Handle = eina_list_append(Handle, ManagedToNativeAlloc(val)); } /// Prepends val to the list. /// Since EFL 1.23. /// /// The item to be prepended. public void Prepend(T val) { RequireWritable(); Handle = eina_list_prepend(Handle, ManagedToNativeAlloc(val)); } /// Inserts val in the list in a sorted manner. /// It presumes the list is already sorted. /// Since EFL 1.23. /// /// The item to be inserted. public void SortedInsert(T val) { RequireWritable(); Handle = eina_list_sorted_insert(Handle, EinaCompareCb(), ManagedToNativeAlloc(val)); } /// Inserts val in the list in a sorted manner with the /// given compareCb for element comparison. /// It presumes the list is already sorted. /// Since EFL 1.23. /// /// The function to compare two elements of the list. /// The item to be inserted. public void SortedInsert(Compare compareCb, T val) { RequireWritable(); Handle = eina_list_sorted_insert(Handle, Marshal.GetFunctionPointerForDelegate(GetNativeCompareCb(compareCb)), ManagedToNativeAlloc(val)); } /// Sorts limit elements in this list inplace. /// Since EFL 1.23. /// /// The max number of elements to be sorted. public void Sort(int limit = 0) { RequireWritable(); Handle = eina_list_sort(Handle, (uint)limit, EinaCompareCb()); } /// Sorts all elements in this list inplace. /// Since EFL 1.23. /// /// The function to compare two elements of the list. public void Sort(Compare compareCb) { RequireWritable(); Handle = eina_list_sort(Handle, (uint)0, Marshal.GetFunctionPointerForDelegate(GetNativeCompareCb(compareCb))); } /// Sorts limit elements in this list inplace. /// Since EFL 1.23. /// /// The max number of elements to be sorted. /// The function to compare two elements of the list. public void Sort(int limit, Compare compareCb) { RequireWritable(); Handle = eina_list_sort(Handle, (uint)limit, Marshal.GetFunctionPointerForDelegate(GetNativeCompareCb(compareCb))); } private Eina.Callbacks.EinaCompareCb GetNativeCompareCb(Compare managedCb) => (IntPtr a, IntPtr b) => managedCb(NativeToManaged(a), NativeToManaged(b)); /// Returns the nth element of this list. Due to marshalling details, the returned element /// may be a different C# object from the one you used to append. /// Since EFL 1.23. /// /// The 0-based index to be retrieved. /// The value in the specified element. public T Nth(int n) => NativeToManaged(GetNativeDataPointer(n)); /// Sets the data at the idx position. /// Since EFL 1.23. /// /// The 0-based index to be set. /// The value to be inserted. internal void DataSet(int idx, T val) { RequireWritable(); IntPtr pos = GetNativePointer(idx); DeleteData(pos); InternalDataSet(pos, ManagedToNativeAlloc(val)); } /// Accessor for the data at the idx position. /// Since EFL 1.23. /// /// The 0-based index to be get/set. public T this[int idx] { get { return Nth(idx); } set { DataSet(idx, value); } } /// Returns the data at the last list element. /// Since EFL 1.23. /// /// The value contained in the last list position. public T LastDataGet() => NativeToManaged(eina_list_last_data_get_custom_export_mono(Handle)); /// Reverses this list in place. /// Since EFL 1.23. /// /// A reference to this object. public List Reverse() { RequireWritable(); Handle = eina_list_reverse(Handle); return this; } /// Randomly shuffles this list in place. /// Since EFL 1.23. /// public void Shuffle() { RequireWritable(); Handle = eina_list_shuffle(Handle, IntPtr.Zero); } /// Gets a C# array of the elements in this list. /// Since EFL 1.23. /// /// A managed array of the elements. public T[] ToArray() { var managed = new T[Count]; int i = 0; for (IntPtr curr = Handle; curr != IntPtr.Zero; curr = InternalNext(curr), ++i) { managed[i] = NativeToManaged(InternalDataGet(curr)); } return managed; } /// Appends the given array of elements to this list. /// Since EFL 1.23. /// /// The values to be appended. public void Append(T[] values) { Contract.Requires(values != null, nameof(values)); RequireWritable(); foreach (T v in values) { Append(v); } } /// Gets an iterator that iterates this list in normal order. /// Since EFL 1.23. /// /// The iterator. public Eina.Iterator GetIterator() => new Eina.Iterator(eina_list_iterator_new(Handle), true); /// Gets an iterator that iterates this list in reverse order. /// Since EFL 1.23. /// /// The iterator. public Eina.Iterator GetReversedIterator() => new Eina.Iterator(eina_list_iterator_reversed_new(Handle), true); /// Gets an enumerator into this list. /// Since EFL 1.23. /// /// The enumerator. public IEnumerator GetEnumerator() { for (IntPtr curr = Handle; curr != IntPtr.Zero; curr = InternalNext(curr)) { yield return NativeToManaged(InternalDataGet(curr)); } } /// Gets an enumerator into this list. /// Since EFL 1.23. /// /// The enumerator. System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => this.GetEnumerator(); /// Gets an Accessor for this List. /// Since EFL 1.23. /// /// The accessor. public Eina.Accessor GetAccessor() => new Eina.Accessor(eina_list_accessor_new(Handle), Ownership.Managed); /// /// Removes the first occurrence of a specific object. /// Since EFL 1.24. /// /// The object to remove. public bool Remove(T val) { RequireWritable(); var prev_count = Count; var deleted = LoopingThrough(val, (i) => { RemoveAt(i); return true; }, () => false); return deleted && (prev_count - 1 == Count); } /// /// Adds an item. /// Since EFL 1.24. /// /// The object to add. public void Add(T val) => Append(val); /// /// Removes all items. /// Since EFL 1.24. /// public void Clear() { RequireWritable(); for (; Handle != IntPtr.Zero;) { Handle = eina_list_remove_list(Handle, Handle); } } /// /// Determines whether the contains a specific value. /// Since EFL 1.24. /// /// The object to locate. public bool Contains(T val) => LoopingThrough(val, (i) => true, () => false); /// /// Determines the index of a specific item. /// Since EFL 1.24. /// /// The object to locate. public int IndexOf(T val) => LoopingThrough(val, (i) => i, () => -1); /// /// Inserts an item to the at the specified index. /// Since EFL 1.24. /// /// The zero-based index at which item should be inserted. /// The object to insert. public void Insert(int idx, T val) { RequireWritable(); if (idx == 0) { Prepend(val); return; } if (idx == Count) { Append(val); return; } if (idx < 0) { throw new ArgumentOutOfRangeException(nameof(idx), $"{nameof(idx)} cannot be negative."); } if (Count < idx) { throw new ArgumentOutOfRangeException(nameof(idx), $"{nameof(idx)} cannot be greater than {nameof(Count)} + 1."); } Handle = eina_list_prepend_relative_list(Handle, ManagedToNativeAlloc(val), GetNativePointer(idx)); } /// /// Removes the item at the specified index. /// Since EFL 1.24. /// /// The zero-based index of the item to remove. public void RemoveAt(int idx) { RequireWritable(); var ele = GetNativePointer(idx); DeleteData(ele); Handle = eina_list_remove_list(Handle, ele); } /// /// Copies the elements of the to an /// , starting at a particular index. /// Since EFL 1.24. /// /// The one-dimensional that is the /// destination of the elements copied from . /// The must have zero-based indexing. /// The zero-based index in array at which copying /// begins. public void CopyTo(T[] array, int arrayIndex) => ToArray().CopyTo(array, arrayIndex); } }