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

A persistent tree map (work in progress).

上级 377e6a96
......@@ -10,8 +10,6 @@ import java.util.Random;
import java.util.TreeMap;
import org.h2.dev.store.btree.BtreeMap;
import org.h2.dev.store.btree.BtreeMapStore;
import org.h2.dev.store.tree.StoredMap;
import org.h2.dev.store.tree.TreeMapStore;
import org.h2.jaqu.bytecode.Null;
import org.h2.store.fs.FileUtils;
import org.h2.test.TestBase;
......@@ -31,10 +29,7 @@ public class TestTreeMapStore extends TestBase {
}
public void test() throws Exception {
// btree
testBtreeStore();
// left leaning red-black tree
testDefragment();
testReuseSpace();
testRandom();
......@@ -84,13 +79,12 @@ public class TestTreeMapStore extends TestBase {
FileUtils.delete(fileName);
long initialLength = 0;
for (int j = 0; j < 20; j++) {
TreeMapStore s = TreeMapStore.open(fileName);
StoredMap<Integer, String> m = s.openMap("data", Integer.class, String.class);
BtreeMapStore s = BtreeMapStore.open(fileName);
BtreeMap<Integer, String> m = s.openMap("data", Integer.class, String.class);
for (int i = 0; i < 10; i++) {
m.put(j + i, "Hello " + j);
}
s.store();
// s.log(s.toString());
s.compact();
s.close();
long len = FileUtils.size(fileName);
......@@ -108,8 +102,8 @@ public class TestTreeMapStore extends TestBase {
FileUtils.delete(fileName);
long initialLength = 0;
for (int j = 0; j < 10; j++) {
TreeMapStore s = TreeMapStore.open(fileName);
StoredMap<Integer, String> m = s.openMap("data", Integer.class, String.class);
BtreeMapStore s = BtreeMapStore.open(fileName);
BtreeMap<Integer, String> m = s.openMap("data", Integer.class, String.class);
for (int i = 0; i < 10; i++) {
m.put(i, "Hello");
}
......@@ -131,8 +125,8 @@ public class TestTreeMapStore extends TestBase {
private void testRandom() {
String fileName = getBaseDir() + "/data.h3";
FileUtils.delete(fileName);
TreeMapStore s = TreeMapStore.open(fileName);
StoredMap<Integer, Integer> m = s.openMap("data", Integer.class, Integer.class);
BtreeMapStore s = BtreeMapStore.open(fileName);
BtreeMap<Integer, Integer> m = s.openMap("data", Integer.class, Integer.class);
TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>();
Random r = new Random(1);
for (int i = 0; i < 100; i++) {
......@@ -159,14 +153,14 @@ public class TestTreeMapStore extends TestBase {
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);
BtreeMapStore s = BtreeMapStore.open(fileName);
BtreeMap<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);
BtreeMap<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);
BtreeMap<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);
BtreeMap<String, String> ss = s.openMap("stringString", String.class, String.class);
ss.put("Hello", "World");
try {
s.openMap("invalid", Null.class, Integer.class);
......@@ -181,7 +175,7 @@ public class TestTreeMapStore extends TestBase {
// expected
}
s.close();
s = TreeMapStore.open(fileName);
s = BtreeMapStore.open(fileName);
is = s.openMap("intString", Integer.class, String.class);
assertEquals("Hello", is.get(1));
ii = s.openMap("intInt", Integer.class, Integer.class);
......@@ -196,8 +190,8 @@ public class TestTreeMapStore extends TestBase {
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);
BtreeMapStore s = BtreeMapStore.open(fileName);
BtreeMap<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++) {
......@@ -229,8 +223,8 @@ public class TestTreeMapStore extends TestBase {
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);
BtreeMapStore s = BtreeMapStore.open(fileName);
BtreeMap<Integer, String> m = s.openMap("data", Integer.class, String.class);
for (int i = 0; i < 3; i++) {
m.put(i, "hello " + i);
}
......@@ -244,7 +238,7 @@ public class TestTreeMapStore extends TestBase {
s.close();
s = TreeMapStore.open(fileName);
s = BtreeMapStore.open(fileName);
m = s.openMap("data", Integer.class, String.class);
assertNull(m.get(0));
for (int i = 1; i < 3; i++) {
......
......@@ -90,7 +90,7 @@ public class BtreeMap<K, V> {
* Get a value.
*
* @param key the key
* @return the value
* @return the value, or null if not found
*/
@SuppressWarnings("unchecked")
public V get(K key) {
......@@ -100,6 +100,19 @@ public class BtreeMap<K, V> {
return (V) root.find(key);
}
/**
* Get the page for the given value.
*
* @param key the key
* @return the value, or null if not found
*/
public Page getPage(K key) {
if (root == null) {
return null;
}
return root.findPage(key);
}
/**
* Remove a key-value pair.
*
......@@ -109,7 +122,9 @@ public class BtreeMap<K, V> {
if (!isChanged()) {
store.markChanged(name, this);
}
root = Page.remove(root, key);
if (root != null) {
root = Page.remove(root, key);
}
}
/**
......
......@@ -7,7 +7,6 @@
package org.h2.dev.store.btree;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
......@@ -273,15 +272,16 @@ public class BtreeMapStore {
if (x.liveCount == 0) {
meta.remove("block." + x.id);
} else {
meta.put("block." + x.id, "temp " + x.toString());
meta.put("block." + x.id, "temp-" + x.toString());
}
}
// modifying the meta can itself affect the metadata
// TODO solve this in a better way
ArrayList<Integer> removedBlocks = New.arrayList();
for (Block x : new ArrayList<Block>(blocks.values())) {
if (x.liveCount == 0) {
meta.remove("block." + x.id);
blocks.remove(x.id);
removedBlocks.add(x.id);
} else {
meta.put("block." + x.id, x.toString());
}
......@@ -289,7 +289,13 @@ public class BtreeMapStore {
lenEstimate += meta.getRoot().lengthIncludingTempChildren();
b.length = lenEstimate;
blocks.remove(b.id);
long storePos = allocateBlock(lenEstimate);
blocks.put(b.id, b);
for (int id : removedBlocks) {
blocks.remove(id);
}
long pageId = getId(blockId, 1 + 8);
for (BtreeMap<?, ?> m : mapsChanged.values()) {
......@@ -301,7 +307,6 @@ public class BtreeMapStore {
}
}
int metaRootOffset = (int) (pageId - getId(blockId, 0));
// add a dummy entry so the count is correct
meta.put("block." + b.id, b.toString());
int count = 0;
......@@ -424,7 +429,6 @@ public class BtreeMapStore {
* be re-written.
*/
public void compact() {
if(true)throw new RuntimeException("not implemented yet");
if (blocks.size() <= 1) {
return;
}
......@@ -473,7 +477,7 @@ public class BtreeMapStore {
}
long oldMetaRootId = readMetaRootId(move.start);
long offset = getPosition(oldMetaRootId);
log(" meta:" + move.id + "/" + offset);
log(" meta:" + move.id + "/" + offset + " start: " + move.start);
BtreeMap<String, String> oldMeta = BtreeMap.open(this, "old-meta", String.class, String.class);
oldMeta.setRoot(oldMetaRootId);
Iterator<String> it = oldMeta.keyIterator(null);
......@@ -509,19 +513,19 @@ public class BtreeMapStore {
Iterator<?> dataIt = oldData.keyIterator(null);
while (dataIt.hasNext()) {
Object o = dataIt.next();
int todo;
Page p = null; // data.getNode(o);
Page p = data.getPage(o);
if (p == null) {
// was removed later - ignore
} else if (p.getId() < 0) {
// temporarily changed - ok
// TODO move old data if changed temporarily?
} else {
Block b = getBlock(p.getId());
if (old.contains(b)) {
log(" move key:" + o + " block:" + b.id);
Object value = data.get(o);
data.remove(o);
int todo2;
// data.put(o, n.getData());
data.put(o, value);
}
}
}
......@@ -657,6 +661,7 @@ public class BtreeMapStore {
* @param string the string to log
*/
public void log(String string) {
// TODO logging
// System.out.println(string);
}
......
......@@ -135,6 +135,29 @@ class Page {
return null;
}
/**
* Get the value for the given key, or null if not found.
*
* @param key the key
* @return the page or null
*/
Page findPage(Object key) {
int x = findKey(key);
if (children != null) {
if (x < 0) {
x = -x - 1;
} else {
x++;
}
Page p = map.readPage(children[x]);
return p.findPage(key);
}
if (x >= 0) {
return this;
}
return null;
}
private int findKey(Object key) {
int low = 0, high = keys.length - 1;
while (low <= high) {
......@@ -175,7 +198,6 @@ class Page {
* @param key the key
*/
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) {
......@@ -184,11 +206,11 @@ class Page {
} else {
x++;
}
p = p.map.readPage(p.children[x]);
CursorPos c = new CursorPos();
c.page = p;
c.index = x;
parents.add(c);
p = p.map.readPage(p.children[x]);
} else {
if (x < 0) {
x = -x - 1;
......@@ -209,12 +231,11 @@ class Page {
* @return the next key
*/
public static Object nextKey(ArrayList<CursorPos> parents) {
int todoTest;
if (parents.size() == 0) {
return null;
}
while (true) {
// TODO avoid remove/add pairs is possible
// TODO avoid remove/add pairs if possible
CursorPos p = parents.remove(parents.size() - 1);
int index = p.index++;
if (index < p.page.keys.length) {
......@@ -229,7 +250,9 @@ class Page {
index = p.index++;
if (index < p.page.children.length) {
parents.add(p);
min(p.page, parents, null);
Page x = p.page;
x = x.map.readPage(x.children[index]);
min(x, parents, null);
break;
}
}
......@@ -321,6 +344,11 @@ class Page {
int index = p.findKey(key);
if (p.isLeaf()) {
if (index >= 0) {
// create a copy
// TODO might not be required, but needs a "modified" flag
Object[] v2 = new Object[p.values.length];
System.arraycopy(p.values, 0, v2, 0, v2.length);
p.values = v2;
p.values[index] = value;
break;
}
......@@ -398,26 +426,6 @@ class Page {
return top;
}
/**
* Remove a key.
*
* @param key the key
* @return the new page or null if the page is now empty
*/
private Page remove(Object key) {
int index = findKey(key);
if (isLeaf()) {
if (index < 0) {
// not found
return this;
}
}
Page p = copyOnWrite();
p = p.remove(key);
return p;
}
private void insert(int index, Object key, Object value, long child) {
Object[] newKeys = new Object[keys.length + 1];
copyWithGap(keys, newKeys, keys.length, index);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论