From b7b7283ff9dd698efddaa334d76e8f4987ad345c Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Sun, 27 Mar 2011 22:19:50 +0000
Subject: [PATCH] - Use new UTF8StringBytes - Track number of SkipLevels in a
 SkipList - More double-checks - Cleanups, logging, generics

---
 .../client/naming/BlockfileNamingService.java | 25 +---------
 .../io/block/index/BSkipLevels.java           |  8 ++--
 .../metanotion/io/block/index/BSkipList.java  | 32 ++++++++++---
 .../metanotion/io/block/index/BSkipSpan.java  |  7 ++-
 .../metanotion/io/block/index/IBSkipSpan.java |  4 +-
 .../metanotion/util/skiplist/SkipLevels.java  |  2 +
 .../metanotion/util/skiplist/SkipList.java    | 46 +++++++++++++++----
 .../metanotion/util/skiplist/SkipSpan.java    |  8 ++--
 8 files changed, 78 insertions(+), 54 deletions(-)

diff --git a/core/java/src/net/i2p/client/naming/BlockfileNamingService.java b/core/java/src/net/i2p/client/naming/BlockfileNamingService.java
index a3cc2a851e..879a36adf4 100644
--- a/core/java/src/net/i2p/client/naming/BlockfileNamingService.java
+++ b/core/java/src/net/i2p/client/naming/BlockfileNamingService.java
@@ -35,6 +35,7 @@ import net.i2p.util.SecureFileOutputStream;
 
 import net.metanotion.io.Serializer;
 import net.metanotion.io.block.BlockFile;
+import net.metanotion.io.data.UTF8StringBytes;
 import net.metanotion.util.skiplist.SkipIterator;
 import net.metanotion.util.skiplist.SkipList;
 
@@ -79,7 +80,7 @@ public class BlockfileNamingService extends DummyNamingService {
     private volatile boolean _isClosed;
 
     private static final Serializer _infoSerializer = new PropertiesSerializer();
-    private static final Serializer _stringSerializer = new StringSerializer();
+    private static final Serializer _stringSerializer = new UTF8StringBytes();
     private static final Serializer _destSerializer = new DestEntrySerializer();
 
     private static final String HOSTS_DB = "hostsdb.blockfile";
@@ -743,28 +744,6 @@ public class BlockfileNamingService extends DummyNamingService {
         }
     }
 
-    /**
-     *  UTF-8 Serializer (the one in the lib is US-ASCII).
-     *  Used for all keys.
-     */
-    private static class StringSerializer implements Serializer {
-        public byte[] getBytes(Object o) {
-            try {
-                return ((String) o).getBytes("UTF-8");
-            } catch (UnsupportedEncodingException uee) {
-                throw new RuntimeException("No UTF-8", uee);
-            }
-        }
-
-        public Object construct(byte[] b) {
-            try {
-                return new String(b, "UTF-8");
-            } catch (UnsupportedEncodingException uee) {
-                throw new RuntimeException("No UTF-8", uee);
-            }
-        }
-    }
-
     /**
      *  Used for the values in the header skiplist
      *  Take care not to throw on any error.
diff --git a/core/java/src/net/metanotion/io/block/index/BSkipLevels.java b/core/java/src/net/metanotion/io/block/index/BSkipLevels.java
index 39a5bbc3b8..7a9dce53a1 100644
--- a/core/java/src/net/metanotion/io/block/index/BSkipLevels.java
+++ b/core/java/src/net/metanotion/io/block/index/BSkipLevels.java
@@ -65,14 +65,14 @@ public class BSkipLevels extends SkipLevels {
 		if (magic != MAGIC)
 			throw new IOException("Bad SkipLevels magic number 0x" + Long.toHexString(magic) + " on page " + levelPage);
 
-		bsl.levelHash.put(new Integer(this.levelPage), this);
+		bsl.levelHash.put(Integer.valueOf(this.levelPage), this);
 
 		int maxLen = bf.file.readUnsignedShort();
 		int nonNull = bf.file.readUnsignedShort();
 		if(maxLen < 1 || maxLen > MAX_SIZE || nonNull > maxLen)
 			throw new IOException("Invalid Level Skip size " + nonNull + " / " + maxLen);
 		spanPage = bf.file.readUnsignedInt();
-		bottom = (BSkipSpan) bsl.spanHash.get(new Integer(spanPage));
+		bottom = bsl.spanHash.get(Integer.valueOf(spanPage));
 
 		this.levels = new BSkipLevels[maxLen];
 		if (BlockFile.log.shouldLog(Log.DEBUG))
@@ -86,10 +86,10 @@ public class BSkipLevels extends SkipLevels {
 		for(int i = 0; i < nonNull; i++) {
 			int lp = lps[i];
 			if(lp != 0) {
-				levels[i] = (BSkipLevels) bsl.levelHash.get(new Integer(lp));
+				levels[i] = bsl.levelHash.get(Integer.valueOf(lp));
 				if(levels[i] == null) {
 					levels[i] = new BSkipLevels(bf, lp, bsl);
-					bsl.levelHash.put(new Integer(lp), levels[i]);
+					bsl.levelHash.put(Integer.valueOf(lp), levels[i]);
 				} else {
 				}
 			} else {
diff --git a/core/java/src/net/metanotion/io/block/index/BSkipList.java b/core/java/src/net/metanotion/io/block/index/BSkipList.java
index 9c1a329ed2..94b7baff1a 100644
--- a/core/java/src/net/metanotion/io/block/index/BSkipList.java
+++ b/core/java/src/net/metanotion/io/block/index/BSkipList.java
@@ -44,6 +44,7 @@ import net.metanotion.util.skiplist.*;
  *    first level page (unsigned int)
  *    size (unsigned int)
  *    spans (unsigned int)
+ *    levels (unsigned int)
  *
  * Always fits on one page.
  */
@@ -52,10 +53,11 @@ public class BSkipList extends SkipList {
 	public int firstSpanPage = 0;
 	public int firstLevelPage = 0;
 	public int skipPage = 0;
-	public BlockFile bf;
+	public final BlockFile bf;
+	private boolean isClosed;
 
-	public HashMap spanHash = new HashMap();
-	public HashMap levelHash = new HashMap();
+	final HashMap<Integer, BSkipSpan> spanHash = new HashMap();
+	final HashMap<Integer, SkipLevels> levelHash = new HashMap();
 
 	private final boolean fileOnly;
 
@@ -77,6 +79,7 @@ public class BSkipList extends SkipList {
 		firstLevelPage = bf.file.readUnsignedInt();
 		size = bf.file.readUnsignedInt();
 		spans = bf.file.readUnsignedInt();
+		levelCount = bf.file.readUnsignedInt();
 		//System.out.println(size + " " + spans); 
 
 		this.fileOnly = fileOnly;
@@ -91,18 +94,24 @@ public class BSkipList extends SkipList {
 	public void close() {
 		//System.out.println("Closing index " + size + " and " + spans);
 		flush();
-		first = null;
-		stack = null;
+		spanHash.clear();
+		levelHash.clear();
+		isClosed = true;
 	}
 
 	public void flush() {
+		if (isClosed) {
+			BlockFile.log.error("Already closed!! " + this, new Exception());
+			return;
+		}
 		try {
 			BlockFile.pageSeek(bf.file, skipPage);
 			bf.file.writeLong(MAGIC);
 			bf.file.writeInt(firstSpanPage);
 			bf.file.writeInt(firstLevelPage);
-			bf.file.writeInt(size);
-			bf.file.writeInt(spans);
+			bf.file.writeInt(Math.max(0, size));
+			bf.file.writeInt(Math.max(0, spans));
+			bf.file.writeInt(Math.max(0, levelCount));
 			
 		} catch (IOException ioe) { throw new RuntimeException("Error writing to database", ioe); }
 	}
@@ -177,6 +186,7 @@ public class BSkipList extends SkipList {
 	public void bslck(boolean isMeta, boolean fix) {
 		BlockFile.log.warn("    size " + this.size);
 		BlockFile.log.warn("    spans " + this.spans);
+		BlockFile.log.warn("    levels " + this.levelCount);
 		BlockFile.log.warn("    skipPage " + this.skipPage);
 		BlockFile.log.warn("    firstSpanPage " + this.firstSpanPage);
 		BlockFile.log.warn("    firstLevelPage " + this.firstLevelPage);
@@ -201,4 +211,12 @@ public class BSkipList extends SkipList {
 		if (items != this.size)
 			BlockFile.log.warn("****** size mismatch, header = " + this.size + " actual = " + items);
 	}
+
+	@Override
+	public String toString() {
+		String rv = getClass().getSimpleName() + " page " + skipPage;
+		if (isClosed)
+			rv += " CLOSED";
+		return rv;
+	}
 }
diff --git a/core/java/src/net/metanotion/io/block/index/BSkipSpan.java b/core/java/src/net/metanotion/io/block/index/BSkipSpan.java
index 4da6ed0de1..277b9e80ac 100644
--- a/core/java/src/net/metanotion/io/block/index/BSkipSpan.java
+++ b/core/java/src/net/metanotion/io/block/index/BSkipSpan.java
@@ -218,7 +218,7 @@ public class BSkipSpan extends SkipSpan {
 		bss.keySer = key;
 		bss.valSer = val;
 
-		bsl.spanHash.put(new Integer(spanPage), bss);
+		bsl.spanHash.put(Integer.valueOf(spanPage), bss);
 
 		BlockFile.pageSeek(bf.file, spanPage);
 
@@ -308,10 +308,9 @@ public class BSkipSpan extends SkipSpan {
 		this.prev = null;
 
 		BSkipSpan bss = this;
-		BSkipSpan temp;
 		int np = nextPage;
 		while(np != 0) {
-			temp = (BSkipSpan) bsl.spanHash.get(new Integer(np));
+			BSkipSpan temp = bsl.spanHash.get(Integer.valueOf(np));
 			if(temp != null) {
 				bss.next = temp;
 				break;
@@ -328,7 +327,7 @@ public class BSkipSpan extends SkipSpan {
 		bss = this;
 		np = prevPage;
 		while(np != 0) {
-			temp = (BSkipSpan) bsl.spanHash.get(new Integer(np));
+			BSkipSpan temp = bsl.spanHash.get(Integer.valueOf(np));
 			if(temp != null) {
 				bss.next = temp;
 				break;
diff --git a/core/java/src/net/metanotion/io/block/index/IBSkipSpan.java b/core/java/src/net/metanotion/io/block/index/IBSkipSpan.java
index e03e882655..8b65cb4c97 100644
--- a/core/java/src/net/metanotion/io/block/index/IBSkipSpan.java
+++ b/core/java/src/net/metanotion/io/block/index/IBSkipSpan.java
@@ -246,7 +246,7 @@ public class IBSkipSpan extends BSkipSpan {
 		IBSkipSpan temp;
 		int np = nextPage;
 		while(np != 0) {
-			temp = (IBSkipSpan) bsl.spanHash.get(new Integer(np));
+			temp = (IBSkipSpan) bsl.spanHash.get(Integer.valueOf(np));
 			if(temp != null) {
 				bss.next = temp;
 				break;
@@ -264,7 +264,7 @@ public class IBSkipSpan extends BSkipSpan {
 		bss = this;
 		np = prevPage;
 		while(np != 0) {
-			temp = (IBSkipSpan) bsl.spanHash.get(new Integer(np));
+			temp = (IBSkipSpan) bsl.spanHash.get(Integer.valueOf(np));
 			if(temp != null) {
 				bss.next = temp;
 				break;
diff --git a/core/java/src/net/metanotion/util/skiplist/SkipLevels.java b/core/java/src/net/metanotion/util/skiplist/SkipLevels.java
index 8e090c4896..f2af9a4eb4 100644
--- a/core/java/src/net/metanotion/util/skiplist/SkipLevels.java
+++ b/core/java/src/net/metanotion/util/skiplist/SkipLevels.java
@@ -187,6 +187,7 @@ public class SkipLevels {
 				}
 				res[1] = null;
 			}
+			sl.delSpan(res[1] != null);
 		}
 		if((bottom.nKeys == 0) && (sl.first != bottom)) { this.killInstance(); }
 		return res;
@@ -243,6 +244,7 @@ public class SkipLevels {
 				if(levels.length < height)
 					return slvls;
 			}
+			sl.addSpan(height != 0);
 		}
 		return null;
 	}
diff --git a/core/java/src/net/metanotion/util/skiplist/SkipList.java b/core/java/src/net/metanotion/util/skiplist/SkipList.java
index 5fe2c72a4d..132e4e8f7f 100644
--- a/core/java/src/net/metanotion/util/skiplist/SkipList.java
+++ b/core/java/src/net/metanotion/util/skiplist/SkipList.java
@@ -32,16 +32,21 @@ import java.util.Random;
 
 import net.i2p.util.RandomSource;
 
-import net.metanotion.io.block.BlockFile;
+//import net.metanotion.io.block.BlockFile;
 
 public class SkipList {
+	/** the probability of each next higher level */
+	private static final int P = 2;
+	private static final int MIN_SLOTS = 4;
+	// these two are really final
 	protected SkipSpan first;
 	protected SkipLevels stack;
 	// I2P mod
 	public static final Random rng = RandomSource.getInstance();
 
-	public int size=0;
-	public int spans=0;
+	protected int size;
+	protected int spans;
+	protected int levelCount;
 
 	public void flush() { }
 	protected SkipList() { }
@@ -56,11 +61,34 @@ public class SkipList {
 		first = new SkipSpan(span);
 		stack = new SkipLevels(1, first);
 		spans = 1;
+		levelCount = 1;
 		//rng = new Random(System.currentTimeMillis());
 	}
 
 	public int size() { return size; }
 
+	public void addItem() {
+		size++;
+	}
+
+	public void delItem() {
+		if (size > 0)
+		       size--;
+	}
+
+	public void addSpan(boolean addLevel) {
+		spans++;
+		if (addLevel)
+			levelCount++;
+	}
+
+	public void delSpan(boolean delLevel) {
+		if (spans > 0)
+			spans--;
+		if (delLevel && levelCount > 0)
+			levelCount--;
+	}
+
 	/**
 	 *  @return log2(spans), minimum 4
 	 */
@@ -68,21 +96,21 @@ public class SkipList {
 		int hob = 0, s = spans;
 		while(s > 0) {
 			hob++;
-			s /= 2;
+			s /= P;
 		}
-		return Math.max(hob, 4);
+		return Math.max(hob, MIN_SLOTS);
 	}
 
 	/**
-	 *  @return 0..maxLevels()
+	 *  @return 0..maxLevels(), each successive one with probability 1 / P
 	 */
 	public int generateColHeight() {
 		int bits = rng.nextInt();
 		int max = maxLevels();
 		for(int res = 0; res < max; res++) {
-			if (bits % 2 == 0)
+			if (bits % P == 0)
 				return res;
-			bits /= 2;
+			bits /= P;
 		}
 		return max;
 	}
@@ -93,7 +121,7 @@ public class SkipList {
 		SkipLevels slvls = stack.put(stack.levels.length - 1, key, val, this);
 		if(slvls != null) {
 			// grow our stack
-			BlockFile.log.info("Top level old hgt " + stack.levels.length +  " new hgt " + slvls.levels.length);
+			//BlockFile.log.info("Top level old hgt " + stack.levels.length +  " new hgt " + slvls.levels.length);
 			SkipLevels[] levels = new SkipLevels[slvls.levels.length];
 			for(int i=0;i < slvls.levels.length; i++) {
 				if(i < stack.levels.length) {
diff --git a/core/java/src/net/metanotion/util/skiplist/SkipSpan.java b/core/java/src/net/metanotion/util/skiplist/SkipSpan.java
index 655002be42..ae2cc9ef7d 100644
--- a/core/java/src/net/metanotion/util/skiplist/SkipSpan.java
+++ b/core/java/src/net/metanotion/util/skiplist/SkipSpan.java
@@ -138,7 +138,6 @@ public class SkipSpan {
 
 	private void split(int loc, Comparable key, Object val, SkipList sl) {
 		SkipSpan right = newInstance(sl);
-		sl.spans++;
 
 		if(this.next != null) { this.next.prev = right; }
 		right.next = this.next;
@@ -175,7 +174,7 @@ public class SkipSpan {
 	 *  @return the new span if it caused a split, else null if it went in this span
 	 */
 	private SkipSpan insert(int loc, Comparable key, Object val, SkipList sl) {
-		sl.size++;
+		sl.addItem();
 		if(nKeys == keys.length) {
 			// split.
 			split(loc, key, val, sl);
@@ -194,7 +193,7 @@ public class SkipSpan {
 	 */
 	public SkipSpan put(Comparable key, Object val, SkipList sl)	{
 		if(nKeys == 0) {
-			sl.size++;
+			sl.addItem();
 			keys[0] = key;
 			vals[0] = val;
 			nKeys++;
@@ -256,9 +255,8 @@ public class SkipSpan {
 		Object o = vals[loc];
 		Object[] res = new Object[2];
 		res[0] = o;
-		sl.size--;
+		sl.delItem();
 		if(nKeys == 1) {
-			if(sl.spans > 1) { sl.spans--; }
 			if((this.prev == null) && (this.next != null)) {
 				res[1] = this.next;
 				// We're the first node in the list... copy the next node over and kill it. See also bottom of SkipLevels.java
-- 
GitLab