[wpimath] Add generic circular buffer class to Java (#5969)

The original is now called DoubleCircularBuffer.
This commit is contained in:
Tyler Veness
2023-11-30 21:12:23 -08:00
committed by GitHub
parent 9fa28eb07a
commit 0f9ebe92d9
7 changed files with 455 additions and 42 deletions

View File

@@ -4,11 +4,9 @@
package edu.wpi.first.util;
import java.util.Arrays;
/** This is a simple circular buffer so we don't need to "bucket brigade" copy old values. */
public class CircularBuffer {
private double[] m_data;
public class CircularBuffer<T> {
private T[] m_data;
// Index of element at front of buffer
private int m_front;
@@ -21,9 +19,9 @@ public class CircularBuffer {
*
* @param size The size of the circular buffer.
*/
@SuppressWarnings("unchecked")
public CircularBuffer(int size) {
m_data = new double[size];
Arrays.fill(m_data, 0.0);
m_data = (T[]) new Object[size];
}
/**
@@ -40,7 +38,7 @@ public class CircularBuffer {
*
* @return value at front of buffer
*/
public double getFirst() {
public T getFirst() {
return m_data[m_front];
}
@@ -48,11 +46,14 @@ public class CircularBuffer {
* Get value at back of buffer.
*
* @return value at back of buffer
* @throws IndexOutOfBoundsException if the index is out of range (index &lt; 0 || index &gt;=
* size())
*/
public double getLast() {
@SuppressWarnings("unchecked")
public T getLast() {
// If there are no elements in the buffer, do nothing
if (m_length == 0) {
return 0.0;
throw new IndexOutOfBoundsException("getLast() called on an empty container");
}
return m_data[(m_front + m_length - 1) % m_data.length];
@@ -64,7 +65,7 @@ public class CircularBuffer {
*
* @param value The value to push.
*/
public void addFirst(double value) {
public void addFirst(T value) {
if (m_data.length == 0) {
return;
}
@@ -84,7 +85,7 @@ public class CircularBuffer {
*
* @param value The value to push.
*/
public void addLast(double value) {
public void addLast(T value) {
if (m_data.length == 0) {
return;
}
@@ -103,14 +104,17 @@ public class CircularBuffer {
* Pop value at front of buffer.
*
* @return value at front of buffer
* @throws IndexOutOfBoundsException if the index is out of range (index &lt; 0 || index &gt;=
* size())
*/
public double removeFirst() {
@SuppressWarnings("unchecked")
public T removeFirst() {
// If there are no elements in the buffer, do nothing
if (m_length == 0) {
return 0.0;
throw new IndexOutOfBoundsException("removeFirst() called on an empty container");
}
double temp = m_data[m_front];
T temp = m_data[m_front];
m_front = moduloInc(m_front);
m_length--;
return temp;
@@ -120,11 +124,14 @@ public class CircularBuffer {
* Pop value at back of buffer.
*
* @return value at back of buffer
* @throws IndexOutOfBoundsException if the index is out of range (index &lt; 0 || index &gt;=
* size())
*/
public double removeLast() {
@SuppressWarnings("unchecked")
public T removeLast() {
// If there are no elements in the buffer, do nothing
if (m_length == 0) {
return 0.0;
throw new IndexOutOfBoundsException("removeLast() called on an empty container");
}
m_length--;
@@ -138,8 +145,9 @@ public class CircularBuffer {
*
* @param size New buffer size.
*/
@SuppressWarnings("unchecked")
public void resize(int size) {
double[] newBuffer = new double[size];
var newBuffer = (T[]) new Object[size];
m_length = Math.min(m_length, size);
for (int i = 0; i < m_length; i++) {
newBuffer[i] = m_data[(m_front + i) % m_data.length];
@@ -150,7 +158,6 @@ public class CircularBuffer {
/** Sets internal buffer contents to zero. */
public void clear() {
Arrays.fill(m_data, 0.0);
m_front = 0;
m_length = 0;
}
@@ -161,23 +168,25 @@ public class CircularBuffer {
* @param index Index into the buffer.
* @return Element at index starting from front of buffer.
*/
public double get(int index) {
public T get(int index) {
return m_data[(m_front + index) % m_data.length];
}
/**
* Increment an index modulo the length of the m_data buffer.
* Increment an index modulo the length of the buffer.
*
* @param index Index into the buffer.
* @return The incremented index.
*/
private int moduloInc(int index) {
return (index + 1) % m_data.length;
}
/**
* Decrement an index modulo the length of the m_data buffer.
* Decrement an index modulo the length of the buffer.
*
* @param index Index into the buffer.
* @return The decremented index.
*/
private int moduloDec(int index) {
if (index == 0) {

View File

@@ -0,0 +1,191 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.util;
import java.util.Arrays;
/** This is a simple circular buffer so we don't need to "bucket brigade" copy old values. */
public class DoubleCircularBuffer {
private double[] m_data;
// Index of element at front of buffer
private int m_front;
// Number of elements used in buffer
private int m_length;
/**
* Create a CircularBuffer with the provided size.
*
* @param size The size of the circular buffer.
*/
public DoubleCircularBuffer(int size) {
m_data = new double[size];
Arrays.fill(m_data, 0.0);
}
/**
* Returns number of elements in buffer.
*
* @return number of elements in buffer
*/
public int size() {
return m_length;
}
/**
* Get value at front of buffer.
*
* @return value at front of buffer
*/
public double getFirst() {
return m_data[m_front];
}
/**
* Get value at back of buffer.
*
* @return value at back of buffer
*/
public double getLast() {
// If there are no elements in the buffer, do nothing
if (m_length == 0) {
return 0.0;
}
return m_data[(m_front + m_length - 1) % m_data.length];
}
/**
* Push new value onto front of the buffer. The value at the back is overwritten if the buffer is
* full.
*
* @param value The value to push.
*/
public void addFirst(double value) {
if (m_data.length == 0) {
return;
}
m_front = moduloDec(m_front);
m_data[m_front] = value;
if (m_length < m_data.length) {
m_length++;
}
}
/**
* Push new value onto back of the buffer. The value at the front is overwritten if the buffer is
* full.
*
* @param value The value to push.
*/
public void addLast(double value) {
if (m_data.length == 0) {
return;
}
m_data[(m_front + m_length) % m_data.length] = value;
if (m_length < m_data.length) {
m_length++;
} else {
// Increment front if buffer is full to maintain size
m_front = moduloInc(m_front);
}
}
/**
* Pop value at front of buffer.
*
* @return value at front of buffer
*/
public double removeFirst() {
// If there are no elements in the buffer, do nothing
if (m_length == 0) {
return 0.0;
}
double temp = m_data[m_front];
m_front = moduloInc(m_front);
m_length--;
return temp;
}
/**
* Pop value at back of buffer.
*
* @return value at back of buffer
*/
public double removeLast() {
// If there are no elements in the buffer, do nothing
if (m_length == 0) {
return 0.0;
}
m_length--;
return m_data[(m_front + m_length) % m_data.length];
}
/**
* Resizes internal buffer to given size.
*
* <p>A new buffer is allocated because arrays are not resizable.
*
* @param size New buffer size.
*/
public void resize(int size) {
double[] newBuffer = new double[size];
m_length = Math.min(m_length, size);
for (int i = 0; i < m_length; i++) {
newBuffer[i] = m_data[(m_front + i) % m_data.length];
}
m_data = newBuffer;
m_front = 0;
}
/** Sets internal buffer contents to zero. */
public void clear() {
Arrays.fill(m_data, 0.0);
m_front = 0;
m_length = 0;
}
/**
* Get the element at the provided index relative to the start of the buffer.
*
* @param index Index into the buffer.
* @return Element at index starting from front of buffer.
*/
public double get(int index) {
return m_data[(m_front + index) % m_data.length];
}
/**
* Increment an index modulo the length of the buffer.
*
* @param index Index into the buffer.
* @return The incremented index.
*/
private int moduloInc(int index) {
return (index + 1) % m_data.length;
}
/**
* Decrement an index modulo the length of the buffer.
*
* @param index Index into the buffer.
* @return The decremented index.
*/
private int moduloDec(int index) {
if (index == 0) {
return m_data.length - 1;
} else {
return index - 1;
}
}
}