C# Circular Buffer

Circular buffer or cyclic buffer is a data structure in which you can consume buffering data streams. You can have a fixed size Circular buffer as well as a growing and shrinking Circular buffer. Even though Circular buffer is said to be used with multimedia applications, you can use circular buffer for several producer consumer problems. Benefit of using using Circular buffer is that underlying data structure which can be an array or list, is compact. Queue or stack can be implemented using a Circular buffer. Even though native implementation of Queue and Stack doesn’t use Circular buffer but Array, it is efficient to use a Circular buffer as the underlying data structure to implement a Queue. Again, you will gain the benefit of saving space and compact elements.

It s pretty straightforward to implement a Circular Buffer and there are couple ways of doing so one of which is using Count property of the collection, and another is to have 2 pointers only to keep track of start and end of the circular buffer.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace FiratAtagun.Common.Collections
{
    [DebuggerDisplay("Count = {Count}")]
    public class FixedCircularBuffer : IEnumerable
    {
        private const int DefaultCapacity = 5;
        private readonly int _capacity;
        private T[] _buffer;
        private int _count;
        private int _end;
        private int _start;

        public FixedCircularBuffer() : this(DefaultCapacity)
        {
        }

        public FixedCircularBuffer(int capacity)
        {
            _capacity = capacity;
            _buffer = new T[capacity];
            _start = _end = 0;
        }

        public FixedCircularBuffer(IEnumerable collection) : this(collection.Count())
        {
            foreach (var item in collection)
            {
                Add(item);
            }
        }

        public bool IsEmpty
        {
            get { return Count == 0; }
        }

        public bool IsFull
        {
            get { return Count == Capacity; }
        }

        public int Count
        {
            get { return _count; }
        }

        public int Capacity
        {
            get { return _capacity; }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        public void Add(T item)
        {
            if (IsFull)
            {
                _end = _start;
                if (_start == _capacity - 1)
                {
                    _start = _capacity - _start - 1;
                }
                else
                {
                    _start++;
                }
                _buffer[_end] = item;
            }
            else
            {
                if (++_end >= _capacity)
                {
                    _end = _end - _capacity;
                }

                _buffer[_end] = item;

                _count++;
            }
        }

        public T Get()
        {
            if (IsEmpty)
            {
                throw new InvalidOperationException("Buffer is Empty");
            }

            _count--;
            T item = _buffer[_start];
            _buffer[_start] = default(T);

            if (_start == _capacity - 1)
            {
                _start = _capacity - _start - 1;
            }
            else
            {
                _start++;
            }

            if (IsEmpty)
            {
                _end = _start;
            }

            return item;
        }

        public T Peek(int index)
        {
            return _buffer[index];
        }

        public void Clear()
        {
            _buffer = new T[_capacity];
            _start = _end = 0;
        }

        public bool Contains(T item)
        {
            return _buffer.Contains(item);
        }

        #region IEnumerable Members

        public IEnumerator GetEnumerator()
        {
            return _buffer.Where(item => !item.Equals(default(T))).GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion
    }
}

    [TestClass]
    public class StackLinkedListTest
    {

        private StackLinkedList _collection;

        public UnitTest1()
        {
            _collection = new StackLinkedList();
        }
        
        [TestMethod]
        public void StackTestMethod()
        {
             _collection.Push(1);
             _collection.Push(2);
             _collection.Push(3);
            Assert.AreEqual(3, _collection.Peek());
            Assert.AreEqual(3, _collection.Pop());
            Assert.AreEqual(2, _collection.Pop());
            Assert.AreEqual(1, _collection.Pop());
            Assert.AreEqual(0, _collection.Count());
        }
 
    }