提交 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; ...@@ -163,6 +163,7 @@ import org.h2.test.unit.TestStreams;
import org.h2.test.unit.TestStringCache; import org.h2.test.unit.TestStringCache;
import org.h2.test.unit.TestStringUtils; import org.h2.test.unit.TestStringUtils;
import org.h2.test.unit.TestTools; import org.h2.test.unit.TestTools;
import org.h2.test.unit.TestTreeMapStore;
import org.h2.test.unit.TestUtils; import org.h2.test.unit.TestUtils;
import org.h2.test.unit.TestValue; import org.h2.test.unit.TestValue;
import org.h2.test.unit.TestValueHashMap; import org.h2.test.unit.TestValueHashMap;
...@@ -700,6 +701,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1` ...@@ -700,6 +701,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
new TestStringCache().runTest(this); new TestStringCache().runTest(this);
new TestStringUtils().runTest(this); new TestStringUtils().runTest(this);
new TestTools().runTest(this); new TestTools().runTest(this);
new TestTreeMapStore().runTest(this);
new TestTraceSystem().runTest(this); new TestTraceSystem().runTest(this);
new TestUpgrade().runTest(this); new TestUpgrade().runTest(this);
new TestUtils().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 { ...@@ -16,7 +16,7 @@ class Node {
private static final int FLAG_BLACK = 1; private static final int FLAG_BLACK = 1;
// private static final int FLAG_BACK_REFERENCES = 2; // private static final int FLAG_BACK_REFERENCES = 2;
private final TreeMapStore store; private final StoredMap<?, ?> map;
private long id; private long id;
private long leftId, rightId; private long leftId, rightId;
private long transaction; private long transaction;
...@@ -25,36 +25,36 @@ class Node { ...@@ -25,36 +25,36 @@ class Node {
private Node left, right; private Node left, right;
private int flags; private int flags;
private Node(TreeMapStore store) { private Node(StoredMap<?, ?> map) {
this.store = store; this.map = map;
} }
static Node create(TreeMapStore store, Object key, Object data) { static Node create(StoredMap<?, ?> map, Object key, Object data) {
Node n = new Node(store); Node n = new Node(map);
n.key = key; n.key = key;
n.data = data; n.data = data;
n.transaction = store.getTransaction(); n.transaction = map.getTransaction();
n.id = store.nextTempNodeId(); n.id = map.nextTempNodeId();
return n; return n;
} }
static Node load(TreeMapStore store, long id, ByteBuffer buff) { static Node read(StoredMap<?, ?> map, long id, ByteBuffer buff) {
Node n = new Node(store); Node n = new Node(map);
n.id = id; n.id = id;
n.load(buff); n.read(buff);
return n; return n;
} }
Node getLeft() { Node getLeft() {
if (left == null && leftId != 0) { if (left == null && leftId != 0) {
left = store.loadNode(leftId); return map.readNode(leftId);
} }
return left; return left;
} }
Node getRight() { Node getRight() {
if (right == null && rightId != 0) { if (right == null && rightId != 0) {
right = store.loadNode(rightId); return map.readNode(rightId);
} }
return right; return right;
} }
...@@ -87,12 +87,12 @@ class Node { ...@@ -87,12 +87,12 @@ class Node {
this.rightId = r == null ? 0 : r.getId(); this.rightId = r == null ? 0 : r.getId();
} }
private Node copyOnWrite(long writeTransaction) { private Node copyOnWrite() {
if (writeTransaction == transaction) { if (transaction == map.getTransaction()) {
return this; return this;
} }
store.removeNode(id); map.removeNode(id);
Node n2 = create(store, key, data); Node n2 = create(map, key, data);
n2.leftId = leftId; n2.leftId = leftId;
n2.left = left; n2.left = left;
n2.rightId = rightId; n2.rightId = rightId;
...@@ -124,9 +124,9 @@ class Node { ...@@ -124,9 +124,9 @@ class Node {
private void flipColor() { private void flipColor() {
flags = flags ^ FLAG_BLACK; flags = flags ^ FLAG_BLACK;
setLeft(getLeft().copyOnWrite(transaction)); setLeft(getLeft().copyOnWrite());
getLeft().flags = getLeft().flags ^ FLAG_BLACK; getLeft().flags = getLeft().flags ^ FLAG_BLACK;
setRight(getRight().copyOnWrite(transaction)); setRight(getRight().copyOnWrite());
getRight().flags = getRight().flags ^ FLAG_BLACK; getRight().flags = getRight().flags ^ FLAG_BLACK;
} }
...@@ -147,7 +147,7 @@ class Node { ...@@ -147,7 +147,7 @@ class Node {
} }
private Node rotateLeft() { private Node rotateLeft() {
Node x = getRight().copyOnWrite(store.getTransaction()); Node x = getRight().copyOnWrite();
setRight(x.getLeft()); setRight(x.getLeft());
x.setLeft(this); x.setLeft(this);
x.flags = flags; x.flags = flags;
...@@ -157,7 +157,7 @@ class Node { ...@@ -157,7 +157,7 @@ class Node {
} }
private Node rotateRight() { private Node rotateRight() {
Node x = getLeft().copyOnWrite(store.getTransaction()); Node x = getLeft().copyOnWrite();
setLeft(x.getRight()); setLeft(x.getRight());
x.setRight(this); x.setRight(this);
x.flags = flags; x.flags = flags;
...@@ -187,7 +187,7 @@ class Node { ...@@ -187,7 +187,7 @@ class Node {
return this; return this;
} }
private Node min() { private Node getMin() {
Node n = this; Node n = this;
while (n.getLeft() != null) { while (n.getLeft() != null) {
n = n.getLeft(); n = n.getLeft();
...@@ -195,61 +195,61 @@ class Node { ...@@ -195,61 +195,61 @@ class Node {
return n; return n;
} }
private Node deleteMin() { private Node removeMin() {
if (getLeft() == null) { if (getLeft() == null) {
store.removeNode(id); map.removeNode(id);
return null; return null;
} }
Node n = copyOnWrite(transaction); Node n = copyOnWrite();
if (!isRed(n.getLeft()) && !isRed(n.getLeft().getLeft())) { if (!isRed(n.getLeft()) && !isRed(n.getLeft().getLeft())) {
n = n.moveRedLeft(); n = n.moveRedLeft();
} }
n.setLeft(n.getLeft().deleteMin()); n.setLeft(n.getLeft().removeMin());
return n.fixUp(); return n.fixUp();
} }
static Node remove(Node n, Object key) { static Node remove(Node n, Object key) {
if (findNode(n, key) == null) { if (getNode(n, key) == null) {
return n; return n;
} }
return n.delete(key); return n.remove(key);
} }
private int compare(Object key) { int compare(Object key) {
return store.compare(key, this.key); return map.compare(key, this.key);
} }
private Node delete(Object key) { private Node remove(Object key) {
Node n = copyOnWrite(transaction); Node n = copyOnWrite();
if (store.compare(key, n) < 0) { if (map.compare(key, n.key) < 0) {
if (!isRed(n.getLeft()) && !isRed(n.getLeft().getLeft())) { if (!isRed(n.getLeft()) && !isRed(n.getLeft().getLeft())) {
n = n.moveRedLeft(); n = n.moveRedLeft();
} }
n.setLeft(n.getLeft().delete(key)); n.setLeft(n.getLeft().remove(key));
} else { } else {
if (isRed(n.getLeft())) { if (isRed(n.getLeft())) {
n = n.rotateRight(); n = n.rotateRight();
} }
if (n.compare(key) == 0 && n.getRight() == null) { if (n.compare(key) == 0 && n.getRight() == null) {
store.removeNode(id); map.removeNode(id);
return null; return null;
} }
if (!isRed(n.getRight()) && !isRed(n.getRight().getLeft())) { if (!isRed(n.getRight()) && !isRed(n.getRight().getLeft())) {
n = n.moveRedRight(); n = n.moveRedRight();
} }
if (n.compare(key) == 0) { if (n.compare(key) == 0) {
Node min = n.getRight().min(); Node min = n.getRight().getMin();
n.key = min.key; n.key = min.key;
n.data = min.data; n.data = min.data;
n.setRight(n.getRight().deleteMin()); n.setRight(n.getRight().removeMin());
} else { } else {
n.setRight(n.getRight().delete(key)); n.setRight(n.getRight().remove(key));
} }
} }
return n.fixUp(); return n.fixUp();
} }
static Node findNode(Node n, Object key) { static Node getNode(Node n, Object key) {
while (n != null) { while (n != null) {
int compare = n.compare(key); int compare = n.compare(key);
if (compare == 0) { if (compare == 0) {
...@@ -263,19 +263,19 @@ class Node { ...@@ -263,19 +263,19 @@ class Node {
return null; 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) { if (n == null) {
n = Node.create(store, key, data); n = Node.create(map, key, data);
return n; return n;
} }
n = n.copyOnWrite(store.getTransaction()); n = n.copyOnWrite();
int compare = n.compare(key); int compare = n.compare(key);
if (compare == 0) { if (compare == 0) {
n.data = data; n.data = data;
} else if (compare < 0) { } else if (compare < 0) {
n.setLeft(add(store, n.getLeft(), key, data)); n.setLeft(put(map, n.getLeft(), key, data));
} else { } else {
n.setRight(add(store, n.getRight(), key, data)); n.setRight(put(map, n.getRight(), key, data));
} }
return n.fixUp(); return n.fixUp();
} }
...@@ -298,25 +298,25 @@ class Node { ...@@ -298,25 +298,25 @@ class Node {
return n != null && (n.flags & FLAG_BLACK) == 0; return n != null && (n.flags & FLAG_BLACK) == 0;
} }
private void load(ByteBuffer buff) { private void read(ByteBuffer buff) {
flags = buff.get(); flags = buff.get();
leftId = buff.getLong(); leftId = buff.getLong();
rightId = buff.getLong(); rightId = buff.getLong();
key = store.getKeyType().read(buff); key = map.getKeyType().read(buff);
data = store.getValueType().read(buff); data = map.getValueType().read(buff);
} }
void store(ByteBuffer buff) { void write(ByteBuffer buff) {
buff.put((byte) flags); buff.put((byte) flags);
buff.putLong(leftId); buff.putLong(leftId);
buff.putLong(rightId); buff.putLong(rightId);
store.getKeyType().write(buff, key); map.getKeyType().write(buff, key);
store.getValueType().write(buff, data); map.getValueType().write(buff, data);
} }
int length() { int length() {
return store.getKeyType().length(key) + return map.getKeyType().length(key) +
store.getValueType().length(data) + 17; 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; ...@@ -11,11 +11,11 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Properties; import java.util.Properties;
import java.util.TreeSet; import java.util.TreeSet;
import org.h2.store.fs.FilePath; import org.h2.store.fs.FilePath;
import org.h2.util.New;
import org.h2.util.SmallLRUCache; import org.h2.util.SmallLRUCache;
/* /*
...@@ -57,18 +57,19 @@ todo: ...@@ -57,18 +57,19 @@ todo:
*/ */
/** /**
* A persistent tree map. * A persistent storage for tree maps.
*/ */
public class TreeMapStore { public class TreeMapStore {
private final KeyType keyType;
private final ValueType valueType;
private final String fileName; private final String fileName;
private FileChannel file; private FileChannel file;
private int pageSize = 4 * 1024; private int pageSize = 4 * 1024;
private long rootPos; private long rootPos;
private HashMap<Long, Node> cache = SmallLRUCache.newInstance(50000); private HashMap<Long, Node> cache = SmallLRUCache.newInstance(50000);
private TreeSet<Block> blocks = new TreeSet<Block>(); 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) // TODO use an int instead? (with rollover to 0)
private long transaction; private long transaction;
...@@ -76,35 +77,41 @@ public class TreeMapStore { ...@@ -76,35 +77,41 @@ public class TreeMapStore {
private int tempNodeId; private int tempNodeId;
private long storePos; private long storePos;
private Node root;
private int loadCount; private int loadCount;
private TreeMapStore(String fileName, Class<?> keyClass, Class<?> valueClass) { private TreeMapStore(String fileName) {
this.fileName = 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) { public static TreeMapStore open(String fileName) {
TreeMapStore s = new TreeMapStore(fileName, keyClass, valueClass); TreeMapStore s = new TreeMapStore(fileName);
s.open(); s.open();
return s; 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() { void open() {
meta = StoredMap.open(this, "meta", String.class, String.class);
new File(fileName).getParentFile().mkdirs(); new File(fileName).getParentFile().mkdirs();
try { try {
file = FilePathCache.wrap(FilePath.get(fileName).open("rw")); file = FilePathCache.wrap(FilePath.get(fileName).open("rw"));
...@@ -114,7 +121,7 @@ public class TreeMapStore { ...@@ -114,7 +121,7 @@ public class TreeMapStore {
} else { } else {
readHeader(); readHeader();
if (rootPos > 0) { if (rootPos > 0) {
root = loadNode(rootPos); meta.setRoot(rootPos);
} }
} }
} catch (Exception e) { } catch (Exception e) {
...@@ -165,9 +172,7 @@ public class TreeMapStore { ...@@ -165,9 +172,7 @@ public class TreeMapStore {
} }
public void close() { public void close() {
if (root != null && root.getId() < 0) {
store(); store();
}
if (file != null) { if (file != null) {
try { try {
file.close(); file.close();
...@@ -201,7 +206,7 @@ public class TreeMapStore { ...@@ -201,7 +206,7 @@ public class TreeMapStore {
n.setRightId(right.getId()); n.setRightId(right.getId());
} }
int count = 1; int count = 1;
n.store(buff); n.write(buff);
if (left != null) { if (left != null) {
count += store(buff, left); count += store(buff, left);
} }
...@@ -211,18 +216,31 @@ public class TreeMapStore { ...@@ -211,18 +216,31 @@ public class TreeMapStore {
return count; return count;
} }
void store() { public void store() {
if (root == null || root.getId() >= 0) { if (!meta.isChanged() && mapsChanged.size() == 0) {
// TODO truncate file if empty // TODO truncate file if empty
return; return;
} }
commit(); commit();
Block b = new Block(storePos); Block b = new Block(storePos);
b.transaction = transaction; 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)); ByteBuffer buff = ByteBuffer.allocate((int) (end - storePos));
buff.put((byte) 'd'); 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.liveCount = b.entryCount;
b.length = buff.limit(); b.length = buff.limit();
blocks.add(b); blocks.add(b);
...@@ -236,17 +254,18 @@ public class TreeMapStore { ...@@ -236,17 +254,18 @@ public class TreeMapStore {
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
rootPos = meta.getRoot().getId();
storePos = end; storePos = end;
rootPos = root.getId();
writeHeader(); writeHeader();
tempNodeId = 0; tempNodeId = 0;
mapsChanged.clear();
} }
public long getTransaction() { long getTransaction() {
return transaction; return transaction;
} }
public long nextTempNodeId() { long nextTempNodeId() {
return -(++tempNodeId); return -(++tempNodeId);
} }
...@@ -254,24 +273,7 @@ public class TreeMapStore { ...@@ -254,24 +273,7 @@ public class TreeMapStore {
return ++transaction; return ++transaction;
} }
public void add(Object key, Object data) { Node readNode(StoredMap<?, ?> map, long id) {
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 n = cache.get(id); Node n = cache.get(id);
if (n == null) { if (n == null) {
try { try {
...@@ -285,7 +287,7 @@ public class TreeMapStore { ...@@ -285,7 +287,7 @@ public class TreeMapStore {
} }
} while (buff.remaining() > 0); } while (buff.remaining() > 0);
buff.rewind(); buff.rewind();
n = Node.load(this, id, buff); n = Node.read(map, id, buff);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
...@@ -344,98 +346,6 @@ public class TreeMapStore { ...@@ -344,98 +346,6 @@ public class TreeMapStore {
return x; 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) { void removeNode(long id) {
if (id > 0) { if (id > 0) {
getBlock(id).liveCount--; getBlock(id).liveCount--;
...@@ -445,95 +355,6 @@ public class TreeMapStore { ...@@ -445,95 +355,6 @@ public class TreeMapStore {
private Block getBlock(long pos) { private Block getBlock(long pos) {
return blocks.lower(new Block(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. * A block of data.
*/ */
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论