I haven't had much time to work on blog posts recently, but I do have one quick post to make.

One of our current application prototype's stores a bunch of data. Data continuously arrives, but I only want to store so much of it. You could do this with something list a List<T> and just remove items when the collection is too big, but I wanted something a bit more efficient which didn't have to do any sort of resizing or allocation when adding and removing items. Enter the circular buffer.

What is a circular buffer?

To quote Wikipedia, a circular buffer, cyclic buffer or ring buffer is a data structure that uses a single, fixed-size buffer as if it were connected end-to-end. This structure lends itself easily to buffering data streams.

Indeed. Originally I didn't want to invent the wheel, so when I found Circular Buffer for .NET1 I thought I would use that. Unfortunately as soon as I started using, I hit some problems, both in the code and with what I was trying to do. So I stopped working on what I was doing and wrote a full set of tests for the class, fixing bugs as I went, and also adding some more features to handle what I wanted.

Eventually I decided I would put the code up on GitHub as Circular Buffer for .NET doesn't seem to be maintained any longer.

A generic CircularBuffer<T> class for .NET

On our GitHub page you can download a modified version of the original class. I'm not going into too many details here as it's very straightforward to use - if you've used Queue<T> or Stack<T> then you'll be right at home.

  • Get - Removes and returns one or more items from the start of the buffer
  • Put - Adds one or more items to the end of the buffer. If the buffer is full, then items at the start will be overwritten
  • Peek - Retrieve one or more items from the start of the buffer, without removing them
  • PeekLast - Retrieve the last item in the buffer without removing it
  • ToArray - Return all items in the buffer
  • CopyTo - An advanced version of ToArray, allows you to copy items from the buffer into another array
  • Clear - Resets the buffer

There are also some properties to control behaviour or provide state information.

  • Capacity - The total number of items the buffer can hold
  • Size - The current number of items in the buffer
  • AllowOverwrite - When true, new items overwrite the oldest items when the buffer is full. Otherwise, an exception is thrown
  • IsEmpty - true if the buffer is empty
  • IsFull - true if the buffer is full and AllowOverwrite is false

Examples

This first example creates a CircularBuffer<T>, adds four items, then retrieves the first item.

csharp
CircularBuffer<string> target;
string firstItem;

target = new CircularBuffer<string>(10);
target.Put("Alpha");
target.Put("Beta");
target.Put("Gamma");

firstItem = target.Get(); // Returns Alpha

This second example shows how the buffer will automatically overwrite the oldest items when full.

csharp
CircularBuffer<string> target;
string firstItem;

target = new CircularBuffer<string>(3);
target.Put("Alpha");
target.Put("Beta");
target.Put("Gamma");
target.Put("Delta");

firstItem = target.Get(); // Returns beta

For more examples, see the test class CircularBufferTests as this has tests which cover almost all the code paths.

Requirements

.NET Framework 2.0 or later.

Download

Cyotek.Collections.Generic.CircularBuffer

Update History

  • 2014-06-21 - First published
  • 2020-11-21 - Updated formatting
  • 2023-02-05 - Fixed swapped URLs

Like what you're reading? Perhaps you like to buy us a coffee?

Donate via Buy Me a Coffee

Donate via PayPal


Comments

# Mercede

Is this threadsafe or we have to make it threadsafe>

Reply

# Richard Moss

Hello,

Thanks for the question. At the moment, it's not thread safe - the initial use I had for the class didn't involve multi-threaded access and I didn't want to burden the base class with having to do processing that wouldn't be used.

Regards;
Richard Moss

Reply