Skip to content

Commit 38a41ca

Browse files
committed
Added CircularBuffer DataStructure. It's also known as circular queue or ring buffer. I also added appropriate unit tests for each behaviour
1 parent d1ca661 commit 38a41ca

File tree

2 files changed

+178
-0
lines changed

2 files changed

+178
-0
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
using System;
2+
3+
namespace DataStructures.Lists {
4+
public class CircularBuffer<T> {
5+
/// <summary>
6+
/// Initializes a circular buffer with initial length of 10
7+
/// </summary>
8+
public CircularBuffer(bool canOverride=true) : this(10,canOverride) {
9+
}
10+
11+
/// <summary>
12+
/// Initializes a circular buffer with given length
13+
/// </summary>
14+
/// <param name="length">The length of the buffer</param>
15+
public CircularBuffer(int length, bool canOverride=true) {
16+
if (length < 1) {
17+
throw new ArgumentOutOfRangeException("length can not be zero or negative");
18+
}
19+
_circularBuffer = new T[length + 1];
20+
_end = 0;
21+
_start = 0;
22+
CanOverride = canOverride;
23+
}
24+
25+
/// <summary>
26+
/// Writes value to the back of the buffer
27+
/// </summary>
28+
/// <param name="value">value to be added to the buffer</param>
29+
public void Write(T value) {
30+
if (CanOverride) {
31+
InsertData(value);
32+
}
33+
else {
34+
DontOverrides(value);
35+
}
36+
}
37+
38+
/// <summary>
39+
/// Inserts data into the buffer when it is not filled up
40+
/// </summary>
41+
/// <param name="value"></param>
42+
private void DontOverrides(T value) {
43+
if (IsFilledUp) {
44+
return;
45+
}
46+
InsertData(value);
47+
}
48+
49+
/// <summary>
50+
/// Inserts data into the buffer without checking if it is full
51+
/// </summary>
52+
/// <param name="value"></param>
53+
private void InsertData(T value) {
54+
_circularBuffer[_end] = value;
55+
_end = (_end + 1) % _circularBuffer.Length;
56+
if (_end == _start) {
57+
_start = (_start + 1) % _circularBuffer.Length;
58+
}
59+
}
60+
61+
/// <summary>
62+
/// Reads value from the front of the buffer
63+
/// <summay>
64+
/// Use the IsEmpty property to know when to read appropriately.
65+
/// </summay>
66+
/// </summary>
67+
/// <returns></returns>
68+
public T Read() {
69+
var result = _circularBuffer[_start];
70+
_circularBuffer[_start] = _circularBuffer[_end];
71+
_start = (_start + 1) % _circularBuffer.Length;
72+
return result;
73+
}
74+
75+
public bool IsEmpty { get => _end == _start; }
76+
public int Length { get => _circularBuffer.Length - 1; }
77+
public bool IsFilledUp { get => ((_end + 1) % _circularBuffer.Length == _start) && !_circularBuffer[_start].Equals(_circularBuffer[_end]); }
78+
/// <summary>
79+
/// Controls whether data should be overridden when it is continously inserted without reading
80+
/// </summary>
81+
public bool CanOverride { get; }
82+
83+
private T[] _circularBuffer;
84+
private int _end;
85+
private int _start;
86+
}
87+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
using DataStructures.Lists;
2+
using Xunit;
3+
4+
namespace UnitTest.DataStructuresTests {
5+
public class CircularBufferTest {
6+
7+
[Fact]
8+
public static void SetsFixedLength() {
9+
var circularBuffer = new CircularBuffer<byte>(3);
10+
var length = circularBuffer.Length;
11+
12+
Assert.Equal(3, length);
13+
}
14+
15+
[Fact]
16+
public static void InitializesWithDefaultLengthOf10() {
17+
var circularBuffer = new CircularBuffer<byte>();
18+
var length = circularBuffer.Length;
19+
20+
Assert.Equal(10, length);
21+
}
22+
23+
[Fact]
24+
public static void WritesAndReadsValue() {
25+
var circularBuffer = new CircularBuffer<byte>(4);
26+
circularBuffer.Write(13);
27+
circularBuffer.Write(43);
28+
circularBuffer.Write(23);
29+
circularBuffer.Write(2);
30+
31+
var result1 = circularBuffer.Read();
32+
var result2 = circularBuffer.Read();
33+
var result3 = circularBuffer.Read();
34+
var result4 = circularBuffer.Read();
35+
var result5 = circularBuffer.Read();
36+
var result6 = circularBuffer.Read();
37+
var result7 = circularBuffer.Read();
38+
var result8 = circularBuffer.Read();
39+
40+
Assert.Equal(13, result1);
41+
Assert.Equal(43, result2);
42+
Assert.Equal(23, result3);
43+
Assert.Equal(2, result4);
44+
Assert.Equal(0, result5);
45+
Assert.Equal(0, result6);
46+
Assert.Equal(0, result7);
47+
Assert.Equal(0, result8);
48+
}
49+
50+
[Fact]
51+
public static void TestingCantOverrideFunctionality() {
52+
var circularBuffer = new CircularBuffer<byte>(3, false);
53+
circularBuffer.Write(3);
54+
circularBuffer.Write(34);
55+
circularBuffer.Write(24);
56+
// if it doesn't override, then the values above will be read else,
57+
// the value below will be read
58+
circularBuffer.Write(2);
59+
circularBuffer.Write(1);
60+
circularBuffer.Write(3);
61+
62+
var result1 = circularBuffer.Read();
63+
var result2 = circularBuffer.Read();
64+
var result3 = circularBuffer.Read();
65+
66+
Assert.Equal(3, result1);
67+
Assert.Equal(34, result2);
68+
Assert.Equal(24, result3);
69+
}
70+
71+
[Fact]
72+
public static void TestingWritingAndReadingSimultenouslyWithoutOverriding() {
73+
var circularBuffer = new CircularBuffer<byte>(3, false);
74+
circularBuffer.Write(3);
75+
circularBuffer.Write(34);
76+
circularBuffer.Write(24);
77+
var result1 = circularBuffer.Read();
78+
var result2 = circularBuffer.Read();
79+
80+
circularBuffer.Write(4);
81+
circularBuffer.Write(14);
82+
var result3 = circularBuffer.Read();
83+
var result4 = circularBuffer.Read();
84+
var result5 = circularBuffer.Read();
85+
86+
Assert.Equal(24, result3);
87+
Assert.Equal(4, result4);
88+
Assert.Equal(14, result5);
89+
}
90+
}
91+
}

0 commit comments

Comments
 (0)