package com.kuxhausen.huemore.utils;
import java.util.Arrays;
public class RateLimiter {
private long mWindowWidth;
private int mCapacity;
/**
* Rolling buffer of the most recent events. An event with N capacity is stored as N timestamps of
* that event.
*/
private long[] mTimestamps;
/**
* Index of the oldest timestamp in the buffer.
*/
private int mIndex;
private long mLatestEventMs;
/**
* Implements a sliding widow rate limiter. Optimized for minimal object
* allocations/de-allocations. O(capacity) performance and O(capacity) memory.
*
* @param windowWidthMs width in milliseconds of the rolling window being rate limited.
* @param capacity capacity during the window.
*/
public RateLimiter(long windowWidthMs, int capacity) {
mWindowWidth = windowWidthMs;
mCapacity = capacity;
mTimestamps = new long[capacity];
Arrays.fill(mTimestamps, Long.MIN_VALUE);
mIndex = 0;
mLatestEventMs = Long.MIN_VALUE;
}
/**
* @param timestampMs event timestamp in milliseconds
* @return if the event can occur without exceeding the rate limit
*/
public boolean hasCapacity(long timestampMs, int eventCapacity) {
if (timestampMs < mLatestEventMs) {
throw new IllegalArgumentException("RateLimiter cannot go backwards in time");
}
int tempIndex = mIndex;
while (eventCapacity > 0) {
if (mTimestamps[tempIndex] < timestampMs - mWindowWidth) {
tempIndex = (tempIndex + 1) % mCapacity;
eventCapacity--;
} else {
return false;
}
}
return true;
}
/**
* Must only be called if hasCapacity returns true.
*
* @param timestampMs event timestamp in milliseconds.
*/
public void consumeCapacity(long timestampMs, int eventCapacity) {
while (eventCapacity > 0) {
mTimestamps[mIndex] = timestampMs;
mIndex = (mIndex + 1) % mCapacity;
eventCapacity--;
}
mLatestEventMs = timestampMs;
}
}