/Utilities/Collections/CircularBuffer.cs
C# | 187 lines | 117 code | 14 blank | 56 comment | 4 complexity | 62b134d77e61f6824e13b2cbd417bbcf MD5 | raw file
Possible License(s): Apache-2.0
- using System;
- using Delta.Utilities.Helpers;
- using NUnit.Framework;
-
- namespace Delta.Utilities.Collections
- {
- /// <summary>
- /// CircularShortBuffer, used for recording/playback of sound samples.
- /// We can simply add new sample data and read existing sample data
- /// at the same time! The size of the circular buffer should fit
- /// the requirements (data should have been read when cycling around).
- /// There will be no warning if we overwrite data without reading.
- /// </summary>
- /// <typeparam name="T">Data type</typeparam>
- public class CircularBuffer<T>
- {
- #region Length (Public)
- /// <summary>
- /// Get how much bytes can be read, can even be more than buffer.Length
- /// </summary>
- /// <typeparam name="T">T</typeparam>
- public int Length
- {
- get
- {
- return writePos - readPos;
- }
- }
- #endregion
-
- #region Private
-
- #region buffer (Private)
- /// <summary>
- /// Buffer
- /// </summary>
- /// <typeparam name="T">T</typeparam>
- private readonly T[] buffer;
- #endregion
-
- #region writePos (Private)
- /// <summary>
- /// We only increase writePos and readPos, they will
- /// obviously go way out of range of the buffer size,
- /// we need ALWAYS to modulate it by the size of the buffer to access data!
- /// We do this because only this way we can see how much data
- /// was written and how much was read!
- /// </summary>
- /// <typeparam name="T">T</typeparam>
- private int writePos;
- #endregion
-
- #region readPos (Private)
- /// <summary>
- /// Read pos
- /// </summary>
- /// <returns>0</returns>
- /// <typeparam name="T">T</typeparam>
- private int readPos;
- #endregion
-
- #endregion
-
- #region Constructors
- /// <summary>
- /// Circular short buffer
- /// </summary>
- /// <param name="bufferSize">Buffer size</param>
- public CircularBuffer(int bufferSize)
- {
- buffer = new T[bufferSize];
- writePos = 0;
- readPos = 0;
- }
- #endregion
-
- #region Flush (Public)
- /// <summary>
- /// Flush is very simply, we just reset both writePos and readPos to 0.
- /// </summary>
- public void Flush()
- {
- writePos = 0;
- readPos = 0;
- }
- #endregion
-
- #region Write (Public)
- /// <summary>
- /// Write array of new data to write into the circular buffer.
- /// </summary>
- /// <param name="newData">New data to write into the buffer</param>
- public void Write(T[] newData)
- {
- if (newData.Length >
- buffer.Length)
- {
- throw new InvalidOperationException(
- "newData length can't be bigger than circular buffer: " +
- "newData.Length=" + newData.Length +
- ", buffer.Length=" + buffer.Length);
- }
-
- int localWritePos = writePos % buffer.Length;
- // Does whole newData block fit till end of array?
- if (localWritePos + newData.Length <=
- buffer.Length)
- {
- Array.Copy(newData, 0, buffer, localWritePos, newData.Length);
- }
- else
- {
- // Does not fit, split into 2 parts and add seperatly
- int lengthTillEnd = buffer.Length - localWritePos;
- Array.Copy(newData, 0, buffer, localWritePos, lengthTillEnd);
- Array.Copy(newData, lengthTillEnd, buffer, 0,
- newData.Length - lengthTillEnd);
- }
- // Advance write pos
- writePos += newData.Length;
- }
- #endregion
-
- #region Read (Public)
- /// <summary>
- /// Read data into an array of the same type.
- /// </summary>
- /// <param name="readData">Data to read into</param>
- public void Read(T[] readData)
- {
- if (readData.Length >
- buffer.Length)
- {
- throw new InvalidOperationException(
- "readData length can't be bigger than circular buffer: " +
- "readData.Length" + readData.Length +
- ", buffer.Length=" + buffer.Length);
- }
-
- int localReadPos = readPos % buffer.Length;
- // Can read one simple block?
- if (localReadPos + readData.Length <=
- buffer.Length)
- {
- Array.Copy(buffer, localReadPos, readData, 0, readData.Length);
- }
- else
- {
- // Does not fit, split into 2 parts and read seperatly
- int lengthTillEnd = buffer.Length - localReadPos;
- Array.Copy(buffer, localReadPos, readData, 0, lengthTillEnd);
- Array.Copy(buffer, 0, readData, lengthTillEnd,
- readData.Length - lengthTillEnd);
- }
- // Advance read pos
- readPos += readData.Length;
- }
- #endregion
- }
-
- /// <summary>
- /// CircularBuffer tests helper, must be decleared outside of generic class.
- /// </summary>
- internal class CircularBufferTests
- {
- #region TestAddAndReadData (Static)
- /// <summary>
- /// Test add and read data
- /// </summary>
- [Test]
- public static void TestAddAndReadData()
- {
- CircularBuffer<int> testCircularBuffer = new CircularBuffer<int>(100);
- testCircularBuffer.Write(new[]
- {
- 1, 2, 3, 4
- });
-
- int[] readBuffer = new int[2];
- testCircularBuffer.Read(readBuffer);
- Assert.Equal("1, 2", readBuffer.Write());
- testCircularBuffer.Read(readBuffer);
- Assert.Equal("3, 4", readBuffer.Write());
- }
- #endregion
- }
- }