提交 ced610b7 authored 作者: Thomas Mueller's avatar Thomas Mueller

A persistent tree map (work in progress) which might replace the storage of the…

A persistent tree map (work in progress) which might replace the storage of the database at some point in the far future.
上级 16c9d346
......@@ -163,6 +163,7 @@ import org.h2.test.unit.TestStreams;
import org.h2.test.unit.TestStringCache;
import org.h2.test.unit.TestStringUtils;
import org.h2.test.unit.TestTools;
import org.h2.test.unit.TestTreeMapStore;
import org.h2.test.unit.TestUtils;
import org.h2.test.unit.TestValue;
import org.h2.test.unit.TestValueHashMap;
......@@ -700,6 +701,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
new TestStringCache().runTest(this);
new TestStringUtils().runTest(this);
new TestTools().runTest(this);
new TestTreeMapStore().runTest(this);
new TestTraceSystem().runTest(this);
new TestUpgrade().runTest(this);
new TestUtils().runTest(this);
......
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, Version
* 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). Initial Developer: H2 Group
*/
package org.h2.test.unit;
import java.util.Iterator;
import java.util.Random;
import java.util.TreeMap;
import org.h2.dev.store.StoredMap;
import org.h2.dev.store.TreeMapStore;
import org.h2.jaqu.bytecode.Null;
import org.h2.store.fs.FileUtils;
import org.h2.test.TestBase;
/**
* Tests the tree map store.
*/
public class TestTreeMapStore extends TestBase {
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
TestBase.createCaller().init().test();
}
public void test() throws Exception {
testRandom();
testKeyValueClasses();
testIterate();
testSimple();
}
private void testRandom() {
String fileName = getBaseDir() + "/data.h3";
FileUtils.delete(fileName);
TreeMapStore s = TreeMapStore.open(fileName);
StoredMap<Integer, Integer> m = s.openMap("intString", Integer.class, Integer.class);
TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>();
Random r = new Random(1);
for (int i = 0; i < 100; i++) {
int k = r.nextInt(20);
int v = r.nextInt();
if (r.nextBoolean()) {
m.put(k, v);
map.put(k, v);
} else {
m.remove(k);
map.remove(k);
}
Iterator<Integer> it = m.keyIterator(null);
Iterator<Integer> itm = map.keySet().iterator();
while (itm.hasNext()) {
assertTrue(it.hasNext());
assertEquals(itm.next(), it.next());
}
assertFalse(it.hasNext());
}
s.close();
}
private void testKeyValueClasses() {
String fileName = getBaseDir() + "/data.h3";
FileUtils.delete(fileName);
TreeMapStore s = TreeMapStore.open(fileName);
StoredMap<Integer, String> is = s.openMap("intString", Integer.class, String.class);
is.put(1, "Hello");
StoredMap<Integer, Integer> ii = s.openMap("intInt", Integer.class, Integer.class);
ii.put(1, 10);
StoredMap<String, Integer> si = s.openMap("stringInt", String.class, Integer.class);
si.put("Test", 10);
StoredMap<String, String> ss = s.openMap("stringString", String.class, String.class);
ss.put("Hello", "World");
try {
s.openMap("invalid", Null.class, Integer.class);
fail();
} catch (RuntimeException e) {
// expected
}
try {
s.openMap("invalid", Integer.class, Null.class);
fail();
} catch (RuntimeException e) {
// expected
}
s.close();
s = TreeMapStore.open(fileName);
is = s.openMap("intString", Integer.class, String.class);
assertEquals("Hello", is.get(1));
ii = s.openMap("intInt", Integer.class, Integer.class);
assertEquals(10, ii.get(1).intValue());
si = s.openMap("stringInt", String.class, Integer.class);
assertEquals(10, si.get("Test").intValue());
ss = s.openMap("stringString", String.class, String.class);
assertEquals("World", ss.get("Hello"));
s.close();
}
private void testIterate() {
String fileName = getBaseDir() + "/data.h3";
FileUtils.delete(fileName);
TreeMapStore s = TreeMapStore.open(fileName);
StoredMap<Integer, String> m = s.openMap("data", Integer.class, String.class);
Iterator<Integer> it = m.keyIterator(null);
assertFalse(it.hasNext());
for (int i = 0; i < 10; i++) {
m.put(i, "hello " + i);
}
s.store();
it = m.keyIterator(null);
it.next();
assertThrows(UnsupportedOperationException.class, it).remove();
it = m.keyIterator(null);
for (int i = 0; i < 10; i++) {
assertTrue(it.hasNext());
assertEquals(i, it.next().intValue());
}
assertFalse(it.hasNext());
assertNull(it.next());
for (int j = 0; j < 10; j++) {
it = m.keyIterator(j);
for (int i = j; i < 10; i++) {
assertTrue(it.hasNext());
assertEquals(i, it.next().intValue());
}
assertFalse(it.hasNext());
}
}
private void testSimple() {
String fileName = getBaseDir() + "/data.h3";
FileUtils.delete(fileName);
TreeMapStore s = TreeMapStore.open(fileName);
StoredMap<Integer, String> m = s.openMap("data", Integer.class, String.class);
for (int i = 0; i < 3; i++) {
m.put(i, "hello " + i);
}
s.store();
m.remove(0);
assertNull(m.get(0));
for (int i = 1; i < 3; i++) {
assertEquals("hello " + i, m.get(i));
}
s.close();
s = TreeMapStore.open(fileName);
m = s.openMap("data", Integer.class, String.class);
assertNull(m.get(0));
for (int i = 1; i < 3; i++) {
assertEquals("hello " + i, m.get(i));
}
s.close();
}
}
......@@ -16,7 +16,7 @@ class Node {
private static final int FLAG_BLACK = 1;
// private static final int FLAG_BACK_REFERENCES = 2;
private final TreeMapStore store;
private final StoredMap<?, ?> map;
private long id;
private long leftId, rightId;
private long transaction;
......@@ -25,36 +25,36 @@ class Node {
private Node left, right;
private int flags;
private Node(TreeMapStore store) {
this.store = store;
private Node(StoredMap<?, ?> map) {
this.map = map;
}
static Node create(TreeMapStore store, Object key, Object data) {
Node n = new Node(store);
static Node create(StoredMap<?, ?> map, Object key, Object data) {
Node n = new Node(map);
n.key = key;
n.data = data;
n.transaction = store.getTransaction();
n.id = store.nextTempNodeId();
n.transaction = map.getTransaction();
n.id = map.nextTempNodeId();
return n;
}
static Node load(TreeMapStore store, long id, ByteBuffer buff) {
Node n = new Node(store);
static Node read(StoredMap<?, ?> map, long id, ByteBuffer buff) {
Node n = new Node(map);
n.id = id;
n.load(buff);
n.read(buff);
return n;
}
Node getLeft() {
if (left == null && leftId != 0) {
left = store.loadNode(leftId);
return map.readNode(leftId);
}
return left;
}
Node getRight() {
if (right == null && rightId != 0) {
right = store.loadNode(rightId);
return map.readNode(rightId);
}
return right;
}
......@@ -87,12 +87,12 @@ class Node {
this.rightId = r == null ? 0 : r.getId();
}
private Node copyOnWrite(long writeTransaction) {
if (writeTransaction == transaction) {
private Node copyOnWrite() {
if (transaction == map.getTransaction()) {
return this;
}
store.removeNode(id);
Node n2 = create(store, key, data);
map.removeNode(id);
Node n2 = create(map, key, data);
n2.leftId = leftId;
n2.left = left;
n2.rightId = rightId;
......@@ -124,9 +124,9 @@ class Node {
private void flipColor() {
flags = flags ^ FLAG_BLACK;
setLeft(getLeft().copyOnWrite(transaction));
setLeft(getLeft().copyOnWrite());
getLeft().flags = getLeft().flags ^ FLAG_BLACK;
setRight(getRight().copyOnWrite(transaction));
setRight(getRight().copyOnWrite());
getRight().flags = getRight().flags ^ FLAG_BLACK;
}
......@@ -147,7 +147,7 @@ class Node {
}
private Node rotateLeft() {
Node x = getRight().copyOnWrite(store.getTransaction());
Node x = getRight().copyOnWrite();
setRight(x.getLeft());
x.setLeft(this);
x.flags = flags;
......@@ -157,7 +157,7 @@ class Node {
}
private Node rotateRight() {
Node x = getLeft().copyOnWrite(store.getTransaction());
Node x = getLeft().copyOnWrite();
setLeft(x.getRight());
x.setRight(this);
x.flags = flags;
......@@ -187,7 +187,7 @@ class Node {
return this;
}
private Node min() {
private Node getMin() {
Node n = this;
while (n.getLeft() != null) {
n = n.getLeft();
......@@ -195,61 +195,61 @@ class Node {
return n;
}
private Node deleteMin() {
private Node removeMin() {
if (getLeft() == null) {
store.removeNode(id);
map.removeNode(id);
return null;
}
Node n = copyOnWrite(transaction);
Node n = copyOnWrite();
if (!isRed(n.getLeft()) && !isRed(n.getLeft().getLeft())) {
n = n.moveRedLeft();
}
n.setLeft(n.getLeft().deleteMin());
n.setLeft(n.getLeft().removeMin());
return n.fixUp();
}
static Node remove(Node n, Object key) {
if (findNode(n, key) == null) {
if (getNode(n, key) == null) {
return n;
}
return n.delete(key);
return n.remove(key);
}
private int compare(Object key) {
return store.compare(key, this.key);
int compare(Object key) {
return map.compare(key, this.key);
}
private Node delete(Object key) {
Node n = copyOnWrite(transaction);
if (store.compare(key, n) < 0) {
private Node remove(Object key) {
Node n = copyOnWrite();
if (map.compare(key, n.key) < 0) {
if (!isRed(n.getLeft()) && !isRed(n.getLeft().getLeft())) {
n = n.moveRedLeft();
}
n.setLeft(n.getLeft().delete(key));
n.setLeft(n.getLeft().remove(key));
} else {
if (isRed(n.getLeft())) {
n = n.rotateRight();
}
if (n.compare(key) == 0 && n.getRight() == null) {
store.removeNode(id);
map.removeNode(id);
return null;
}
if (!isRed(n.getRight()) && !isRed(n.getRight().getLeft())) {
n = n.moveRedRight();
}
if (n.compare(key) == 0) {
Node min = n.getRight().min();
Node min = n.getRight().getMin();
n.key = min.key;
n.data = min.data;
n.setRight(n.getRight().deleteMin());
n.setRight(n.getRight().removeMin());
} else {
n.setRight(n.getRight().delete(key));
n.setRight(n.getRight().remove(key));
}
}
return n.fixUp();
}
static Node findNode(Node n, Object key) {
static Node getNode(Node n, Object key) {
while (n != null) {
int compare = n.compare(key);
if (compare == 0) {
......@@ -263,19 +263,19 @@ class Node {
return null;
}
static Node add(TreeMapStore store, Node n, Object key, Object data) {
static Node put(StoredMap<?, ?> map, Node n, Object key, Object data) {
if (n == null) {
n = Node.create(store, key, data);
n = Node.create(map, key, data);
return n;
}
n = n.copyOnWrite(store.getTransaction());
n = n.copyOnWrite();
int compare = n.compare(key);
if (compare == 0) {
n.data = data;
} else if (compare < 0) {
n.setLeft(add(store, n.getLeft(), key, data));
n.setLeft(put(map, n.getLeft(), key, data));
} else {
n.setRight(add(store, n.getRight(), key, data));
n.setRight(put(map, n.getRight(), key, data));
}
return n.fixUp();
}
......@@ -298,25 +298,25 @@ class Node {
return n != null && (n.flags & FLAG_BLACK) == 0;
}
private void load(ByteBuffer buff) {
private void read(ByteBuffer buff) {
flags = buff.get();
leftId = buff.getLong();
rightId = buff.getLong();
key = store.getKeyType().read(buff);
data = store.getValueType().read(buff);
key = map.getKeyType().read(buff);
data = map.getValueType().read(buff);
}
void store(ByteBuffer buff) {
void write(ByteBuffer buff) {
buff.put((byte) flags);
buff.putLong(leftId);
buff.putLong(rightId);
store.getKeyType().write(buff, key);
store.getValueType().write(buff, data);
map.getKeyType().write(buff, key);
map.getValueType().write(buff, data);
}
int length() {
return store.getKeyType().length(key) +
store.getValueType().length(data) + 17;
return map.getKeyType().length(key) +
map.getValueType().length(data) + 17;
}
}
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.dev.store;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Iterator;
/**
* A stored map.
*
* @param <K> the key class
* @param <V> the value class
*/
public class StoredMap<K, V> {
private final TreeMapStore store;
private final String name;
private final KeyType keyType;
private final ValueType valueType;
private Node root;
private StoredMap(TreeMapStore store, String name, Class<K> keyClass, Class<V> valueClass) {
this.store = store;
this.name = name;
if (keyClass == Integer.class) {
keyType = new IntegerType();
} else if (keyClass == String.class) {
keyType = new StringType();
} else {
throw new RuntimeException("Unsupported key class " + keyClass.toString());
}
if (valueClass == Integer.class) {
valueType = new IntegerType();
} else if (valueClass == String.class) {
valueType = new StringType();
} else {
throw new RuntimeException("Unsupported value class " + keyClass.toString());
}
}
static <K, V> StoredMap<K, V> open(TreeMapStore store, String name, Class<K> keyClass, Class<V> valueClass) {
return new StoredMap<K, V>(store, name, keyClass, valueClass);
}
public void put(K key, V data) {
if (!isChanged()) {
store.changed(name, this);
}
root = Node.put(this, root, key, data);
}
@SuppressWarnings("unchecked")
public V get(K key) {
Node n = Node.getNode(root, key);
return (V) (n == null ? null : n.getData());
}
public void remove(K key) {
if (!isChanged()) {
store.changed(name, this);
}
root = Node.remove(root, key);
}
boolean isChanged() {
return root != null && root.getId() < 0;
}
/**
* A value type.
*/
static interface ValueType {
int length(Object obj);
void write(ByteBuffer buff, Object x);
Object read(ByteBuffer buff);
}
/**
* A key type.
*/
static interface KeyType extends ValueType {
int compare(Object a, Object b);
}
int compare(Object a, Object b) {
return keyType.compare(a, b);
}
/**
* An integer type.
*/
static class IntegerType implements KeyType {
public int compare(Object a, Object b) {
return ((Integer) a).compareTo((Integer) b);
}
public int length(Object obj) {
return TreeMapStore.getVarIntLen((Integer) obj);
}
public Integer read(ByteBuffer buff) {
return TreeMapStore.readVarInt(buff);
}
public void write(ByteBuffer buff, Object x) {
TreeMapStore.writeVarInt(buff, (Integer) x);
}
}
/**
* A string type.
*/
static class StringType implements KeyType {
public int compare(Object a, Object b) {
return a.toString().compareTo(b.toString());
}
public int length(Object obj) {
try {
byte[] bytes = obj.toString().getBytes("UTF-8");
return TreeMapStore.getVarIntLen(bytes.length) + bytes.length;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public String read(ByteBuffer buff) {
int len = TreeMapStore.readVarInt(buff);
byte[] bytes = new byte[len];
buff.get(bytes);
try {
return new String(bytes, "UTF-8");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void write(ByteBuffer buff, Object x) {
try {
byte[] bytes = x.toString().getBytes("UTF-8");
TreeMapStore.writeVarInt(buff, bytes.length);
buff.put(bytes);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
KeyType getKeyType() {
return keyType;
}
ValueType getValueType() {
return valueType;
}
long getTransaction() {
return store.getTransaction();
}
long nextTempNodeId() {
return store.nextTempNodeId();
}
public Node readNode(long id) {
return store.readNode(this, id);
}
void removeNode(long id) {
store.removeNode(id);
}
void setRoot(long rootPos) {
root = readNode(rootPos);
}
public Iterator<K> keyIterator(K from) {
return new Cursor(root, from);
}
/**
* A cursor to iterate over elements in ascending order.
*/
class Cursor implements Iterator<K> {
Node current;
ArrayList<Node> parents = new ArrayList<Node>();
Cursor(Node root, K from) {
min(root, from);
}
private void min(Node n, K key) {
while (n != null) {
int compare = key == null ? -1 : n.compare(key);
if (compare == 0) {
current = n;
return;
} else if (compare > 0) {
n = n.getRight();
} else {
parents.add(n);
n = n.getLeft();
}
}
if (parents.size() == 0) {
current = null;
return;
}
current = parents.remove(parents.size() - 1);
}
@SuppressWarnings("unchecked")
public K next() {
Node c = current;
if (c != null) {
fetchNext();
}
return c == null ? null : (K) c.getKey();
}
private void fetchNext() {
Node r = current.getRight();
if (r != null) {
min(r, null);
return;
}
if (parents.size() == 0) {
current = null;
return;
}
current = parents.remove(parents.size() - 1);
}
public boolean hasNext() {
return current != null;
}
public void remove() {
throw new UnsupportedOperationException();
}
}
Node getRoot() {
return root;
}
String getName() {
return name;
}
}
......@@ -11,11 +11,11 @@ import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Properties;
import java.util.TreeSet;
import org.h2.store.fs.FilePath;
import org.h2.util.New;
import org.h2.util.SmallLRUCache;
/*
......@@ -57,18 +57,19 @@ todo:
*/
/**
* A persistent tree map.
* A persistent storage for tree maps.
*/
public class TreeMapStore {
private final KeyType keyType;
private final ValueType valueType;
private final String fileName;
private FileChannel file;
private int pageSize = 4 * 1024;
private long rootPos;
private HashMap<Long, Node> cache = SmallLRUCache.newInstance(50000);
private TreeSet<Block> blocks = new TreeSet<Block>();
private StoredMap<String, String> meta;
private HashMap<String, StoredMap<?, ?>> maps = New.hashMap();
private HashMap<String, StoredMap<?, ?>> mapsChanged = New.hashMap();
// TODO use an int instead? (with rollover to 0)
private long transaction;
......@@ -76,35 +77,41 @@ public class TreeMapStore {
private int tempNodeId;
private long storePos;
private Node root;
private int loadCount;
private TreeMapStore(String fileName, Class<?> keyClass, Class<?> valueClass) {
private TreeMapStore(String fileName) {
this.fileName = fileName;
if (keyClass == Integer.class) {
keyType = new IntegerType();
} else if (keyClass == String.class) {
keyType = new StringType();
} else {
throw new RuntimeException("Unsupported key class " + keyClass.toString());
}
if (valueClass == Integer.class) {
valueType = new IntegerType();
} else if (valueClass == String.class) {
valueType = new StringType();
} else {
throw new RuntimeException("Unsupported value class " + keyClass.toString());
}
}
static TreeMapStore open(String fileName, Class<?> keyClass, Class<?> valueClass) {
TreeMapStore s = new TreeMapStore(fileName, keyClass, valueClass);
public static TreeMapStore open(String fileName) {
TreeMapStore s = new TreeMapStore(fileName);
s.open();
return s;
}
public <K, V> StoredMap<K, V> openMap(String name, Class<K> keyClass, Class<V> valueClass) {
@SuppressWarnings("unchecked")
StoredMap<K, V> m = (StoredMap<K, V>) maps.get(name);
if (m == null) {
String root = meta.get("map." + name);
m = StoredMap.open(this, name, keyClass, valueClass);
maps.put(name, m);
if (root != null) {
m.setRoot(Long.parseLong(root));
}
}
return m;
}
void changed(String name, StoredMap<?, ?> map) {
if (map != meta) {
mapsChanged.put(name, map);
}
}
void open() {
meta = StoredMap.open(this, "meta", String.class, String.class);
new File(fileName).getParentFile().mkdirs();
try {
file = FilePathCache.wrap(FilePath.get(fileName).open("rw"));
......@@ -114,7 +121,7 @@ public class TreeMapStore {
} else {
readHeader();
if (rootPos > 0) {
root = loadNode(rootPos);
meta.setRoot(rootPos);
}
}
} catch (Exception e) {
......@@ -165,9 +172,7 @@ public class TreeMapStore {
}
public void close() {
if (root != null && root.getId() < 0) {
store();
}
if (file != null) {
try {
file.close();
......@@ -201,7 +206,7 @@ public class TreeMapStore {
n.setRightId(right.getId());
}
int count = 1;
n.store(buff);
n.write(buff);
if (left != null) {
count += store(buff, left);
}
......@@ -211,18 +216,31 @@ public class TreeMapStore {
return count;
}
void store() {
if (root == null || root.getId() >= 0) {
public void store() {
if (!meta.isChanged() && mapsChanged.size() == 0) {
// TODO truncate file if empty
return;
}
commit();
Block b = new Block(storePos);
b.transaction = transaction;
long end = updateId(root, storePos + 1);
long end = storePos + 1 + 8;
for (StoredMap<?, ?> m : mapsChanged.values()) {
meta.put("map." + m.getName(), String.valueOf(end));
end = updateId(m.getRoot(), end);
}
long metaPos = end;
end = updateId(meta.getRoot(), end);
ByteBuffer buff = ByteBuffer.allocate((int) (end - storePos));
buff.put((byte) 'd');
b.entryCount = store(buff, root);
buff.putLong(metaPos - storePos);
int entryCount = 0;
for (StoredMap<?, ?> m : mapsChanged.values()) {
entryCount += store(buff, m.getRoot());
}
entryCount += store(buff, meta.getRoot());
b.entryCount = entryCount;
b.liveCount = b.entryCount;
b.length = buff.limit();
blocks.add(b);
......@@ -236,17 +254,18 @@ public class TreeMapStore {
} catch (IOException e) {
throw new RuntimeException(e);
}
rootPos = meta.getRoot().getId();
storePos = end;
rootPos = root.getId();
writeHeader();
tempNodeId = 0;
mapsChanged.clear();
}
public long getTransaction() {
long getTransaction() {
return transaction;
}
public long nextTempNodeId() {
long nextTempNodeId() {
return -(++tempNodeId);
}
......@@ -254,24 +273,7 @@ public class TreeMapStore {
return ++transaction;
}
public void add(Object key, Object data) {
root = Node.add(this, root, key, data);
}
public void remove(Object key) {
root = Node.remove(root, key);
}
public Object find(Object key) {
Node n = Node.findNode(root, key);
return n == null ? null : n.getData();
}
public Object getRoot() {
return root;
}
public Node loadNode(long id) {
Node readNode(StoredMap<?, ?> map, long id) {
Node n = cache.get(id);
if (n == null) {
try {
......@@ -285,7 +287,7 @@ public class TreeMapStore {
}
} while (buff.remaining() > 0);
buff.rewind();
n = Node.load(this, id, buff);
n = Node.read(map, id, buff);
} catch (Exception e) {
throw new RuntimeException(e);
}
......@@ -344,98 +346,6 @@ public class TreeMapStore {
return x;
}
int compare(Object a, Object b) {
return keyType.compare(a, b);
}
/**
* A value type.
*/
static interface ValueType {
int length(Object obj);
void write(ByteBuffer buff, Object x);
Object read(ByteBuffer buff);
}
/**
* A key type.
*/
static interface KeyType extends ValueType {
int compare(Object a, Object b);
}
/**
* An integer type.
*/
static class IntegerType implements KeyType {
public int compare(Object a, Object b) {
return ((Integer) a).compareTo((Integer) b);
}
public int length(Object obj) {
return getVarIntLen((Integer) obj);
}
public Integer read(ByteBuffer buff) {
return readVarInt(buff);
}
public void write(ByteBuffer buff, Object x) {
writeVarInt(buff, (Integer) x);
}
}
/**
* A string type.
*/
static class StringType implements KeyType {
public int compare(Object a, Object b) {
return a.toString().compareTo(b.toString());
}
public int length(Object obj) {
try {
byte[] bytes = obj.toString().getBytes("UTF-8");
return getVarIntLen(bytes.length) + bytes.length;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public String read(ByteBuffer buff) {
int len = readVarInt(buff);
byte[] bytes = new byte[len];
buff.get(bytes);
try {
return new String(bytes, "UTF-8");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void write(ByteBuffer buff, Object x) {
try {
byte[] bytes = x.toString().getBytes("UTF-8");
writeVarInt(buff, bytes.length);
buff.put(bytes);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
KeyType getKeyType() {
return keyType;
}
ValueType getValueType() {
return valueType;
}
void removeNode(long id) {
if (id > 0) {
getBlock(id).liveCount--;
......@@ -445,95 +355,6 @@ public class TreeMapStore {
private Block getBlock(long pos) {
return blocks.lower(new Block(pos));
}
public Cursor cursor() {
return new Cursor(root);
}
/**
* A cursor to iterate over all elements.
*/
public static class Cursor {
Node current;
ArrayList<Node> parents = new ArrayList<Node>();
Cursor(Node root) {
min(root);
}
void min(Node n) {
while (true) {
Node x = n.getLeft();
if (x == null) {
break;
}
parents.add(n);
n = x;
}
current = n;
}
Object next() {
Node c = current;
if (c != null) {
fetchNext();
}
return c == null ? null : c.getKey();
}
private void fetchNext() {
Node r = current.getRight();
if (r != null) {
min(r);
return;
}
if (parents.size() == 0) {
current = null;
return;
}
current = parents.remove(parents.size() - 1);
}
}
/**
* A cursor to iterate beginning from the root
* (not in ascending order).
*/
public static class RootCursor {
Node current;
ArrayList<Node> parents = new ArrayList<Node>();
RootCursor(Node root) {
current = root;
}
Object next() {
Node c = current;
if (c != null) {
fetchNext();
}
return c == null ? null : c.getKey();
}
private void fetchNext() {
Node l = current.getLeft();
if (l != null) {
parents.add(current);
current = l;
return;
}
while (true) {
Node r = current.getRight();
if (r != null) {
current = r;
return;
}
if (parents.size() == 0) {
current = null;
return;
}
current = parents.remove(parents.size() - 1);
}
}
}
/**
* A block of data.
*/
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论