001// Copyright (c) FIRST and other WPILib contributors. 002// Open Source Software; you can modify and/or share it under the terms of 003// the WPILib BSD license file in the root directory of this project. 004 005package edu.wpi.first.util; 006 007/** 008 * This is a simple circular buffer so we don't need to "bucket brigade" copy old values. 009 * 010 * @param <T> Buffer element type. 011 */ 012public class CircularBuffer<T> { 013 private T[] m_data; 014 015 // Index of element at front of buffer 016 private int m_front; 017 018 // Number of elements used in buffer 019 private int m_length; 020 021 /** 022 * Create a CircularBuffer with the provided size. 023 * 024 * @param size Maximum number of buffer elements. 025 */ 026 @SuppressWarnings("unchecked") 027 public CircularBuffer(int size) { 028 m_data = (T[]) new Object[size]; 029 } 030 031 /** 032 * Returns number of elements in buffer. 033 * 034 * @return number of elements in buffer 035 */ 036 public int size() { 037 return m_length; 038 } 039 040 /** 041 * Get value at front of buffer. 042 * 043 * @return value at front of buffer 044 */ 045 public T getFirst() { 046 return m_data[m_front]; 047 } 048 049 /** 050 * Get value at back of buffer. 051 * 052 * @return value at back of buffer 053 * @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index >= 054 * size()) 055 */ 056 public T getLast() { 057 // If there are no elements in the buffer, do nothing 058 if (m_length == 0) { 059 throw new IndexOutOfBoundsException("getLast() called on an empty container"); 060 } 061 062 return m_data[(m_front + m_length - 1) % m_data.length]; 063 } 064 065 /** 066 * Push new value onto front of the buffer. The value at the back is overwritten if the buffer is 067 * full. 068 * 069 * @param value The value to push. 070 */ 071 public void addFirst(T value) { 072 if (m_data.length == 0) { 073 return; 074 } 075 076 m_front = moduloDec(m_front); 077 078 m_data[m_front] = value; 079 080 if (m_length < m_data.length) { 081 m_length++; 082 } 083 } 084 085 /** 086 * Push new value onto back of the buffer. The value at the front is overwritten if the buffer is 087 * full. 088 * 089 * @param value The value to push. 090 */ 091 public void addLast(T value) { 092 if (m_data.length == 0) { 093 return; 094 } 095 096 m_data[(m_front + m_length) % m_data.length] = value; 097 098 if (m_length < m_data.length) { 099 m_length++; 100 } else { 101 // Increment front if buffer is full to maintain size 102 m_front = moduloInc(m_front); 103 } 104 } 105 106 /** 107 * Pop value at front of buffer. 108 * 109 * @return value at front of buffer 110 * @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index >= 111 * size()) 112 */ 113 public T removeFirst() { 114 // If there are no elements in the buffer, do nothing 115 if (m_length == 0) { 116 throw new IndexOutOfBoundsException("removeFirst() called on an empty container"); 117 } 118 119 T temp = m_data[m_front]; 120 m_front = moduloInc(m_front); 121 m_length--; 122 return temp; 123 } 124 125 /** 126 * Pop value at back of buffer. 127 * 128 * @return value at back of buffer 129 * @throws IndexOutOfBoundsException if the index is out of range (index < 0 || index >= 130 * size()) 131 */ 132 public T removeLast() { 133 // If there are no elements in the buffer, do nothing 134 if (m_length == 0) { 135 throw new IndexOutOfBoundsException("removeLast() called on an empty container"); 136 } 137 138 m_length--; 139 return m_data[(m_front + m_length) % m_data.length]; 140 } 141 142 /** 143 * Resizes internal buffer to given size. 144 * 145 * <p>A new buffer is allocated because arrays are not resizable. 146 * 147 * @param size New buffer size. 148 */ 149 @SuppressWarnings("unchecked") 150 public void resize(int size) { 151 var newBuffer = (T[]) new Object[size]; 152 m_length = Math.min(m_length, size); 153 for (int i = 0; i < m_length; i++) { 154 newBuffer[i] = m_data[(m_front + i) % m_data.length]; 155 } 156 m_data = newBuffer; 157 m_front = 0; 158 } 159 160 /** Sets internal buffer contents to zero. */ 161 public void clear() { 162 m_front = 0; 163 m_length = 0; 164 } 165 166 /** 167 * Get the element at the provided index relative to the start of the buffer. 168 * 169 * @param index Index into the buffer. 170 * @return Element at index starting from front of buffer. 171 */ 172 public T get(int index) { 173 return m_data[(m_front + index) % m_data.length]; 174 } 175 176 /** 177 * Increment an index modulo the length of the buffer. 178 * 179 * @param index Index into the buffer. 180 * @return The incremented index. 181 */ 182 private int moduloInc(int index) { 183 return (index + 1) % m_data.length; 184 } 185 186 /** 187 * Decrement an index modulo the length of the buffer. 188 * 189 * @param index Index into the buffer. 190 * @return The decremented index. 191 */ 192 private int moduloDec(int index) { 193 if (index == 0) { 194 return m_data.length - 1; 195 } else { 196 return index - 1; 197 } 198 } 199}