using System; using System.Collections.Generic; using UnityEngine.Pool; namespace UnityEngine.Localization { /// /// Alternative to using a delegate/event. /// Designed to reduce the amount of memory allocations by allocating in blocks. /// /// /// /// To invoke do the following: /// /// try /// { /// m_ChangeHandler.LockForChanges(); /// var len = m_ChangeHandler.Length; /// if (len == 1) /// { /// m_ChangeHandler.SingleDelegate(value); /// } /// else if (len > 1) /// { /// var array = m_ChangeHandler.MultiDelegates; /// for (int i = 0; i < len; ++i) /// array[i](value); /// } /// } /// catch (Exception ex) /// { /// Debug.LogException(ex); /// } /// m_ChangeHandler.UnlockForChanges(); /// /// struct CallbackArray where TDelegate : Delegate { const int k_AllocationIncrement = 5; public TDelegate SingleDelegate => m_SingleDelegate; public TDelegate[] MultiDelegates => m_MultipleDelegates; public int Length => m_Length; TDelegate m_SingleDelegate; TDelegate[] m_MultipleDelegates; List m_AddCallbacks; List m_RemoveCallbacks; int m_Length; bool m_CannotMutateCallbacksArray; bool m_MutatedDuringCallback; public void Add(TDelegate callback, int capacityIncrement = k_AllocationIncrement) { if (callback == null) return; if (m_CannotMutateCallbacksArray) { if (m_AddCallbacks == null) m_AddCallbacks = ListPool.Get(); m_AddCallbacks.Add(callback); m_MutatedDuringCallback = true; return; } if (m_Length == 0) { m_SingleDelegate = callback; } else if (m_Length == 1) { m_MultipleDelegates = new TDelegate[capacityIncrement]; m_MultipleDelegates[0] = m_SingleDelegate; m_MultipleDelegates[1] = callback; m_SingleDelegate = null; } else { if (m_MultipleDelegates.Length == m_Length) Array.Resize(ref m_MultipleDelegates, m_Length + capacityIncrement); m_MultipleDelegates[m_Length] = callback; } ++m_Length; } public void RemoveByMovingTail(TDelegate callback) { if (callback == null) return; if (m_CannotMutateCallbacksArray) { if (m_RemoveCallbacks == null) m_RemoveCallbacks = ListPool.Get(); m_RemoveCallbacks.Add(callback); m_MutatedDuringCallback = true; return; } if (m_Length <= 1) { // m_SingleDelegate == callback creates GC if (Equals(m_SingleDelegate, callback)) { m_SingleDelegate = null; m_Length = 0; } } else { for (int i = 0; i < m_Length; ++i) { // m_MultipleDelegates[i] == callback creates GC if (Equals(m_MultipleDelegates[i], callback)) { m_MultipleDelegates[i] = m_MultipleDelegates[m_Length - 1]; --m_Length; break; } } if (m_Length == 1) { m_SingleDelegate = m_MultipleDelegates[0]; m_MultipleDelegates = null; } } } public void LockForChanges() { m_CannotMutateCallbacksArray = true; } public void UnlockForChanges() { m_CannotMutateCallbacksArray = false; if (m_MutatedDuringCallback) { // Process mutations that have happened while we were executing callbacks. if (m_AddCallbacks != null) { foreach (var cb in m_AddCallbacks) Add(cb); ListPool.Release(m_AddCallbacks); m_AddCallbacks = null; } if (m_RemoveCallbacks != null) { foreach (var cb in m_RemoveCallbacks) RemoveByMovingTail(cb); ListPool.Release(m_RemoveCallbacks); m_RemoveCallbacks = null; } m_MutatedDuringCallback = true; } } public void Clear() { m_SingleDelegate = null; m_MultipleDelegates = null; m_Length = 0; } } }