blob: dd5b7ab2403d320de227517949be7c82a8c534fc [file] [log] [blame]
Alan Viverette3da604b2020-06-10 18:34:39 +00001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.display.utils;
18
19/**
20 * A buffer that supports adding new values and truncating old ones.
21 */
22public class RollingBuffer {
23
24 private static final int INITIAL_SIZE = 50;
25
26 private int mSize;
27 private int mCount;
28 private int mStart;
29 private int mEnd;
30
31 private long[] mTimes; // Milliseconds
32 private float[] mValues;
33
34 public RollingBuffer() {
35 mSize = INITIAL_SIZE;
36 mTimes = new long[INITIAL_SIZE];
37 mValues = new float[INITIAL_SIZE];
38 clear();
39 }
40
41 /**
42 * Add a value at a given time.
43 *
44 * @param time
45 * The time (in milliseconds).
46 * @param value
47 * The value.
48 */
49 public void add(long time, float value) {
50 if (mCount >= mSize) {
51 expandBuffer();
52 }
53 mTimes[mEnd] = time;
54 mValues[mEnd] = value;
55 mEnd = (mEnd + 1) % mSize;
56 mCount++;
57 }
58
59 /**
60 * Get the size of the buffer.
61 *
62 * @return The size of the buffer.
63 */
64 public int size() {
65 return mCount;
66 }
67
68 /**
69 * Return whether the buffer is empty or not.
70 *
71 * @return Whether the buffer is empty or not.
72 */
73 public boolean isEmpty() {
74 return size() == 0;
75 }
76
77 /**
78 * Get a time.
79 *
80 * @param index
81 * The index of the time.
82 *
83 * @return The time.
84 */
85 public long getTime(int index) {
86 return mTimes[offsetOf(index)];
87 }
88
89 /**
90 * Get a value.
91 *
92 * @param index
93 * The index of the value.
94 *
95 * @return The value.
96 */
97 public float getValue(int index) {
98 return mValues[offsetOf(index)];
99 }
100
101 /**
102 * Truncate old values.
103 *
104 * @param minTime
105 * The minimum time (all values older than this time are truncated).
106 */
107 public void truncate(long minTime) {
108 if (isEmpty() || getTime(0) >= minTime) {
109 return;
110 }
111 final int index = getLatestIndexBefore(minTime);
112 mStart = offsetOf(index);
113 mCount -= index;
114 // Remove everything that happened before mTimes[index], but set the index-th value time to
115 // minTime rather than dropping it, as that would've been the value between the minTime and
116 // mTimes[index+1].
117 //
118 // -*---*---|---*---*- => xxxxxxxxx|*--*---*- rather than xxxxxxxxx|???*---*-
119 // ^ ^ ^ ^ ^
120 // i i+1 i i+1 i+1
121 mTimes[mStart] = minTime;
122 }
123
124 /**
125 * Clears the buffer.
126 */
127 public void clear() {
128 mCount = 0;
129 mStart = 0;
130 mEnd = 0;
131 }
132
133 /**
134 * Convert the buffer to string.
135 *
136 * @return The buffer as string.
137 */
138 public String toString() {
139 StringBuffer sb = new StringBuffer();
140 sb.append("[");
141 for (int i = 0; i < mCount; i++) {
142 final int index = offsetOf(i);
143 sb.append(mValues[index] + " @ " + mTimes[index]);
144 if (i + 1 != mCount) {
145 sb.append(", ");
146 }
147 }
148 sb.append("]");
149 return sb.toString();
150 }
151
152 private int offsetOf(int index) {
153 if (index < 0 || index >= mCount) {
154 throw new ArrayIndexOutOfBoundsException("invalid index: " + index + ", mCount= "
155 + mCount);
156 }
157 return (mStart + index) % mSize;
158 }
159
160 private void expandBuffer() {
161 final int size = mSize * 2;
162 long[] times = new long[size];
163 float[] values = new float[size];
164 System.arraycopy(mTimes, mStart, times, 0, mCount - mStart);
165 System.arraycopy(mTimes, 0, times, mCount - mStart, mStart);
166 System.arraycopy(mValues, mStart, values, 0, mCount - mStart);
167 System.arraycopy(mValues, 0, values, mCount - mStart, mStart);
168 mSize = size;
169 mStart = 0;
170 mEnd = mCount;
171 mTimes = times;
172 mValues = values;
173 }
174
175 private int getLatestIndexBefore(long time) {
176 for (int i = 1; i < mCount; i++) {
177 if (mTimes[offsetOf(i)] > time) {
178 return i - 1;
179 }
180 }
181 return mCount - 1;
182 }
183
184}