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