Author: toad
Date: 2007-03-09 02:28:26 +0000 (Fri, 09 Mar 2007)
New Revision: 12049

Added:
   trunk/freenet/src/freenet/support/TimeSortedHashtable.java
Modified:
   trunk/freenet/src/freenet/node/LocationManager.java
   trunk/freenet/src/freenet/support/LRUHashtable.java
Log:
Fix too-many-locations bug

Modified: trunk/freenet/src/freenet/node/LocationManager.java
===================================================================
--- trunk/freenet/src/freenet/node/LocationManager.java 2007-03-09 00:32:33 UTC 
(rev 12048)
+++ trunk/freenet/src/freenet/node/LocationManager.java 2007-03-09 02:28:26 UTC 
(rev 12049)
@@ -4,14 +4,9 @@
 package freenet.node;

 import java.security.MessageDigest;
-import java.util.Collection;
 import java.util.Enumeration;
 import java.util.Hashtable;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
 import java.util.Vector;
-import java.util.Date;

 import freenet.crypt.RandomSource;
 import freenet.crypt.SHA256;
@@ -23,6 +18,7 @@
 import freenet.support.Fields;
 import freenet.support.Logger;
 import freenet.support.ShortBuffer;
+import freenet.support.TimeSortedHashtable;

 /**
  * @author amphibian
@@ -1027,8 +1023,10 @@
         recentlyForwardedIDs.remove(new Long(item.outgoingID));
     }

-    private final TreeMap knownLocs = new TreeMap();
+    private final long MAX_AGE = 7*24*60*60*1000;

+    private final TimeSortedHashtable knownLocs = new TimeSortedHashtable();
+    
     void registerLocationLink(double d, double t) {
        if(logMINOR) Logger.minor(this, "Known Link: "+d+ ' ' +t);
     }
@@ -1036,28 +1034,23 @@
     void registerKnownLocation(double d) {
        if(logMINOR) Logger.minor(this, "Known Location: "+d);
         Double dd = new Double(d);
-        Date timestamp = new Date();
-        Long longTime = new Long(timestamp.getTime());
+        long now = System.currentTimeMillis();

         synchronized(knownLocs) {
-                       // FIXME: The TreeMap size will keep on increasing...
-                       // knownLocs.values().remove(dd); would be costy
-                       // Maybe the best solution is to do a 
-                       // knownLocs = knownLocs.headMap(longTime - 
arbitrary_value)
-               
-                       //Add the location to the map with the current 
timestamp as key
-                       knownLocs.put(longTime, dd);    
+               Logger.minor(this, "Adding location "+dd+" knownLocs size 
"+knownLocs.size());
+               knownLocs.push(dd, now);
+               Logger.minor(this, "Added location "+dd+" knownLocs size 
"+knownLocs.size());
+               knownLocs.removeBefore(now - MAX_AGE);
+               Logger.minor(this, "Added and pruned location "+dd+" knownLocs 
size "+knownLocs.size());
         }
                if(logMINOR) Logger.minor(this, "Estimated net size(session): 
"+knownLocs.size());
     }

     //Return the estimated network size based on locations seen after 
timestamp or for the whole session if -1
     public int getNetworkSizeEstimate(long timestamp) {
-       final SortedMap temp;
        synchronized (knownLocs) {
-               temp = (timestamp == -1 ? knownLocs : knownLocs.tailMap(new 
Long(timestamp)));
+               return knownLocs.countValuesAfter(timestamp);
        }
-               return temp.size();
        }

     /**
@@ -1066,12 +1059,8 @@
      * @Return an array containing two cells : Locations and their last seen 
time for a given timestamp.
      */
     public Object[] getKnownLocations(long timestamp) {
-               final SortedMap temp;
        synchronized (knownLocs) {
-               temp = timestamp == -1 ? knownLocs :  knownLocs.tailMap(new 
Long(timestamp));
-               Set keys = temp.keySet();
-               Collection values = temp.values();
-               return new Object[]{ (Double[])values.toArray(new 
Double[keys.size()]), (Long[])keys.toArray(new Long[values.size()])};
+               return knownLocs.pairsAfter(timestamp, new 
Double[knownLocs.size()]);
        }
        }
 }

Modified: trunk/freenet/src/freenet/support/LRUHashtable.java
===================================================================
--- trunk/freenet/src/freenet/support/LRUHashtable.java 2007-03-09 00:32:33 UTC 
(rev 12048)
+++ trunk/freenet/src/freenet/support/LRUHashtable.java 2007-03-09 02:28:26 UTC 
(rev 12049)
@@ -47,6 +47,14 @@
         }
     }

+       public final synchronized Object peekValue() {
+        if ( list.size() > 0 ) {
+            return ((QItem)hash.get(((QItem)list.pop()).obj)).obj;
+        } else {
+            return null;
+        }
+       }
+    
     /**
      * @return Least recently pushed value.
      */
@@ -120,4 +128,5 @@
                return super.toString()+": "+obj+ ' ' +value;
         }
     }
+
 }

Added: trunk/freenet/src/freenet/support/TimeSortedHashtable.java
===================================================================
--- trunk/freenet/src/freenet/support/TimeSortedHashtable.java                  
        (rev 0)
+++ trunk/freenet/src/freenet/support/TimeSortedHashtable.java  2007-03-09 
02:28:26 UTC (rev 12049)
@@ -0,0 +1,195 @@
+package freenet.support;
+
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * Variant on LRUHashtable which provides an efficient how-many-since-time-T 
operation.
+ */
+public class TimeSortedHashtable {
+
+       private class Element {
+               
+               Element(long t, Comparable v) {
+                       time = t;
+                       value = v;
+               }
+               
+               long time;
+               final Comparable value;
+               
+               public int compareTo(Object o) {
+                       Element e = (Element) o;
+                       if(time > e.time) return 1;
+                       if(time < e.time) return -1;
+                       return value.compareTo(e.value);
+               }
+       }
+
+       private class MyComparator implements Comparator {
+
+               public int compare(Object arg0, Object arg1) {
+                       if(arg0 instanceof Long && arg1 instanceof Long) return 
((Long)arg0).compareTo(arg1);
+                       if(arg0 instanceof Element && arg1 instanceof Element) 
return ((Element)arg0).compareTo(arg1);
+                       if(arg0 instanceof Long) {
+                               long l = ((Long)arg0).longValue();
+                               Element e = (Element)arg1;
+                               if(l > e.time) return 1;
+                               if(l < e.time) return -1;
+                               return 0;
+                       } else {
+                               // arg1 instanceof Long
+                               Element e = (Element)arg0;
+                               long l = ((Long)arg1).longValue();
+                               if(e.time > l) return 1;
+                               if(e.time < l) return -1;
+                               return 0;
+                       }
+               }
+               
+       }
+       
+    private final TreeSet elements = new TreeSet(new MyComparator());
+    private final HashMap valueToElement = new HashMap();
+
+    public final void push(Comparable value) {
+       push(value, System.currentTimeMillis());
+    }
+    
+    /**
+     *       push()ing an object that is already in
+     *       the queue moves that object to the most
+     *       recently used position, but doesn't add
+     *       a duplicate entry in the queue.
+     * @param now 
+     */
+    public final synchronized void push(Comparable value, long now) {
+
+       assert(elements.size() == valueToElement.size());
+       
+       Element e = (Element) valueToElement.get(value);
+       
+       if(e == null) {
+               e = new Element(now, value);
+               elements.add(e);
+               valueToElement.put(value, e);
+       } else {
+               elements.remove(e);
+               e.time = now;
+               elements.add(e);
+       }
+       
+       assert(elements.size() == valueToElement.size());
+    } 
+
+    /**
+     * @return Least recently pushed value.
+     */
+    public final synchronized Comparable popValue() {
+       assert(elements.size() == valueToElement.size());
+       
+       Element e = (Element) elements.first();
+       valueToElement.remove(e.value);
+       
+       assert(elements.size() == valueToElement.size());
+       return e.value;
+    }
+    
+       public final synchronized Object peekValue() {
+       return ((Element) elements.first()).value;
+       }
+    
+    public final int size() {
+        return elements.size();
+    }
+    
+    public final synchronized boolean removeValue(Comparable value) {
+       assert(elements.size() == valueToElement.size());
+       Element e = (Element) valueToElement.remove(value);
+       if(e == null) return false;
+       elements.remove(e);
+       assert(elements.size() == valueToElement.size());
+       return true;
+    }
+    
+    public final synchronized boolean containsValue(Comparable key) {
+       return valueToElement.containsKey(key);
+    }
+    
+    /**
+     * Note that this does not automatically promote the key. You have
+     * to do that by hand with push(key, value).
+     */
+    public final synchronized long getTime(Object value) {
+       Element e = (Element) valueToElement.remove(value);
+       if(e == null) return -1;
+       return e.time;
+    }
+    
+    /**
+     * @return The set of times after the given time.
+     */
+    public final synchronized Long[] timesAfter(long t) {
+       Long time = new Long(t);
+       
+       Set s = elements.tailSet(time);
+       
+       Long[] times = new Long[s.size()];
+       int x = 0;
+       for(Iterator i = s.iterator();i.hasNext();) {
+               times[x++] = new Long(((Element)i.next()).time);
+       }
+       
+       return times;
+    }
+    
+    /**
+     * @return The set of values after the given time.
+     */
+    public final synchronized Comparable[] valuesAfter(long t, Comparable[] 
values) {
+       Long time = new Long(t);
+       
+       Set s = elements.tailSet(time);
+       
+       int x = 0;
+       for(Iterator i = s.iterator();i.hasNext();) {
+               values[x++] = ((Element)i.next()).value;
+       }
+       
+       return values;
+    }
+
+       public int countValuesAfter(long t) {
+       Long time = new Long(t);
+       
+       Set s = elements.tailSet(time);
+       
+       return s.size();
+       }
+    
+    /**
+     * Remove all entries before the given time.
+     */
+       public final synchronized void removeBefore(long t) {
+       assert(elements.size() == valueToElement.size());
+       
+       Long time = new Long(t);
+       Set s = elements.headSet(time);
+       
+       for(Iterator i = s.iterator();i.hasNext();) {
+               Element e = (Element) i.next();
+               valueToElement.remove(e.value);
+               i.remove();
+       }
+       
+       assert(elements.size() == valueToElement.size());
+       }
+
+       public final synchronized Object[] pairsAfter(long timestamp, 
Comparable[] valuesArray) {
+               return new Object[] { valuesAfter(timestamp, valuesArray), 
timesAfter(timestamp) };
+       }
+
+}


Reply via email to