using System; using System.Collections.Generic; using System.Runtime.InteropServices; namespace UnityEngine.Formats.Alembic.Sdk { class PinnedObject : IDisposable { T m_data; GCHandle m_gch; public PinnedObject(T data) { m_data = data; m_gch = GCHandle.Alloc(m_data, GCHandleType.Pinned); } public T Object { get { return m_data; } } public IntPtr Pointer { get { return m_gch.AddrOfPinnedObject(); } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { if (m_gch.IsAllocated) m_gch.Free(); } } public static implicit operator IntPtr(PinnedObject v) { return v == null ? IntPtr.Zero : v.Pointer; } internal static IntPtr ToIntPtr(PinnedObject v) { return v; } } class PinnedArray : IDisposable, IEnumerable where T : struct { T[] m_data; GCHandle m_gch; public PinnedArray(int size) { m_data = new T[size]; m_gch = GCHandle.Alloc(m_data, GCHandleType.Pinned); } public PinnedArray(T[] data, bool clone = false) { if (data == null) { return; } m_data = clone ? (T[])data.Clone() : data; m_gch = GCHandle.Alloc(m_data, GCHandleType.Pinned); } public int Length { get { return m_data.Length; } } public T this[int i] { get { return m_data[i]; } set { m_data[i] = value; } } public T[] GetArray() { return m_data; } public IntPtr Pointer { get { return m_data.Length == 0 ? IntPtr.Zero : m_gch.AddrOfPinnedObject(); } } public PinnedArray Clone() { return new PinnedArray((T[])m_data.Clone()); } public bool Assign(T[] source) { if (source != null && m_data.Length == source.Length) { System.Array.Copy(source, m_data, m_data.Length); return true; } return false; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { if (m_gch.IsAllocated) m_gch.Free(); } } public IEnumerator GetEnumerator() { return (IEnumerator)m_data.GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } public static implicit operator IntPtr(PinnedArray v) { return v == null ? IntPtr.Zero : v.Pointer; } internal static IntPtr ToIntPtr(PinnedArray v) { return v; } } #region dirty internal static class PinnedListImpl { class ListData { public object items; public int size; } [StructLayout(LayoutKind.Explicit)] struct Caster { [FieldOffset(0)] public object list; [FieldOffset(0)] public ListData data; } internal static T[] GetInternalArray(List list) where T : struct { if (list == null) { return null; } var caster = new Caster(); caster.list = list; return (T[])caster.data.items; } internal static List CreateIntrusiveList(T[] data) where T : struct { if (data == null) { return null; } var ret = new List(); var caster = new Caster(); caster.list = ret; caster.data.items = data; caster.data.size = data.Length; return ret; } internal static void SetCount(List list, int count) where T : struct { if (list == null) { return; } var caster = new Caster(); caster.list = list; caster.data.size = count; } } #endregion // Pinned"List" but assume size is fixed (== functionality is same as PinnedArray). // this class is intended to pass to Mesh.GetNormals(), Mesh.SetNormals(), and C++ functions. class PinnedList : IDisposable, IEnumerable where T : struct { List m_list; T[] m_data; GCHandle m_gch; public PinnedList(int size = 0) { m_data = new T[size]; m_list = PinnedListImpl.CreateIntrusiveList(m_data); m_gch = GCHandle.Alloc(m_data, GCHandleType.Pinned); } public PinnedList(T[] data, bool clone = false) { if (data == null) { return; } m_data = clone ? (T[])data.Clone() : data; m_list = PinnedListImpl.CreateIntrusiveList(m_data); m_gch = GCHandle.Alloc(m_data, GCHandleType.Pinned); } public PinnedList(List data, bool clone = false) { m_list = clone ? new List(data) : data; m_data = PinnedListImpl.GetInternalArray(m_list); m_gch = GCHandle.Alloc(m_data, GCHandleType.Pinned); } public int Capacity { get { return m_data.Length; } } public int Count { get { return m_list.Count; } } public T this[int i] { get { return m_data[i]; } set { m_data[i] = value; } } public T[] GetArray() { return m_data; } public List List { get { return m_list; } } public IntPtr Pointer { get { return Count == 0 ? IntPtr.Zero : m_gch.AddrOfPinnedObject(); } } public void LockList(Action> body) { if (body == null) { return; } if (m_gch.IsAllocated) m_gch.Free(); body(m_list); m_data = PinnedListImpl.GetInternalArray(m_list); m_gch = GCHandle.Alloc(m_data, GCHandleType.Pinned); } public void Resize(int size) { if (size > m_data.Length) { LockList(l => { l.Capacity = size; }); } PinnedListImpl.SetCount(m_list, size); } public void ResizeDiscard(int size) { if (size > m_data.Length) { if (m_gch.IsAllocated) m_gch.Free(); m_data = new T[size]; m_list = PinnedListImpl.CreateIntrusiveList(m_data); m_gch = GCHandle.Alloc(m_data, GCHandleType.Pinned); } else { PinnedListImpl.SetCount(m_list, size); } } public void Clear() { if (m_data.Length > 0) PinnedListImpl.SetCount(m_list, 0); } public PinnedList Clone() { return new PinnedList(m_list, true); } public void Assign(T[] source) { if (source == null) { return; } ResizeDiscard(source.Length); System.Array.Copy(source, m_data, source.Length); } public void Assign(List sourceList) { if (sourceList == null) { return; } var sourceData = PinnedListImpl.GetInternalArray(sourceList); var count = sourceList.Count; ResizeDiscard(count); System.Array.Copy(sourceData, m_data, count); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { if (m_gch.IsAllocated) m_gch.Free(); } } public IEnumerator GetEnumerator() { return (IEnumerator)m_data.GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } public static implicit operator IntPtr(PinnedList v) { return v == null ? IntPtr.Zero : v.Pointer; } internal static IntPtr ToIntPtr(PinnedList v) { return v; } } }