/* This code is part of Freenet. It is distributed under the GNU General * Public License, version 2 (or at your option any later version). See * http://www.gnu.org/ for further details of the GPL. */ package freenet.support; import java.util.LinkedHashMap; import java.util.Map; /** * Keeps track of times at which sequence numbers were reported as sent, with bounded capacity. * Cached times at which {@link #sent(int)} was invoked are dropped on a first-in first-out basis. * * @author bertm */ public class SentTimeCache { /** * LinkedHashMap with int keys, long values, that has bounded capacity. */ private static class BoundedSentTimeMap extends LinkedHashMap<Integer, Long> { private static final long serialVersionUID = 0; private final int maxSize; /** * Constructs a map with the given maximum (and initial) size. */ BoundedSentTimeMap(int maxSize) { super(maxSize); if (maxSize <= 0) { throw new IllegalArgumentException("Negative or zero maxSize"); } this.maxSize = maxSize; } /** * Automatically maintains the maximum size by returning true if the capacity is exceeded, * indicating that the eldest entry must be removed. * @see LinkedHashMap#removeEldestEntry(Map.Entry) */ protected boolean removeEldestEntry(Map.Entry<Integer, Long> eldest) { return size() > maxSize; } } /** * The inner cache. */ private BoundedSentTimeMap cache; /** * Constructs a sent time cache with the given maximal capacity. */ public SentTimeCache(int maxSize) { cache = new BoundedSentTimeMap(maxSize); } /** * Reports the given sequence number as being sent at the given time. If the cache is at full * capacity, this will lead to the oldest entry being dropped. If the sequence number was * already in this cache, the given time will be associated with the sequence number, but the * order in which sequence numbers are dropped from the cache will not be affected. * @param seqnum the sequence number * @param time the sent time in milliseconds */ public synchronized void report(int seqnum, long time) { cache.put(seqnum, time); } /** * Convenience wrapper for {@link #report(int, long)}. * Reports the given sequence number as being sent right now. If the cache is at full capacity, * this will lead to the oldest entry being dropped. If the sequence number was already in this * cache, the current time will be associated with the sequence number, but the order in which * sequence numbers are dropped from the cache will not be affected. * @param seqnum the sequence number * @see SentTimeCache#report(int, long) */ public synchronized void sent(int seqnum) { long time = System.currentTimeMillis(); report(seqnum, time); } /** * Queries the sent time for the given sequence number and removes it from the cache. * @param seqnum the sequence number * @returns The time at which the sequence number was reported to {@link #sent(int)}, in * milliseconds, or a negative value if the sequence number was not in this cache. */ public synchronized long queryAndRemove(int seqnum) { Long ret = cache.remove(seqnum); if (ret == null) { return -1; } return ret; } /** * Queries the number of items currently held by this cache. * @return The number of items in this cache. */ synchronized int size() { return cache.size(); } }