using System; using System.Buffers; using System.Collections.Generic; using System.Threading; namespace RGB.NET.Core; /// /// Represents a generic update queue. /// /// The type of the key used to identify some data. /// The type of the data. public abstract class UpdateQueue : AbstractReferenceCounting, IUpdateQueue where TIdentifier : notnull { #region Properties & Fields private readonly Lock _dataLock = new(); private readonly IDeviceUpdateTrigger _updateTrigger; private readonly Dictionary _currentDataSet = []; /// public bool RequiresFlush { get; private set; } #endregion #region Constructors /// /// Initializes a new instance of the class. /// /// The causing this queue to update. protected UpdateQueue(IDeviceUpdateTrigger updateTrigger) { this._updateTrigger = updateTrigger; _updateTrigger.Starting += OnStartup; _updateTrigger.Update += OnUpdate; } #endregion #region Methods /// /// Event handler for the -event. /// /// The causing this update. /// provided by the trigger. protected virtual void OnUpdate(object? sender, CustomUpdateData customData) { (TIdentifier, TData)[] dataSet; Span<(TIdentifier, TData)> data; lock (_dataLock) { if (_currentDataSet.Count == 0) return; dataSet = ArrayPool<(TIdentifier, TData)>.Shared.Rent(_currentDataSet.Count); data = new Span<(TIdentifier, TData)>(dataSet)[.._currentDataSet.Count]; int i = 0; foreach ((TIdentifier key, TData value) in _currentDataSet) data[i++] = (key, value); _currentDataSet.Clear(); } RequiresFlush = !Update(data); ArrayPool<(TIdentifier, TData)>.Shared.Return(dataSet); } /// /// Event handler for the -event. /// /// The starting . /// provided by the trigger. protected virtual void OnStartup(object? sender, CustomUpdateData customData) { } /// /// Performs the update this queue is responsible for. /// /// The set of data that needs to be updated. protected abstract bool Update(ReadOnlySpan<(TIdentifier key, TData color)> dataSet); /// /// Sets or merges the provided data set in the current dataset and notifies the trigger that there is new data available. /// /// The set of data. // ReSharper disable once MemberCanBeProtected.Global public virtual void SetData(ReadOnlySpan<(TIdentifier, TData)> data) { if (data.Length == 0) return; lock (_dataLock) { foreach ((TIdentifier key, TData value) in data) _currentDataSet[key] = value; } _updateTrigger.TriggerHasData(); } /// /// Resets the current data set. /// public virtual void Reset() { lock (_dataLock) _currentDataSet.Clear(); } /// public virtual void Dispose() { _updateTrigger.Starting -= OnStartup; _updateTrigger.Update -= OnUpdate; Reset(); GC.SuppressFinalize(this); } #endregion } /// /// Represents a generic using an object as the key and a color as the value. /// public abstract class UpdateQueue : UpdateQueue, IUpdateQueue { #region Constructors /// protected UpdateQueue(IDeviceUpdateTrigger updateTrigger) : base(updateTrigger) { } #endregion }