forked from I2P_Developers/i2p.i2p
- Use new UTF8StringBytes
- Track number of SkipLevels in a SkipList - More double-checks - Cleanups, logging, generics
This commit is contained in:
@ -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.
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
Reference in New Issue
Block a user