/******************************************************************************* * Copyright (c) 2008, 2011 Thomas Holland (thomas@innot.de) and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Thomas Holland - initial API and implementation *******************************************************************************/ package de.innot.avreclipse.util; /** * Utility class to calculate an average download rate. * <p> * Objects of this class calculate the average rate of a download. To smooth the rate, the download * rate of the last <code>samplesize</code> blocks is used. The sample size can be set and has a * default of 20. * </p> * <p> * The timing is started with the {@link #start()} method, which should be as close as possible to * the actual start of the download. * </p> * Usage example with a sample size of 100: * * <pre> * DownloadRateCalculator dac = new DownloadRateCalculator(100); * dac.start(); * while (readbytes) { * int rate = dac.getCurrentRate(bytesread); * } * </pre> * * * @author Thomas Holland * @since 2.2 * */ public class DownloadRateCalculator { private final static int DEFAULT_SAMPLE_SIZE = 50; // Ring buffer for the last time/bytes read private long[][] samples; // Index for the ring buffer private int currentindex; // the start time of the download private long starttime; // total number of bytes downloaded private int totalbytes; // Number of nanoseconds in a second // Just a convenience so I don't miss a zero. private final static long ONEBILLION = 1000 * 1000 * 1000; /** * Create a new DownloadRateCalculator with the default sample size. */ public DownloadRateCalculator() { this(DEFAULT_SAMPLE_SIZE); } /** * Create a new DownloadRateCalculator with the given sample size. * * @see #setSampleSize(int) * * @param samplesize * Size of the sample buffer. */ public DownloadRateCalculator(int samplesize) { setSampleSize(samplesize); } /** * Sets the size of the samples buffer. * <p> * While high sample sizes will result in a smoother rate, they will also cause the average rate * to lag behind the real download rate.<br> * Very small samplesizes will make the returned rate erratic because the time for a * Stream.read() will differ dramatically for single calls (depending on the fill state of the * internal buffers). * </p> * <p> * A good samplesize is probably around 10% of the number of blocks and at least 20. * </p> * * @param size */ public void setSampleSize(int size) { samples = new long[(size < 3 ? 3 : size)][2]; currentindex = 0; } /** * Start the timing of the download. */ public void start() { currentindex = 0; starttime = System.nanoTime(); totalbytes = 0; // clear the samples array for (int i = 0; i < samples.length; i++) { samples[i][0] = starttime; samples[i][1] = 0; } } /** * Return the current average download rate in <code>bytes per second</code>. * <p> * The returned rate is the average of the last <code>samplesize</code> rates. * </p> * * @param bytesread * The number of bytes read since the last call to this method (or since * instantiation) * @return long with the current download rate in Bytes per Second */ public long getCurrentRate(int bytesread) { long currenttime = System.nanoTime(); totalbytes += bytesread; // Store the current time and total bytes read so far in the buffer samples[currentindex][0] = currenttime; samples[currentindex][1] = totalbytes; currentindex++; if (currentindex == samples.length) { currentindex = 0; } // Get the least recent time/totalbytes point and calculate the download // rate from that point to the current time/totalbytes // The oldest point is the one just one ahead in the buffer int fromindex = currentindex == samples.length - 1 ? 0 : currentindex; long fromtime = samples[fromindex][0]; long frombytes = samples[fromindex][1]; long deltatime = currenttime - fromtime; long deltabytes = totalbytes - frombytes; // convert to bytes per second long rate = (deltabytes * ONEBILLION) / deltatime; return rate; } private final static int KBYTE = 1024; private final static int MBYTE = KBYTE * KBYTE; /** * Returns the given current rate in a human readable format. * <p> * The returned String is automatically adjusted to MByte / KByte / Bytes per second. Changeover * to the next higher unit is done at 2.000 of the previous unit. * </p> * * @return <code>String</code> with the rate in human readable format. */ public String getCurrentRateString(int bytesread) { long rate = getCurrentRate(bytesread); if (rate > 2 * MBYTE) { return Long.toString(rate / MBYTE) + " MBytes/sec"; } else if (rate > 2 * KBYTE) { return Long.toString(rate / KBYTE) + " KBytes/sec"; } else { return Long.toString(rate) + " Bytes/sec"; } } }