/// <summary> /// Not allowed to set data larger than the buffer size /// </summary> public void Put(T[] dataToSet) { var sourceDataLength = dataToSet.Length; if (sourceDataLength > _capacity) { throw new ArgumentException("Amount of data that is trying to be set is larger than the capacity of this buffer"); } bool isOverflowed; int nextTail; lock (_lockObj) { // Calculate the amount of data (including the data being read at the moment, if a read is ongoing) var capacityInUse = BufferLogicHelper.CalculateUsedCapacity(_isEmpty, _head, _tail, _capacity); if (BufferLogicHelper.WillDataBeOverwritten(sourceDataLength, capacityInUse, _capacity)) { // We just throw away a chunk if there is no room LostData += sourceDataLength; } // Calculate the next tail, this way we also know whether we are overflowing nextTail = BufferLogicHelper.CalculateNextIndex(_tail, sourceDataLength, _capacity, out isOverflowed); } if (isOverflowed) { // Data spans across the end and beginning of the storage array, we need to do two copies var itemsToWriteAtEnd = _capacity - _tail; Array.Copy(dataToSet, 0, _storage, _tail, itemsToWriteAtEnd); var itemsToWriteAtBeginning = sourceDataLength - itemsToWriteAtEnd; Array.Copy(dataToSet, itemsToWriteAtEnd, _storage, 0, itemsToWriteAtBeginning); } else { Array.Copy(dataToSet, 0, _storage, _tail, sourceDataLength); } lock (_lockObj) { _tail = nextTail; // We just added data, the storage can never be empty now _isEmpty = false; // Check if we need to set the event if (_notifyEventEnabled && !_notifyEvent.IsSet && BufferLogicHelper.CalculateUsedCapacity(_isEmpty, _head, _tail, _capacity) >= _notifyThreshold) { // There is not enough data anymore, reset the event _notifyEvent.Set(); } } }
public T[] Get() { bool isOverflowed; int nextHead; lock (_lockObj) { if (BufferLogicHelper.CalculateUsedCapacity(_isEmpty, _head, _tail, _capacity) < _notifyThreshold) { // We do not have enough data throw new Exception("A get should never be done when there is not enough available data! Use the NotifyEvent."); } nextHead = BufferLogicHelper.CalculateNextIndex(_head, _notifyThreshold, _capacity, out isOverflowed); } var getValues = new T[_notifyThreshold]; if (isOverflowed) { // Data spans across the end and beginning of the storage array, we need to do two copies var itemsToReadFromEnd = _capacity - _head; Array.Copy(_storage, _head, getValues, 0, itemsToReadFromEnd); var itemsToReadFromBeginning = _notifyThreshold - itemsToReadFromEnd; Array.Copy(_storage, 0, getValues, itemsToReadFromEnd, itemsToReadFromBeginning); } else { Array.Copy(_storage, _head, getValues, 0, _notifyThreshold); } lock (_lockObj) { _head = nextHead; // Check if we just read the last available data _isEmpty = _head == _tail; // Check if we need to reset the event if (_notifyEventEnabled && _notifyEvent.IsSet && BufferLogicHelper.CalculateUsedCapacity(_isEmpty, _head, _tail, _capacity) < _notifyThreshold) { // There is not enough data anymore, reset the event _notifyEvent.Reset(); } } return(getValues); }
public void Test_CalculateNextIndex() { // Simple start case Assert.AreEqual(2, BufferLogicHelper.CalculateNextIndex(0, 2, 10, out var isOverFlowed)); Assert.IsFalse(isOverFlowed); // Fully fill the end Assert.AreEqual(0, BufferLogicHelper.CalculateNextIndex(7, 3, 10, out isOverFlowed)); Assert.IsTrue(isOverFlowed); // Overflow Assert.AreEqual(2, BufferLogicHelper.CalculateNextIndex(7, 5, 10, out isOverFlowed)); Assert.IsTrue(isOverFlowed); }