- Use new UTF8StringBytes

- Track number of SkipLevels in a SkipList
- More double-checks
- Cleanups, logging, generics
This commit is contained in:
zzz
2011-03-27 22:19:50 +00:00
parent bc231b51b5
commit b7b7283ff9
8 changed files with 78 additions and 54 deletions

View File

@ -35,6 +35,7 @@ import net.i2p.util.SecureFileOutputStream;
import net.metanotion.io.Serializer; import net.metanotion.io.Serializer;
import net.metanotion.io.block.BlockFile; import net.metanotion.io.block.BlockFile;
import net.metanotion.io.data.UTF8StringBytes;
import net.metanotion.util.skiplist.SkipIterator; import net.metanotion.util.skiplist.SkipIterator;
import net.metanotion.util.skiplist.SkipList; import net.metanotion.util.skiplist.SkipList;
@ -79,7 +80,7 @@ public class BlockfileNamingService extends DummyNamingService {
private volatile boolean _isClosed; private volatile boolean _isClosed;
private static final Serializer _infoSerializer = new PropertiesSerializer(); 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 Serializer _destSerializer = new DestEntrySerializer();
private static final String HOSTS_DB = "hostsdb.blockfile"; 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 * Used for the values in the header skiplist
* Take care not to throw on any error. * Take care not to throw on any error.

View File

@ -65,14 +65,14 @@ public class BSkipLevels extends SkipLevels {
if (magic != MAGIC) if (magic != MAGIC)
throw new IOException("Bad SkipLevels magic number 0x" + Long.toHexString(magic) + " on page " + levelPage); 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 maxLen = bf.file.readUnsignedShort();
int nonNull = bf.file.readUnsignedShort(); int nonNull = bf.file.readUnsignedShort();
if(maxLen < 1 || maxLen > MAX_SIZE || nonNull > maxLen) if(maxLen < 1 || maxLen > MAX_SIZE || nonNull > maxLen)
throw new IOException("Invalid Level Skip size " + nonNull + " / " + maxLen); throw new IOException("Invalid Level Skip size " + nonNull + " / " + maxLen);
spanPage = bf.file.readUnsignedInt(); spanPage = bf.file.readUnsignedInt();
bottom = (BSkipSpan) bsl.spanHash.get(new Integer(spanPage)); bottom = bsl.spanHash.get(Integer.valueOf(spanPage));
this.levels = new BSkipLevels[maxLen]; this.levels = new BSkipLevels[maxLen];
if (BlockFile.log.shouldLog(Log.DEBUG)) if (BlockFile.log.shouldLog(Log.DEBUG))
@ -86,10 +86,10 @@ public class BSkipLevels extends SkipLevels {
for(int i = 0; i < nonNull; i++) { for(int i = 0; i < nonNull; i++) {
int lp = lps[i]; int lp = lps[i];
if(lp != 0) { if(lp != 0) {
levels[i] = (BSkipLevels) bsl.levelHash.get(new Integer(lp)); levels[i] = bsl.levelHash.get(Integer.valueOf(lp));
if(levels[i] == null) { if(levels[i] == null) {
levels[i] = new BSkipLevels(bf, lp, bsl); 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 {
} }
} else { } else {

View File

@ -44,6 +44,7 @@ import net.metanotion.util.skiplist.*;
* first level page (unsigned int) * first level page (unsigned int)
* size (unsigned int) * size (unsigned int)
* spans (unsigned int) * spans (unsigned int)
* levels (unsigned int)
* *
* Always fits on one page. * Always fits on one page.
*/ */
@ -52,10 +53,11 @@ public class BSkipList extends SkipList {
public int firstSpanPage = 0; public int firstSpanPage = 0;
public int firstLevelPage = 0; public int firstLevelPage = 0;
public int skipPage = 0; public int skipPage = 0;
public BlockFile bf; public final BlockFile bf;
private boolean isClosed;
public HashMap spanHash = new HashMap(); final HashMap<Integer, BSkipSpan> spanHash = new HashMap();
public HashMap levelHash = new HashMap(); final HashMap<Integer, SkipLevels> levelHash = new HashMap();
private final boolean fileOnly; private final boolean fileOnly;
@ -77,6 +79,7 @@ public class BSkipList extends SkipList {
firstLevelPage = bf.file.readUnsignedInt(); firstLevelPage = bf.file.readUnsignedInt();
size = bf.file.readUnsignedInt(); size = bf.file.readUnsignedInt();
spans = bf.file.readUnsignedInt(); spans = bf.file.readUnsignedInt();
levelCount = bf.file.readUnsignedInt();
//System.out.println(size + " " + spans); //System.out.println(size + " " + spans);
this.fileOnly = fileOnly; this.fileOnly = fileOnly;
@ -91,18 +94,24 @@ public class BSkipList extends SkipList {
public void close() { public void close() {
//System.out.println("Closing index " + size + " and " + spans); //System.out.println("Closing index " + size + " and " + spans);
flush(); flush();
first = null; spanHash.clear();
stack = null; levelHash.clear();
isClosed = true;
} }
public void flush() { public void flush() {
if (isClosed) {
BlockFile.log.error("Already closed!! " + this, new Exception());
return;
}
try { try {
BlockFile.pageSeek(bf.file, skipPage); BlockFile.pageSeek(bf.file, skipPage);
bf.file.writeLong(MAGIC); bf.file.writeLong(MAGIC);
bf.file.writeInt(firstSpanPage); bf.file.writeInt(firstSpanPage);
bf.file.writeInt(firstLevelPage); bf.file.writeInt(firstLevelPage);
bf.file.writeInt(size); bf.file.writeInt(Math.max(0, size));
bf.file.writeInt(spans); 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); } } 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) { public void bslck(boolean isMeta, boolean fix) {
BlockFile.log.warn(" size " + this.size); BlockFile.log.warn(" size " + this.size);
BlockFile.log.warn(" spans " + this.spans); BlockFile.log.warn(" spans " + this.spans);
BlockFile.log.warn(" levels " + this.levelCount);
BlockFile.log.warn(" skipPage " + this.skipPage); BlockFile.log.warn(" skipPage " + this.skipPage);
BlockFile.log.warn(" firstSpanPage " + this.firstSpanPage); BlockFile.log.warn(" firstSpanPage " + this.firstSpanPage);
BlockFile.log.warn(" firstLevelPage " + this.firstLevelPage); BlockFile.log.warn(" firstLevelPage " + this.firstLevelPage);
@ -201,4 +211,12 @@ public class BSkipList extends SkipList {
if (items != this.size) if (items != this.size)
BlockFile.log.warn("****** size mismatch, header = " + this.size + " actual = " + items); 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;
}
} }

View File

@ -218,7 +218,7 @@ public class BSkipSpan extends SkipSpan {
bss.keySer = key; bss.keySer = key;
bss.valSer = val; bss.valSer = val;
bsl.spanHash.put(new Integer(spanPage), bss); bsl.spanHash.put(Integer.valueOf(spanPage), bss);
BlockFile.pageSeek(bf.file, spanPage); BlockFile.pageSeek(bf.file, spanPage);
@ -308,10 +308,9 @@ public class BSkipSpan extends SkipSpan {
this.prev = null; this.prev = null;
BSkipSpan bss = this; BSkipSpan bss = this;
BSkipSpan temp;
int np = nextPage; int np = nextPage;
while(np != 0) { while(np != 0) {
temp = (BSkipSpan) bsl.spanHash.get(new Integer(np)); BSkipSpan temp = bsl.spanHash.get(Integer.valueOf(np));
if(temp != null) { if(temp != null) {
bss.next = temp; bss.next = temp;
break; break;
@ -328,7 +327,7 @@ public class BSkipSpan extends SkipSpan {
bss = this; bss = this;
np = prevPage; np = prevPage;
while(np != 0) { while(np != 0) {
temp = (BSkipSpan) bsl.spanHash.get(new Integer(np)); BSkipSpan temp = bsl.spanHash.get(Integer.valueOf(np));
if(temp != null) { if(temp != null) {
bss.next = temp; bss.next = temp;
break; break;

View File

@ -246,7 +246,7 @@ public class IBSkipSpan extends BSkipSpan {
IBSkipSpan temp; IBSkipSpan temp;
int np = nextPage; int np = nextPage;
while(np != 0) { while(np != 0) {
temp = (IBSkipSpan) bsl.spanHash.get(new Integer(np)); temp = (IBSkipSpan) bsl.spanHash.get(Integer.valueOf(np));
if(temp != null) { if(temp != null) {
bss.next = temp; bss.next = temp;
break; break;
@ -264,7 +264,7 @@ public class IBSkipSpan extends BSkipSpan {
bss = this; bss = this;
np = prevPage; np = prevPage;
while(np != 0) { while(np != 0) {
temp = (IBSkipSpan) bsl.spanHash.get(new Integer(np)); temp = (IBSkipSpan) bsl.spanHash.get(Integer.valueOf(np));
if(temp != null) { if(temp != null) {
bss.next = temp; bss.next = temp;
break; break;

View File

@ -187,6 +187,7 @@ public class SkipLevels {
} }
res[1] = null; res[1] = null;
} }
sl.delSpan(res[1] != null);
} }
if((bottom.nKeys == 0) && (sl.first != bottom)) { this.killInstance(); } if((bottom.nKeys == 0) && (sl.first != bottom)) { this.killInstance(); }
return res; return res;
@ -243,6 +244,7 @@ public class SkipLevels {
if(levels.length < height) if(levels.length < height)
return slvls; return slvls;
} }
sl.addSpan(height != 0);
} }
return null; return null;
} }

View File

@ -32,16 +32,21 @@ import java.util.Random;
import net.i2p.util.RandomSource; import net.i2p.util.RandomSource;
import net.metanotion.io.block.BlockFile; //import net.metanotion.io.block.BlockFile;
public class SkipList { 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 SkipSpan first;
protected SkipLevels stack; protected SkipLevels stack;
// I2P mod // I2P mod
public static final Random rng = RandomSource.getInstance(); public static final Random rng = RandomSource.getInstance();
public int size=0; protected int size;
public int spans=0; protected int spans;
protected int levelCount;
public void flush() { } public void flush() { }
protected SkipList() { } protected SkipList() { }
@ -56,11 +61,34 @@ public class SkipList {
first = new SkipSpan(span); first = new SkipSpan(span);
stack = new SkipLevels(1, first); stack = new SkipLevels(1, first);
spans = 1; spans = 1;
levelCount = 1;
//rng = new Random(System.currentTimeMillis()); //rng = new Random(System.currentTimeMillis());
} }
public int size() { return size; } 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 * @return log2(spans), minimum 4
*/ */
@ -68,21 +96,21 @@ public class SkipList {
int hob = 0, s = spans; int hob = 0, s = spans;
while(s > 0) { while(s > 0) {
hob++; 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() { public int generateColHeight() {
int bits = rng.nextInt(); int bits = rng.nextInt();
int max = maxLevels(); int max = maxLevels();
for(int res = 0; res < max; res++) { for(int res = 0; res < max; res++) {
if (bits % 2 == 0) if (bits % P == 0)
return res; return res;
bits /= 2; bits /= P;
} }
return max; return max;
} }
@ -93,7 +121,7 @@ public class SkipList {
SkipLevels slvls = stack.put(stack.levels.length - 1, key, val, this); SkipLevels slvls = stack.put(stack.levels.length - 1, key, val, this);
if(slvls != null) { if(slvls != null) {
// grow our stack // 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]; SkipLevels[] levels = new SkipLevels[slvls.levels.length];
for(int i=0;i < slvls.levels.length; i++) { for(int i=0;i < slvls.levels.length; i++) {
if(i < stack.levels.length) { if(i < stack.levels.length) {

View File

@ -138,7 +138,6 @@ public class SkipSpan {
private void split(int loc, Comparable key, Object val, SkipList sl) { private void split(int loc, Comparable key, Object val, SkipList sl) {
SkipSpan right = newInstance(sl); SkipSpan right = newInstance(sl);
sl.spans++;
if(this.next != null) { this.next.prev = right; } if(this.next != null) { this.next.prev = right; }
right.next = this.next; 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 * @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) { private SkipSpan insert(int loc, Comparable key, Object val, SkipList sl) {
sl.size++; sl.addItem();
if(nKeys == keys.length) { if(nKeys == keys.length) {
// split. // split.
split(loc, key, val, sl); split(loc, key, val, sl);
@ -194,7 +193,7 @@ public class SkipSpan {
*/ */
public SkipSpan put(Comparable key, Object val, SkipList sl) { public SkipSpan put(Comparable key, Object val, SkipList sl) {
if(nKeys == 0) { if(nKeys == 0) {
sl.size++; sl.addItem();
keys[0] = key; keys[0] = key;
vals[0] = val; vals[0] = val;
nKeys++; nKeys++;
@ -256,9 +255,8 @@ public class SkipSpan {
Object o = vals[loc]; Object o = vals[loc];
Object[] res = new Object[2]; Object[] res = new Object[2];
res[0] = o; res[0] = o;
sl.size--; sl.delItem();
if(nKeys == 1) { if(nKeys == 1) {
if(sl.spans > 1) { sl.spans--; }
if((this.prev == null) && (this.next != null)) { if((this.prev == null) && (this.next != null)) {
res[1] = this.next; 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 // We're the first node in the list... copy the next node over and kill it. See also bottom of SkipLevels.java