/***************************************************************************
* Copyright 2006-2016 by Christian Ihle *
* contact@kouchat.net *
* *
* This file is part of KouChat. *
* *
* KouChat is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 3 of *
* the License, or (at your option) any later version. *
* *
* KouChat is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with KouChat. *
* If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
package net.usikkert.kouchat.util;
/**
* Used for calculating the number of bytes transferred per second.
*
* <p>Usage:</p>
* <ul>
* <li>Run {@link #prepare()} just before starting the transfer to record the time
* when the transfer begins and to reset the counters.</li>
* <li>Then run {@link #addBytes(long)} with regular intervals to register the number
* of bytes transferred.</li>
* <li>After every new second has passed the calculated speed should be
* available through {@link #getBytesPerSec()}.</li>
* </ul>
*
* @author Christian Ihle
*/
public class ByteCounter {
/** Number of milliseconds in one second. */
private static final int ONE_SECOND = 1000;
/** Time of the previous update. */
private long previousTime;
/** The number of milliseconds counted since the last second was calculated. */
private long timeCounted;
/** The current number of bytes per second. */
private long bytesPerSec;
/** The number of bytes counted since the last second was calculated. */
private long bytesCounted;
/**
* Use when starting the transfer to register the current time, and reset the counters.
*/
public void prepare() {
previousTime = System.currentTimeMillis();
timeCounted = 0;
bytesPerSec = 0;
bytesCounted = 0;
}
/**
* Use this to add the number of bytes transferred since last time
* {@link #addBytes(long)} or {@link #prepare()} was called.
* The speed is calculated every second, so {@link #addBytes(long)} should
* be called often for the most accurate result.
*
* @param bytes Number of bytes transferred since last time.
*/
public void addBytes(final long bytes) {
final long currentTime = System.currentTimeMillis();
final long timeSpent = updateTimeSpent(currentTime);
updateCounters(bytes, timeSpent);
}
/**
* Gets the time spent since the last update, and changes the time
* of the last update to current time.
*
* @param currentTime The time when update was run.
* @return Time spent since the last update.
*/
protected long updateTimeSpent(final long currentTime) {
final long timeSpent = currentTime - previousTime;
previousTime = currentTime;
return timeSpent;
}
/**
* Updates the time and byte counters with the bytes added and the time spent.
* If time counted is more than a second, then the bytes per second is calculated.
*
* @param bytesAdded Number of bytes added since last update.
* @param timeSpent The time spent since last update.
*/
protected void updateCounters(final long bytesAdded, final long timeSpent) {
timeCounted += timeSpent;
bytesCounted += bytesAdded;
if (timeCounted >= ONE_SECOND) {
if (timeSpent > ONE_SECOND) {
calculateOnlyTimeSpent(bytesAdded, timeSpent);
} else {
calculateFirstSecond(bytesAdded, timeSpent);
}
}
}
/**
* Time spent since the last update is more than a second.
*
* <p>The speed is calculated as the average number of bytes added in one
* second of the spent time.</p>
*
* <p>Example: 1024 bytes transferred in 8 seconds is 128 bytes per second on average.</p>
*
* @param bytesAdded Number of bytes added since last update.
* @param timeSpent The time spent since last update.
*/
private void calculateOnlyTimeSpent(final long bytesAdded, final long timeSpent) {
bytesPerSec = getBytesAddedInTimeLeft(bytesAdded, timeSpent, ONE_SECOND);
timeCounted = 0;
bytesCounted = 0;
}
/**
* Time spent since the last update is less than a second,
* but the total time since last calculation is a second or more.
*
* <p>The speed is calculated by taking the previously counted bytes
* and adding the average number of bytes transferred in the remaining
* part of a second. The time and bytes left over are saved for later updates.</p>
*
* <p>Example: 1024 bytes has been counted earlier in 900ms, and 1024 bytes
* was now added inn 200ms. The average bytes added in the 100ms that is left
* of a second is 512. Giving 1536 bytes per sec, with 100ms and 512 bytes left.</p>
*
* @param bytesAdded Number of bytes added since last update.
* @param timeSpent The time spent since last update.
*/
private void calculateFirstSecond(final long bytesAdded, final long timeSpent) {
final long originalTimeCount = timeCounted - timeSpent;
final long originalByteCount = bytesCounted - bytesAdded;
final long timeLeftInSecond = ONE_SECOND - originalTimeCount;
final long bytesAddedInTimeLeft = getBytesAddedInTimeLeft(bytesAdded, timeSpent, timeLeftInSecond);
bytesPerSec = originalByteCount + bytesAddedInTimeLeft;
timeCounted %= ONE_SECOND;
bytesCounted -= bytesPerSec;
}
/**
* Gets the average number of bytes added in the time that is left.
*
* <p>Example: 1024 bytes added in 200ms with 100ms left is 512 bytes.</p>
*
* @param bytesAdded Number of bytes added in the time spent.
* @param timeSpent The time spent adding the number of bytes.
* @param timeLeft The number of milliseconds left in a second.
* @return The average number of bytes added in the remaining milliseconds of a second.
*/
private long getBytesAddedInTimeLeft(final long bytesAdded, final long timeSpent, final long timeLeft) {
final double percent = Tools.percent(timeLeft, timeSpent);
return (long) Tools.percentOf(percent, bytesAdded);
}
/**
* Gets the current number of bytes per seconds.
*
* @return The current number of bytes per second.
*/
public long getBytesPerSec() {
return bytesPerSec;
}
/**
* Get the number of bytes counted since the last second was calculated.
*
* @return Bytes counted since last second.
*/
public long getBytesCounted() {
return bytesCounted;
}
/**
* Get the number of milliseconds counted since the last second was calculated.
*
* @return Milliseconds counted since last second.
*/
public long getTimeCounted() {
return timeCounted;
}
}