2 * XdccDownloader - AccumulatingDataCounter.java - Copyright © 2013 David Roden
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 package net.pterodactylus.xdcc.util.io;
20 import java.util.SortedMap;
21 import java.util.concurrent.TimeUnit;
23 import com.google.common.collect.Maps;
26 * Counter for data that can be accumulated over a period. A typical use would
27 * be a bandwidth counter.
29 * A counter has a maximum age for its timestamped entries. Occurrences older
30 * than the maximum lifetime are discarded when new occurrences are added.
32 * The returned rates are always bytes/second, averaged over the given amount of
33 * time (or the maximum lifetime, if unspecified).
35 * A counter can also be {@link #stop()}’ed in which case it will remember the
36 * time when it was stopped so that the calculation of the overall rate is now
37 * skewed by the further passage of time. The calculation of the current rate is
38 * not affected by stopping the counter.
40 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
42 public class AccumulatingDataCounter {
44 /** The timestamped occurrences. */
45 private final SortedMap<Long, Long> occurrences = Maps.newTreeMap();
47 /** The maximum occurrence lifetime. */
48 private final long maximumLifetime;
50 /** The start time. */
51 private final long startTime = System.currentTimeMillis();
54 private long endTime = Long.MIN_VALUE;
56 /** The total number of bytes processed. */
57 private long total = 0;
60 * Creates a new accumulating data counter with a maximum lifetime of 1
63 public AccumulatingDataCounter() {
64 this(1, TimeUnit.MINUTES);
68 * Creates a new accumulating data counter with the given maximum lifetime. A
69 * larger lifetime will also use more memory for the counter data.
72 * The lifetime of the counter data
74 * The unit of the lifetime
76 public AccumulatingDataCounter(long lifetime, TimeUnit timeUnit) {
77 maximumLifetime = timeUnit.toMillis(lifetime);
85 * Counts the given number of bytes at this moment.
88 * The number of bytes to count
90 public void count(long bytes) {
91 long now = System.currentTimeMillis();
92 synchronized (occurrences) {
93 Long counter = occurrences.get(now);
94 if (counter == null) {
98 occurrences.put(now, counter);
100 if (endTime == Long.MIN_VALUE) {
108 * Stops the counter and prevents the overall rate from changing. Calling this
109 * method more than once does not change the end time.
112 if (endTime == Long.MIN_VALUE) {
113 endTime = System.currentTimeMillis();
118 * Returns the current rate of this counter, in bytes/second, averaged over the
119 * maximum lifetime of this counter.
121 * @return The current rate averaged over the maximum lifetime
123 public long getCurrentRate() {
124 return getCurrentRate(maximumLifetime);
128 * Returns the current rate of this counter, in bytes/second, averaged over the
129 * given duration. If the given duration is longer than the maximum lifetime of
130 * this counter, the counter data is averaged over the maximum lifetime as
131 * using the originally given lifetime would skew the results.
133 * @return The current rate averaged over the given duration, in bytes/second
135 public long getCurrentRate(long millis) {
136 long now = System.currentTimeMillis();
138 synchronized (occurrences) {
139 for (long count : occurrences.tailMap(now - Math.min(millis, maximumLifetime)).values()) {
143 return sum * 1000 / Math.min(millis, maximumLifetime);
147 * Returns the overall rate of this counter, counted since its creation.
149 * @return The overall rate of this counter, in bytes/second
151 public long getOverallRate() {
152 return total * 1000 / (((endTime > Long.MIN_VALUE) ? endTime : System.currentTimeMillis()) - startTime);
160 * Trims the occurrences by removing all entries that are older than the
161 * current time and the maximum lifetime of the counter data allows.
163 private void trimOccurrences() {
164 long oldestValid = System.currentTimeMillis() - maximumLifetime;
165 occurrences.headMap(oldestValid).clear();