#region License /* Copyright 2015 Joe Osborne * * This file is part of RingBuffer. * * RingBuffer is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * RingBuffer is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with RingBuffer. If not, see . */ #endregion using System; using System.Collections; using System.Collections.Generic; namespace RingBuffer { /// /// A generic ring buffer with fixed capacity. /// /// The type of data stored in the buffer public class RingBuffer : IEnumerable, IEnumerable, ICollection, ICollection { protected int head = 0; protected int tail = 0; protected int size = 0; protected T[] buffer; private bool allowOverflow; public bool AllowOverflow { get { return allowOverflow; } } /// /// The total number of elements the buffer can store (grows). /// public int Capacity { get { return buffer.Length; } } /// /// The number of elements currently contained in the buffer. /// public int Size { get { return size; } } /// /// Retrieve the next item from the buffer. /// /// The oldest item added to the buffer. public T Get() { if(size == 0) throw new System.InvalidOperationException("Buffer is empty."); T _item = buffer[head]; head = (head + 1) % Capacity; size--; return _item; } /// /// Adds an item to the end of the buffer. /// /// The item to be added. public void Put(T item) { // If tail & head are equal and the buffer is not empty, assume // that it will overflow and throw an exception. if(tail == head && size != 0) { if(allowOverflow) { addToBuffer(item, true); } else { throw new System.InvalidOperationException("The RingBuffer is full"); } } // If the buffer will not overflow, just add the item. else { addToBuffer(item, false); } } public void Put(T[] item) { foreach (var t in item) { Put(t); } } protected void addToBuffer(T toAdd, bool overflow) { if(overflow) { head = (head + 1) % Capacity; } else { size++; } buffer[tail] = toAdd; tail = (tail + 1) % Capacity; } #region Constructors // Default capacity is 4, default overflow behavior is false. public RingBuffer() : this(4) { } public RingBuffer(int capacity) : this(capacity, false) { } public RingBuffer(int capacity, bool overflow) { buffer = new T[capacity]; allowOverflow = overflow; } #endregion #region IEnumerable Members public IEnumerator GetEnumerator() { int _index = head; for(int i = 0; i < size; i++, _index = (_index + 1) % Capacity) { yield return buffer[_index]; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return (IEnumerator)GetEnumerator(); } #endregion #region ICollection Members public int Count { get { return size; } } public bool IsReadOnly { get { return false; } } public void Add(T item) { Put(item); } /// /// Determines whether the RingBuffer contains a specific value. /// /// The value to check the RingBuffer for. /// True if the RingBuffer contains /// , false if it does not. /// public bool Contains(T item) { EqualityComparer comparer = EqualityComparer.Default; int _index = head; for(int i = 0; i < size; i++, _index = (_index + 1) % Capacity) { if(comparer.Equals(item, buffer[_index])) return true; } return false; } /// /// Removes all items from the RingBuffer. /// public void Clear() { for(int i = 0; i < Capacity; i++) { buffer[i] = default(T); } head = 0; tail = 0; size = 0; } /// /// Copies the contents of the RingBuffer to /// starting at . /// /// The array to be copied to. /// The index of /// where the buffer should begin copying to. public void CopyTo(T[] array, int arrayIndex) { int _index = head; for(int i = 0; i < size; i++, arrayIndex++, _index = (_index + 1) % Capacity) { array[arrayIndex] = buffer[_index]; } } /// /// Removes from the buffer. /// /// /// True if was found and /// successfully removed. False if was not /// found or there was a problem removing it from the RingBuffer. /// public bool Remove(T item) { int _index = head; int _removeIndex = 0; bool _foundItem = false; EqualityComparer _comparer = EqualityComparer.Default; for(int i = 0; i < size; i++, _index = (_index + 1) % Capacity) { if(_comparer.Equals(item, buffer[_index])) { _removeIndex = _index; _foundItem = true; break; } } if(_foundItem) { T[] _newBuffer = new T[size - 1]; _index = head; bool _pastItem = false; for(int i = 0; i < size - 1; i++, _index = (_index + 1) % Capacity) { if(_index == _removeIndex) { _pastItem = true; } if(_pastItem) { _newBuffer[_index] = buffer[(_index + 1) % Capacity]; } else { _newBuffer[_index] = buffer[_index]; } } size--; buffer = _newBuffer; return true; } return false; } #endregion #region ICollection Members /// /// Gets an object that can be used to synchronize access to the /// RingBuffer. /// public Object SyncRoot { get { return this; } } /// /// Gets a value indicating whether access to the RingBuffer is /// synchronized (thread safe). /// public bool IsSynchronized { get { return false; } } /// /// Copies the elements of the RingBuffer to , /// starting at a particular Array . /// /// The one-dimensional Array that is the /// destination of the elements copied from RingBuffer. The Array must /// have zero-based indexing. /// The zero-based index in /// at which copying begins. void ICollection.CopyTo(Array array, int index) { CopyTo((T[])array, index); } #endregion } }