C# CircularBuffer

A long long time ago I wrote a simple yet effective generic CircularBuffer implementation for C# which relies on a single resizable back buffer and allows both forward and backward rotations. This is a common example of a data structure every dev should know about, since is commonly taught in the early subjects of decent programming courses around he world. Despite its simplicity, getting to actually implement it takes a little while, and proper unit-testing it should strip you from at least 2 solid hours of your life, and surprisingly its not shipped by default on .NET Framework. Every time I need this kind of implementation, I usually tend to look around my good-ol archives for this particular file, make a couple quick improvements to it (like reviewing the XmlDoc writing for example), and stick it into the project right away, saving me some previous time. Today I’m uploading it here so everyone (me) can easily access it when needed, and hopefully save a couple hours.

using System;

namespace dev.RazorSoftware.Common.Generics
{
    /*  Circular Buffer Generic Implementation
    *   By Fabian R (KDERazorback) @ RazorSoftware.dev
    *  
    *   Fast and Lightweight, Generic Circular Buffer Implementation, supports for bidirectional rotations and Resize operations
    *   Free to use or modify!
    */


    /// <summary>
    /// Used to store a Circular Buffer of objects with a particular size, that rotates when an item is added to the collection.
    /// </summary>
    public class CircularBuffer<T>
    {
        /// <summary>
        /// Creates a new Circular Buffer with the specified Capacity
        /// </summary>
        /// <param name="capacity">Total elements that can be stored inside the buffer before it starts discarding items</param>
        public CircularBuffer(int capacity)
        {
            _plainBuffer = new T[capacity];
            _startIndex = 0;
        }

        private T[] _plainBuffer;
        private int _startIndex; // Stores the start of the Circular Buffer

        /// <summary>
        /// Stores the current Capacity of this Buffer
        /// </summary>
        public int Capacity
        {
            get { return _plainBuffer.Length; }
        }

        /// <summary>
        /// Returns the item that is stored on the specified Index inside the Circular Buffer
        /// </summary>
        /// <param name="index">Index of the Item to be returned</param>
        /// <returns>The object value stored on the specified index</returns>
        public T ElementAt(int index)
        {
            if ((index >= _plainBuffer.Length) || (index < 0))
                throw new IndexOutOfRangeException();

            index += _startIndex;

            if (index >= _plainBuffer.Length)
                index -= _plainBuffer.Length;

            return _plainBuffer[index];
        }

        /// <summary>
        /// Returns an array with the full content of the actual Circular Buffer
        /// </summary>
        /// <returns>An array of type T with the ordered content of the Circular Buffer</returns>
        public T[] ToArray()
        {
            int i;
            T[] output = new T[_plainBuffer.Length];

            for (i = 0; i < _plainBuffer.Length; i++)
            {
                output[i] = ElementAt(i);
            }

            return output;
        }

        /// <summary>
        /// Inserts a new item inside the Circular Buffer and rotates the entire structure by one step forwards
        /// </summary>
        /// <param name="newItem">New item to be inserted into the Circular Buffer</param>
        public void Insert(T newItem)
        {
            if (_startIndex == 0)
                _startIndex = _plainBuffer.Length - 1;
            else
                _startIndex--;

            _plainBuffer[_startIndex] = newItem;
        }

        /// <summary>
        /// Inserts a new item inside the Circular Buffer and rotates the entire structure by one step backwards
        /// </summary>
        /// <param name="newItem">New item to be inserted into the Circular Buffer</param>
        public void InsertBackwards(T newItem)
        {
            _plainBuffer[_startIndex] = newItem;

            if (_startIndex == _plainBuffer.Length - 1)
                _startIndex = 0;
            else
                _startIndex++;
        }

        /// <summary>
        /// Resizes this Circular Buffer to a new Capacity using Forward resizes, removing or inserting new items as needed
        /// </summary>
        /// <param name="newCapacity">New Capacity for this Circular Buffer</param>
        public void Resize(int newCapacity)
        {
            T[] bufferData = ToArray();

            _plainBuffer = new T[newCapacity];
            _startIndex = 0;

            int i;
            for (i = 0; i < Math.Min(_plainBuffer.Length, bufferData.Length); i++)
            {
                InsertBackwards(bufferData[i]); // Use Backward rotations for Forward Resizes
            }

            _startIndex = 0;
        }

        /// <summary>
        /// Resizes this Circular Buffer to a new Capacity using Backward resizes, removing or inserting new items as needed
        /// </summary>
        /// <param name="newCapacity">New Capacity for this Circular Buffer</param>
        public void ResizeBackwards(int newCapacity)
        {
            T[] bufferData = ToArray();

            _plainBuffer = new T[newCapacity];
            _startIndex = newCapacity - 1;

            int i;
            for (i = 0; i < Math.Min(_plainBuffer.Length, bufferData.Length); i++)
            {
                Insert(bufferData[i]); // Use Forward rotations for Backwards Resizes
            }
        }
    }
}

Hope that helps.

Share this post

Leave a Reply