提交 034abb3c authored 作者: Thomas Mueller's avatar Thomas Mueller

A persistent tree map (work in progress)

上级 af6af2f1
...@@ -48,24 +48,35 @@ public class TestTreeMapStore extends TestBase { ...@@ -48,24 +48,35 @@ public class TestTreeMapStore extends TestBase {
FileUtils.delete(fileName); FileUtils.delete(fileName);
BtreeMapStore s = BtreeMapStore.open(fileName); BtreeMapStore s = BtreeMapStore.open(fileName);
BtreeMap<Integer, String> m = s.openMap("data", Integer.class, String.class); BtreeMap<Integer, String> m = s.openMap("data", Integer.class, String.class);
int count = 5; int count = 10000;
// Profiler p = new Profiler();
// p.startCollecting();
// long t = System.currentTimeMillis();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
m.put(i, "hello " + i); m.put(i, "hello " + i);
assertEquals("hello " + i, m.get(i));
} }
// System.out.println("put: " + (System.currentTimeMillis() - t));
// System.out.println(p.getTop(5));
// p = new Profiler();
//p.startCollecting();
// t = System.currentTimeMillis();
s.store(); s.store();
// System.out.println("store: " + (System.currentTimeMillis() - t));
// System.out.println(p.getTop(5));
m.remove(0); m.remove(0);
assertNull(m.get(0)); assertNull(m.get(0));
for (int i = 1; i < count; i++) { for (int i = 1; i < count; i++) {
assertEquals("hello " + i, m.get(i)); assertEquals("hello " + i, m.get(i));
} }
s.close(); s.close();
// s = BtreeMapStore.open(fileName); s = BtreeMapStore.open(fileName);
// m = s.openMap("data", Integer.class, String.class); m = s.openMap("data", Integer.class, String.class);
// assertNull(m.get(0)); assertNull(m.get(0));
// for (int i = 1; i < count; i++) { for (int i = 1; i < count; i++) {
// assertEquals("hello " + i, m.get(i)); assertEquals("hello " + i, m.get(i));
// } }
// s.close(); s.close();
} }
private void testDefragment() { private void testDefragment() {
......
...@@ -331,57 +331,25 @@ public class BtreeMap<K, V> { ...@@ -331,57 +331,25 @@ public class BtreeMap<K, V> {
* A cursor to iterate over elements in ascending order. * A cursor to iterate over elements in ascending order.
*/ */
class Cursor implements Iterator<K> { class Cursor implements Iterator<K> {
Page current; private ArrayList<Page.CursorPos> parents = new ArrayList<Page.CursorPos>();
ArrayList<Page> parents = new ArrayList<Page>(); private K current;
Cursor(Page root, K from) { Cursor(Page root, K from) {
min(root, from); Page.min(root, parents, from);
} fetchNext();
private void min(Page p, K key) {
int todo;
// 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() { public K next() {
int todo; K c = current;
return null; if (c != null) {
// Page c = current; fetchNext();
// if (c != null) { }
// fetchNext(); return c == null ? null : c;
// }
// return c == null ? null : (K) c.getKey();
} }
@SuppressWarnings("unchecked")
private void fetchNext() { private void fetchNext() {
int todo; current = (K) Page.nextKey(parents);
// Page 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() { public boolean hasNext() {
...@@ -391,6 +359,7 @@ public class BtreeMap<K, V> { ...@@ -391,6 +359,7 @@ public class BtreeMap<K, V> {
public void remove() { public void remove() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
} }
/** /**
......
...@@ -21,6 +21,7 @@ import java.util.Properties; ...@@ -21,6 +21,7 @@ import java.util.Properties;
import java.util.TreeMap; import java.util.TreeMap;
import org.h2.dev.store.FilePathCache; import org.h2.dev.store.FilePathCache;
import org.h2.store.fs.FilePath; import org.h2.store.fs.FilePath;
import org.h2.store.fs.FileUtils;
import org.h2.util.New; import org.h2.util.New;
import org.h2.util.SmallLRUCache; import org.h2.util.SmallLRUCache;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
...@@ -140,7 +141,7 @@ public class BtreeMapStore { ...@@ -140,7 +141,7 @@ public class BtreeMapStore {
private void open() { private void open() {
meta = BtreeMap.open(this, "meta", String.class, String.class); meta = BtreeMap.open(this, "meta", String.class, String.class);
new File(fileName).getParentFile().mkdirs(); FileUtils.createDirectories(FileUtils.getParent(fileName));
try { try {
log("file open"); log("file open");
file = FilePathCache.wrap(FilePath.get(fileName).open("rw")); file = FilePathCache.wrap(FilePath.get(fileName).open("rw"));
...@@ -546,7 +547,7 @@ public class BtreeMapStore { ...@@ -546,7 +547,7 @@ public class BtreeMapStore {
try { try {
long pos = getPosition(id); long pos = getPosition(id);
file.position(pos); file.position(pos);
ByteBuffer buff = ByteBuffer.wrap(new byte[1024]); ByteBuffer buff = ByteBuffer.wrap(new byte[8 * 1024]);
// TODO read fully; read only required bytes // TODO read fully; read only required bytes
do { do {
int len = file.read(buff); int len = file.read(buff);
......
...@@ -7,13 +7,14 @@ ...@@ -7,13 +7,14 @@
package org.h2.dev.store.btree; package org.h2.dev.store.btree;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList;
/** /**
* A btree page implementation. * A btree page implementation.
*/ */
class Page { class Page {
private static final int MAX_SIZE = 4; private static final int MAX_SIZE = 20;
private final BtreeMap<?, ?> map; private final BtreeMap<?, ?> map;
private long id; private long id;
...@@ -55,7 +56,7 @@ class Page { ...@@ -55,7 +56,7 @@ class Page {
*/ */
static Page read(BtreeMap<?, ?> map, long id, ByteBuffer buff) { static Page read(BtreeMap<?, ?> map, long id, ByteBuffer buff) {
Page p = new Page(map); Page p = new Page(map);
p.id = id; p.id = p.storedId = id;
p.read(buff); p.read(buff);
return p; return p;
} }
...@@ -79,7 +80,7 @@ class Page { ...@@ -79,7 +80,7 @@ class Page {
buff.append(" "); buff.append(" ");
} }
if (children != null) { if (children != null) {
buff.append("[" + children[i] + "]"); buff.append("[" + children[i] + "] ");
} }
if (i < keys.length) { if (i < keys.length) {
buff.append(keys[i]); buff.append(keys[i]);
...@@ -143,6 +144,70 @@ class Page { ...@@ -143,6 +144,70 @@ class Page {
return -(low + 1); return -(low + 1);
} }
/**
* A position in a cursor
*/
static class CursorPos {
Page page;
int index;
}
static void min(Page p, ArrayList<CursorPos> parents, Object key) {
int todo;
while (p != null) {
int x = key == null ? 0 : p.findKey(key);
if (p.children != null) {
if (x < 0) {
x = -x - 1;
} else {
x++;
}
p = p.map.readPage(p.children[x]);
CursorPos c = new CursorPos();
c.page = p;
c.index = x;
parents.add(c);
} else {
if (x < 0) {
x = -x - 1;
}
CursorPos c = new CursorPos();
c.page = p;
c.index = x;
parents.add(c);
return;
}
}
}
public static Object nextKey(ArrayList<CursorPos> parents) {
int todoTest;
if (parents.size() == 0) {
return null;
}
while (true) {
// TODO avoid remove/add pairs is possible
CursorPos p = parents.remove(parents.size() - 1);
int index = p.index++;
if (index < p.page.keys.length) {
parents.add(p);
return p.page.keys[index];
}
while (true) {
if (parents.size() == 0) {
return null;
}
p = parents.remove(parents.size() - 1);
index = p.index++;
if (index < p.page.children.length) {
parents.add(p);
min(p.page, parents, null);
break;
}
}
}
}
private int size() { private int size() {
return keys.length; return keys.length;
} }
...@@ -151,7 +216,7 @@ class Page { ...@@ -151,7 +216,7 @@ class Page {
return children == null; return children == null;
} }
private Page split(int at) { private Page splitLeaf(int at) {
int a = at, b = keys.length - a; int a = at, b = keys.length - a;
Object[] aKeys = new Object[a]; Object[] aKeys = new Object[a];
Object[] bKeys = new Object[b]; Object[] bKeys = new Object[b];
...@@ -160,22 +225,27 @@ class Page { ...@@ -160,22 +225,27 @@ class Page {
keys = aKeys; keys = aKeys;
Object[] aValues = new Object[a]; Object[] aValues = new Object[a];
Object[] bValues = new Object[b]; Object[] bValues = new Object[b];
bValues = new Object[b];
System.arraycopy(values, 0, aValues, 0, a); System.arraycopy(values, 0, aValues, 0, a);
System.arraycopy(values, a, bValues, 0, b); System.arraycopy(values, a, bValues, 0, b);
values = aValues; values = aValues;
long[] bChildren; Page newPage = create(map, bKeys, bValues, null);
if (children == null) { return newPage;
bChildren = null;
} else {
a = children.length / 2;
b = children.length - a;
long[] aChildren = new long[a];
bChildren = new long[b];
System.arraycopy(children, 0, aChildren, 0, a);
System.arraycopy(children, a, bChildren, 0, b);
children = aChildren;
} }
Page newPage = create(map, bKeys, bValues, bChildren);
private Page splitNode(int at) {
int a = at, b = keys.length - a;
Object[] aKeys = new Object[a];
Object[] bKeys = new Object[b - 1];
System.arraycopy(keys, 0, aKeys, 0, a);
System.arraycopy(keys, a + 1, bKeys, 0, b - 1);
keys = aKeys;
long[] aChildren = new long[a + 1];
long[] bChildren = new long[b];
System.arraycopy(children, 0, aChildren, 0, a + 1);
System.arraycopy(children, a + 1, bChildren, 0, b);
children = aChildren;
Page newPage = create(map, bKeys, null, bChildren);
return newPage; return newPage;
} }
...@@ -203,23 +273,40 @@ class Page { ...@@ -203,23 +273,40 @@ class Page {
if (parent != null) { if (parent != null) {
parent.children[parentIndex] = p.id; parent.children[parentIndex] = p.id;
} }
if (!p.isLeaf()) {
if (p.size() >= MAX_SIZE) {
// TODO almost duplicate code
int pos = p.size() / 2;
Object k = p.keys[pos];
Page split = p.splitNode(pos);
if (parent == null) {
Object[] keys = { k };
long[] children = { p.getId(), split.getId() };
top = create(map, keys, null, children);
p = top;
} else {
parent.insert(parentIndex, k, null, split.getId());
p = parent;
}
}
}
int index = p.findKey(key); int index = p.findKey(key);
if (p.isLeaf()) { if (p.isLeaf()) {
if (index >= 0) { if (index >= 0) {
p.values[index] = value; p.values[index] = value;
} else { break;
}
index = -index - 1; index = -index - 1;
p.insert(index, key, value, 0); p.insert(index, key, value, 0);
if (p.size() >= MAX_SIZE) { if (p.size() >= MAX_SIZE) {
int pos = p.size() / 2; int pos = p.size() / 2;
Object k = p.keys[pos]; Object k = p.keys[pos];
Page split = p.split(pos); Page split = p.splitLeaf(pos);
if (parent == null) { if (parent == null) {
Object[] keys = { k }; Object[] keys = { k };
long[] children = { p.getId(), split.getId() }; long[] children = { p.getId(), split.getId() };
Page newRoot = create(map, keys, null, children); top = create(map, keys, null, children);
return newRoot; } else {
}
parent.insert(parentIndex, k, null, split.getId()); parent.insert(parentIndex, k, null, split.getId());
} }
} }
...@@ -236,7 +323,6 @@ class Page { ...@@ -236,7 +323,6 @@ class Page {
return top; return top;
} }
/** /**
* Remove a key-value pair. * Remove a key-value pair.
* *
...@@ -317,8 +403,8 @@ class Page { ...@@ -317,8 +403,8 @@ class Page {
} }
if (children != null) { if (children != null) {
long[] newChildren = new long[children.length + 1]; long[] newChildren = new long[children.length + 1];
copyWithGap(children, newChildren, children.length, index); copyWithGap(children, newChildren, children.length, index + 1);
newChildren[index] = child; newChildren[index + 1] = child;
children = newChildren; children = newChildren;
} }
} }
...@@ -407,6 +493,10 @@ class Page { ...@@ -407,6 +493,10 @@ class Page {
*/ */
int lengthIncludingTempChildren() { int lengthIncludingTempChildren() {
int len = length(); int len = length();
if (len > 1024) {
int test;
System.out.println("??");
}
if (children != null) { if (children != null) {
int size = children.length; int size = children.length;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
...@@ -508,4 +598,8 @@ class Page { ...@@ -508,4 +598,8 @@ class Page {
} }
} }
public Object getKey(int index) {
return keys[index];
}
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论