/* * 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.ArrayNativeFunctions; namespace Eina { [EditorBrowsable(EditorBrowsableState.Never)] internal static class ArrayNativeFunctions { [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_array_new(uint step); [DllImport(efl.Libs.Eina)] internal static extern void eina_array_free(IntPtr array); [DllImport(efl.Libs.Eina)] internal static extern void eina_array_flush(IntPtr array); public delegate bool KeepCb(IntPtr data, IntPtr gdata); [DllImport(efl.Libs.Eina)] [return: MarshalAs(UnmanagedType.U1)] internal static extern bool eina_array_remove(IntPtr array, KeepCb keep, IntPtr gdata); [DllImport(efl.Libs.Eina)] [return: MarshalAs(UnmanagedType.U1)] internal static extern bool eina_array_push(IntPtr array, IntPtr data); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_array_iterator_new(IntPtr array); [DllImport(efl.Libs.Eina)] internal static extern IntPtr eina_array_accessor_new(IntPtr array); [DllImport(efl.Libs.Eina)] [return: MarshalAs(UnmanagedType.U1)] internal static extern bool eina_array_find(IntPtr array, IntPtr data, uint out_idx); [DllImport(efl.Libs.CustomExports)] internal static extern void eina_array_clean_custom_export_mono(IntPtr array); [DllImport(efl.Libs.CustomExports)] [return: MarshalAs(UnmanagedType.U1)] internal static extern bool eina_array_push_custom_export_mono(IntPtr array, IntPtr data); [DllImport(efl.Libs.CustomExports)] internal static extern IntPtr eina_array_pop_custom_export_mono(IntPtr array); [DllImport(efl.Libs.CustomExports)] internal static extern IntPtr eina_array_data_get_custom_export_mono(IntPtr array, uint idx); [DllImport(efl.Libs.CustomExports)] internal static extern void eina_array_data_set_custom_export_mono(IntPtr array, uint idx, IntPtr data); [DllImport(efl.Libs.CustomExports)] internal static extern uint eina_array_count_custom_export_mono(IntPtr array); [DllImport(efl.Libs.CustomExports)] [return: MarshalAs(UnmanagedType.U1)] internal static extern bool eina_array_foreach_custom_export_mono(IntPtr array, IntPtr cb, IntPtr fdata); [DllImport(efl.Libs.CustomExports)] internal static extern void eina_array_insert_at_custom_export_mono(IntPtr array, uint index, IntPtr data); } /// A container of contiguous allocated elements. /// Since EFL 1.23. /// [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification="This is a generalized container mapping the native one.")] public class Array : IList, IEnumerable, IDisposable { public const uint DefaultStep = 32; /// Pointer to the native buffer. [EditorBrowsable(EditorBrowsableState.Never)] public IntPtr Handle { get; set; } = IntPtr.Zero; /// Whether this wrapper owns the native buffer. /// Since EFL 1.23. /// internal bool Own { get; set; } /// Who is in charge of releasing the resources wrapped by /// this instance. /// Since EFL 1.23. /// internal bool OwnContent { get; set; } /// Gets the number of elements contained in the . /// Since EFL 1.24. /// public int Count { get => (int)eina_array_count_custom_export_mono(Handle); } /// /// Gets a value indicating whether the is read-only. /// Since EFL 1.24. /// /// It's the negative of . /// /// public bool IsReadOnly { get => !OwnContent; } private void InitNew(uint step) { Handle = eina_array_new(step); Own = true; OwnContent = true; if (Handle == IntPtr.Zero) { throw new SEHException("Could not alloc array"); } } internal bool InternalPush(IntPtr ele) => eina_array_push_custom_export_mono(Handle, ele); internal IntPtr InternalPop() => eina_array_pop_custom_export_mono(Handle); internal IntPtr InternalDataGet(int idx) => eina_array_data_get_custom_export_mono(Handle, CheckBounds(idx)); internal void InternalDataSet(int idx, IntPtr ele) => eina_array_data_set_custom_export_mono(Handle, CheckBounds(idx), ele); private uint CheckBounds(int idx) { if (!(0 <= idx && idx < Count)) { throw new ArgumentOutOfRangeException(nameof(idx), $"{nameof(idx)} is out of bounds."); } return (uint)idx; } private U LoopingThrough(T val, Func f1, Func f2) { for (int i = 0, count = Count; i < count; ++i) { if (NativeToManaged(InternalDataGet(i)).Equals(val)) { return f1(i); } } return f2(); } private void CheckOwnerships() { if ((Own == false) && (OwnContent == true)) { throw new InvalidOperationException(nameof(Own) + "/" + nameof(OwnContent)); } } private void RequireWritable() { if (IsReadOnly) { throw new NotSupportedException("This object's instance is read only."); } } private void DeleteData(IntPtr ele) { if (OwnContent) { NativeFree(ele); } } /// /// Create a new array. /// Since EFL 1.23. /// public Array() { InitNew(DefaultStep); } /// /// Create a new array. /// Since EFL 1.23. /// /// Step size of the array. public Array(uint step) { InitNew(step); } /// /// Create a new array. /// /// The native handle to be wrapped. /// Whether this wrapper owns the native handle. [EditorBrowsable(EditorBrowsableState.Never)] public Array(IntPtr handle, bool own) { Handle = (handle != IntPtr.Zero) ? handle : throw new ArgumentNullException(nameof(handle), $"{nameof(Handle)} can't be null"); Own = own; OwnContent = own; CheckOwnerships(); } /// /// Create a new array. /// /// The native array to be wrapped. /// Whether this wrapper owns the native array. /// For compatibility with other EFL# containers. [EditorBrowsable(EditorBrowsableState.Never)] public Array(IntPtr handle, bool own, bool ownContent) { Handle = (handle != IntPtr.Zero) ? handle : throw new ArgumentNullException(nameof(handle), $"{nameof(Handle)} can't be null"); Own = own; OwnContent = ownContent; CheckOwnerships(); } /// /// Finalizer to be called from the Garbage Collector. /// Since EFL 1.23. /// ~Array() { Dispose(false); } /// Disposes of this wrapper, releasing the native array 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; } for (int len = (int)eina_array_count_custom_export_mono(h), i = 0; i < len; ++i) { if (!OwnContent) { break; } DeleteData(eina_array_data_get_custom_export_mono(h, (uint)i)); } if (Own) { if (disposing) { eina_array_free(h); } else { Efl.Eo.Globals.ThreadSafeFreeCbExec(eina_array_free, h); } } } /// Releases the native resources held by this instance. /// Since EFL 1.23. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// Releases the native resources held by this instance. /// Since EFL 1.23. /// public void Free() { Dispose(); } private void FreeElementsIfOwned() { if (IsReadOnly) { throw new NotSupportedException("This object's instance is read only."); } for (int i = 0, count = Count; i < count; ++i) { DeleteData(InternalDataGet(i)); } } /// /// Clears an array's elements and deallocates the memory. /// Since EFL 1.23. /// public void Clean() { FreeElementsIfOwned(); eina_array_clean_custom_export_mono(Handle); } /// /// Clears an array's elements and deallocates the memory. /// public void Flush() { FreeElementsIfOwned(); eina_array_flush(Handle); } internal void SetOwnership(bool ownAll) { Own = ownAll; OwnContent = ownAll; CheckOwnerships(); } internal void SetOwnership(bool own, bool ownContent) { Own = own; OwnContent = ownContent; CheckOwnerships(); } /// /// Inserts the element of the array at the end. /// Since EFL 1.23. /// /// The value of the element to be inserted. public bool Push(T val) { RequireWritable(); IntPtr ele = ManagedToNativeAlloc(val); var r = InternalPush(ele); if (!r) { NativeFree(ele); } return r; } // TODO ??? // public void Add(T val) // { // if (!Push(val)) // { // throw; // } // } /// /// Returns the element of the array at the end. /// Since EFL 1.23. /// /// The element at the end position. public T Pop() { RequireWritable(); IntPtr ele = InternalPop(); var r = NativeToManaged(ele); if (ele != IntPtr.Zero) { DeleteData(ele); } return r; } /// /// Returns the element of the array at the specified position. /// Since EFL 1.23. /// /// The position of the desired element. /// The element at the specified position public T DataGet(int idx) => NativeToManaged(InternalDataGet(idx)); /// /// Returns the element of the array at the specified position. /// Since EFL 1.23. /// /// The position of the desired element. /// The element at the specified position public T At(int idx) => DataGet(idx); /// /// Replaces the element at the specified position. /// Since EFL 1.23. /// /// The position of the desired element. /// The value of the element to be inserted. internal void DataSet(int idx, T val) { RequireWritable(); IntPtr ele = InternalDataGet(idx); if (ele != IntPtr.Zero) { DeleteData(ele); } InternalDataSet(idx, ManagedToNativeAlloc(val)); } /// /// Accessor by index to the elements of this list. /// Since EFL 1.23. /// public T this[int idx] { get { return DataGet(idx); } set { DataSet(idx, value); } } /// /// Returns a array containing all of the elements in proper sequence. /// Since EFL 1.23. /// /// A array public T[] ToArray() { int len = Count; var managed = new T[len]; for (int i = 0; i < len; ++i) { managed[i] = DataGet(i); } return managed; } /// /// Appends all elements at the end of array. /// Since EFL 1.23. /// public bool Append(T[] values) { Contract.Requires(values != null, nameof(values)); foreach (T v in values) { if (!Push(v)) { return false; } } return true; } /// Gets an Iterator for this Array. /// Since EFL 1.23. /// public Eina.Iterator GetIterator() => new Eina.Iterator(eina_array_iterator_new(Handle), true); /// Gets an Enumerator for this Array. /// Since EFL 1.23. /// public IEnumerator GetEnumerator() { for (int i = 0, count = Count; i < count; ++i) { yield return DataGet(i); } } /// Gets an Enumerator for this Array. /// Since EFL 1.23. /// System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => this.GetEnumerator(); /// Gets an Accessor for this Array. /// Since EFL 1.23. /// public Eina.Accessor GetAccessor() => new Eina.Accessor(eina_array_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) => LoopingThrough(val, (i) => { RemoveAt(i); return true; }, () => false); /// /// Adds an item. /// Since EFL 1.24. /// /// The object to add. public void Add(T val) => Push(val); /// /// Removes all items. /// Since EFL 1.24. /// public void Clear() => Clean(); /// /// 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); /// /// 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); /// /// 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 index, T val) { RequireWritable(); if (index < 0) { throw new ArgumentOutOfRangeException(nameof(index), $"{nameof(index)} cannot be negative."); } if (Count < index) { throw new ArgumentOutOfRangeException(nameof(index), $"{nameof(index)} is greater than {nameof(Count)} + 1."); } if (index == Count) { Push(val); return; } eina_array_insert_at_custom_export_mono(Handle, (uint)index, ManagedToNativeAlloc(val)); } /// /// Removes the item at the specified index. /// Since EFL 1.24. /// /// The zero-based index of the item to remove. public void RemoveAt(int index) { RequireWritable(); var ele = InternalDataGet(index); DeleteData(ele); eina_array_remove(Handle, (data,gdata) => ele != data, IntPtr.Zero); } } }