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

A persistent tree map (work in progress).

上级 3ff388a0
...@@ -4,9 +4,11 @@ ...@@ -4,9 +4,11 @@
* (http://h2database.com/html/license.html). * (http://h2database.com/html/license.html).
* Initial Developer: H2 Group * Initial Developer: H2 Group
*/ */
package org.h2.dev.store.btree; package org.h2.test.store;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import org.h2.dev.store.btree.DataType;
import org.h2.dev.store.btree.DataUtils;
/** /**
* An integer type. * An integer type.
...@@ -21,6 +23,14 @@ class IntegerType implements DataType { ...@@ -21,6 +23,14 @@ class IntegerType implements DataType {
return DataUtils.getVarIntLen((Integer) obj); return DataUtils.getVarIntLen((Integer) obj);
} }
public int getMaxLength(Object obj) {
return DataUtils.MAX_VAR_INT_LEN;
}
public int getMemory(Object obj) {
return 20;
}
public Integer read(ByteBuffer buff) { public Integer read(ByteBuffer buff) {
return DataUtils.readVarInt(buff); return DataUtils.readVarInt(buff);
} }
......
/*
* 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.store;
import java.nio.ByteBuffer;
import org.h2.dev.store.btree.DataType;
import org.h2.dev.store.btree.DataTypeFactory;
import org.h2.dev.store.btree.DataUtils;
import org.h2.util.StringUtils;
/**
* A row type.
*/
public class RowType implements DataType {
private final DataType[] types;
private RowType(DataType[] types) {
this.types = types;
}
public int compare(Object a, Object b) {
if (a == b) {
return 0;
}
Object[] ax = (Object[]) a;
Object[] bx = (Object[]) b;
int al = ax.length;
int bl = bx.length;
int len = Math.min(al, bl);
for (int i = 0; i < len; i++) {
int comp = types[i].compare(ax[i], bx[i]);
if (comp != 0) {
return comp;
}
}
if (len < al) {
return -1;
} else if (len < bl) {
return 1;
}
return 0;
}
public int length(Object obj) {
Object[] x = (Object[]) obj;
int len = x.length;
int result = DataUtils.getVarIntLen(len);
for (int i = 0; i < len; i++) {
result += types[i].length(x[i]);
}
return result;
}
public int getMaxLength(Object obj) {
Object[] x = (Object[]) obj;
int len = x.length;
int result = DataUtils.MAX_VAR_INT_LEN;
for (int i = 0; i < len; i++) {
result += types[i].getMaxLength(x[i]);
}
return result;
}
public int getMemory(Object obj) {
Object[] x = (Object[]) obj;
int len = x.length;
int memory = 0;
for (int i = 0; i < len; i++) {
memory += types[i].getMemory(x[i]);
}
return memory;
}
public Object[] read(ByteBuffer buff) {
int len = DataUtils.readVarInt(buff);
Object[] x = new Object[len];
for (int i = 0; i < len; i++) {
x[i] = types[i].read(buff);
}
return x;
}
public void write(ByteBuffer buff, Object obj) {
Object[] x = (Object[]) obj;
int len = x.length;
DataUtils.writeVarInt(buff, len);
for (int i = 0; i < len; i++) {
types[i].write(buff, x[i]);
}
}
public String asString() {
StringBuilder buff = new StringBuilder();
buff.append('r');
buff.append('(');
for (int i = 0; i < types.length; i++) {
if (i > 0) {
buff.append(',');
}
buff.append(types[i].asString());
}
buff.append(')');
return buff.toString();
}
static RowType fromString(String t, DataTypeFactory factory) {
if (!t.startsWith("r(") || !t.endsWith(")")) {
throw new RuntimeException("Unknown type: " + t);
}
t = t.substring(2, t.length() - 1);
String[] array = StringUtils.arraySplit(t, ',', false);
DataType[] types = new DataType[array.length];
for (int i = 0; i < array.length; i++) {
types[i] = factory.fromString(array[i]);
}
return new RowType(types);
}
}
...@@ -29,6 +29,7 @@ public class TestTreeMapStore extends TestBase { ...@@ -29,6 +29,7 @@ public class TestTreeMapStore extends TestBase {
} }
public void test() throws Exception { public void test() throws Exception {
testLargeImport();
testBtreeStore(); testBtreeStore();
testDefragment(); testDefragment();
testReuseSpace(); testReuseSpace();
...@@ -38,10 +39,39 @@ public class TestTreeMapStore extends TestBase { ...@@ -38,10 +39,39 @@ public class TestTreeMapStore extends TestBase {
testSimple(); testSimple();
} }
private void testLargeImport() throws Exception {
String fileName = getBaseDir() + "/testCsv.h3";
int len = 10000;
for (int j = 0; j < 5; j++) {
FileUtils.delete(fileName);
BtreeMapStore s = openStore(fileName);
s.setMaxPageSize(40);
RowType rowType = RowType.fromString("r(i,,)", new TestTypeFactory());
BtreeMap<Integer, Object[]> m = s.openMap("data", new IntegerType(), rowType);
int i = 0;
// long t = System.currentTimeMillis();
for (; i < len;) {
Object[] o = new Object[3];
o[0] = i;
o[1] = "Hello";
o[2] = "World";
m.put(i, o);
i++;
if (i % 10000 == 0) {
s.store();
}
}
s.store();
s.close();
// System.out.println("store time " + (System.currentTimeMillis() - t));
// System.out.println("store size " + FileUtils.size(fileName));
}
}
private void testBtreeStore() { private void testBtreeStore() {
String fileName = getBaseDir() + "/testBtreeStore.h3"; String fileName = getBaseDir() + "/testBtreeStore.h3";
FileUtils.delete(fileName); FileUtils.delete(fileName);
BtreeMapStore s = BtreeMapStore.open(fileName); BtreeMapStore s = openStore(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 = 2000; int count = 2000;
// Profiler p = new Profiler(); // Profiler p = new Profiler();
...@@ -64,8 +94,9 @@ public class TestTreeMapStore extends TestBase { ...@@ -64,8 +94,9 @@ public class TestTreeMapStore extends TestBase {
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.store();
s.close(); s.close();
s = BtreeMapStore.open(fileName); s = openStore(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++) {
...@@ -87,7 +118,7 @@ public class TestTreeMapStore extends TestBase { ...@@ -87,7 +118,7 @@ public class TestTreeMapStore extends TestBase {
FileUtils.delete(fileName); FileUtils.delete(fileName);
long initialLength = 0; long initialLength = 0;
for (int j = 0; j < 20; j++) { for (int j = 0; j < 20; j++) {
BtreeMapStore s = BtreeMapStore.open(fileName); BtreeMapStore s = openStore(fileName);
BtreeMap<Integer, String> m = s.openMap("data", Integer.class, String.class); BtreeMap<Integer, String> m = s.openMap("data", Integer.class, String.class);
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
m.put(j + i, "Hello " + j); m.put(j + i, "Hello " + j);
...@@ -103,9 +134,9 @@ public class TestTreeMapStore extends TestBase { ...@@ -103,9 +134,9 @@ public class TestTreeMapStore extends TestBase {
assertTrue("initial: " + initialLength + " len: " + len, len <= initialLength * 3); assertTrue("initial: " + initialLength + " len: " + len, len <= initialLength * 3);
} }
} }
long len = FileUtils.size(fileName); // long len = FileUtils.size(fileName);
// System.out.println("len0: " + len); // System.out.println("len0: " + len);
BtreeMapStore s = BtreeMapStore.open(fileName); BtreeMapStore s = openStore(fileName);
BtreeMap<Integer, String> m = s.openMap("data", Integer.class, String.class); BtreeMap<Integer, String> m = s.openMap("data", Integer.class, String.class);
for (int i = 0; i < 100; i++) { for (int i = 0; i < 100; i++) {
m.remove(i); m.remove(i);
...@@ -113,13 +144,13 @@ public class TestTreeMapStore extends TestBase { ...@@ -113,13 +144,13 @@ public class TestTreeMapStore extends TestBase {
s.store(); s.store();
s.compact(); s.compact();
s.close(); s.close();
len = FileUtils.size(fileName); // len = FileUtils.size(fileName);
// System.out.println("len1: " + len); // System.out.println("len1: " + len);
s = BtreeMapStore.open(fileName); s = openStore(fileName);
m = s.openMap("data", Integer.class, String.class); m = s.openMap("data", Integer.class, String.class);
s.compact(); s.compact();
s.close(); s.close();
len = FileUtils.size(fileName); // len = FileUtils.size(fileName);
// System.out.println("len2: " + len); // System.out.println("len2: " + len);
} }
...@@ -127,14 +158,15 @@ public class TestTreeMapStore extends TestBase { ...@@ -127,14 +158,15 @@ public class TestTreeMapStore extends TestBase {
String fileName = getBaseDir() + "/testReuseSpace.h3"; String fileName = getBaseDir() + "/testReuseSpace.h3";
FileUtils.delete(fileName); FileUtils.delete(fileName);
long initialLength = 0; long initialLength = 0;
for (int j = 0; j < 10; j++) { for (int j = 0; j < 20; j++) {
BtreeMapStore s = BtreeMapStore.open(fileName); BtreeMapStore s = openStore(fileName);
BtreeMap<Integer, String> m = s.openMap("data", Integer.class, String.class); BtreeMap<Integer, String> m = s.openMap("data", Integer.class, String.class);
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
m.put(i, "Hello"); m.put(i, "Hello");
} }
s.store(); s.store();
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
assertEquals("Hello", m.get(i));
m.remove(i); m.remove(i);
} }
s.store(); s.store();
...@@ -151,23 +183,25 @@ public class TestTreeMapStore extends TestBase { ...@@ -151,23 +183,25 @@ public class TestTreeMapStore extends TestBase {
private void testRandom() { private void testRandom() {
String fileName = getBaseDir() + "/testRandom.h3"; String fileName = getBaseDir() + "/testRandom.h3";
FileUtils.delete(fileName); FileUtils.delete(fileName);
BtreeMapStore s = BtreeMapStore.open(fileName); BtreeMapStore s = openStore(fileName);
BtreeMap<Integer, Integer> m = s.openMap("data", Integer.class, Integer.class); BtreeMap<Integer, Integer> m = s.openMap("data", Integer.class, Integer.class);
TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>(); TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>();
Random r = new Random(1); Random r = new Random(1);
int operationCount = 1000; int operationCount = 10000;
int maxValue = 20; int maxValue = 30;
for (int i = 0; i < operationCount; i++) { for (int i = 0; i < operationCount; i++) {
int k = r.nextInt(maxValue); int k = r.nextInt(maxValue);
int v = r.nextInt(); int v = r.nextInt();
boolean compareAll; boolean compareAll;
switch (r.nextInt(3)) { switch (r.nextInt(3)) {
case 0: case 0:
log(i + ": put " + k + " = " + v);
m.put(k, v); m.put(k, v);
map.put(k, v); map.put(k, v);
compareAll = true; compareAll = true;
break; break;
case 1: case 1:
log(i + ": remove " + k);
m.remove(k); m.remove(k);
map.remove(k); map.remove(k);
compareAll = true; compareAll = true;
...@@ -185,10 +219,12 @@ public class TestTreeMapStore extends TestBase { ...@@ -185,10 +219,12 @@ public class TestTreeMapStore extends TestBase {
} }
if (compareAll) { if (compareAll) {
Iterator<Integer> it = m.keyIterator(null); Iterator<Integer> it = m.keyIterator(null);
Iterator<Integer> it2 = map.keySet().iterator(); Iterator<Integer> itExpected = map.keySet().iterator();
while (it2.hasNext()) { while (itExpected.hasNext()) {
assertTrue(it.hasNext()); assertTrue(it.hasNext());
assertEquals(it2.next(), it.next()); Integer expected = itExpected.next();
Integer got = it.next();
assertEquals(expected, got);
} }
assertFalse(it.hasNext()); assertFalse(it.hasNext());
} }
...@@ -199,7 +235,7 @@ public class TestTreeMapStore extends TestBase { ...@@ -199,7 +235,7 @@ public class TestTreeMapStore extends TestBase {
private void testKeyValueClasses() { private void testKeyValueClasses() {
String fileName = getBaseDir() + "/testKeyValueClasses.h3"; String fileName = getBaseDir() + "/testKeyValueClasses.h3";
FileUtils.delete(fileName); FileUtils.delete(fileName);
BtreeMapStore s = BtreeMapStore.open(fileName); BtreeMapStore s = openStore(fileName);
BtreeMap<Integer, String> is = s.openMap("intString", Integer.class, String.class); BtreeMap<Integer, String> is = s.openMap("intString", Integer.class, String.class);
is.put(1, "Hello"); is.put(1, "Hello");
BtreeMap<Integer, Integer> ii = s.openMap("intInt", Integer.class, Integer.class); BtreeMap<Integer, Integer> ii = s.openMap("intInt", Integer.class, Integer.class);
...@@ -220,8 +256,9 @@ public class TestTreeMapStore extends TestBase { ...@@ -220,8 +256,9 @@ public class TestTreeMapStore extends TestBase {
} catch (RuntimeException e) { } catch (RuntimeException e) {
// expected // expected
} }
s.store();
s.close(); s.close();
s = BtreeMapStore.open(fileName); s = openStore(fileName);
is = s.openMap("intString", Integer.class, String.class); is = s.openMap("intString", Integer.class, String.class);
assertEquals("Hello", is.get(1)); assertEquals("Hello", is.get(1));
ii = s.openMap("intInt", Integer.class, Integer.class); ii = s.openMap("intInt", Integer.class, Integer.class);
...@@ -236,7 +273,7 @@ public class TestTreeMapStore extends TestBase { ...@@ -236,7 +273,7 @@ public class TestTreeMapStore extends TestBase {
private void testIterate() { private void testIterate() {
String fileName = getBaseDir() + "/testIterate.h3"; String fileName = getBaseDir() + "/testIterate.h3";
FileUtils.delete(fileName); FileUtils.delete(fileName);
BtreeMapStore s = BtreeMapStore.open(fileName); BtreeMapStore s = openStore(fileName);
BtreeMap<Integer, String> m = s.openMap("data", Integer.class, String.class); BtreeMap<Integer, String> m = s.openMap("data", Integer.class, String.class);
Iterator<Integer> it = m.keyIterator(null); Iterator<Integer> it = m.keyIterator(null);
assertFalse(it.hasNext()); assertFalse(it.hasNext());
...@@ -269,7 +306,7 @@ public class TestTreeMapStore extends TestBase { ...@@ -269,7 +306,7 @@ public class TestTreeMapStore extends TestBase {
private void testSimple() { private void testSimple() {
String fileName = getBaseDir() + "/testSimple.h3"; String fileName = getBaseDir() + "/testSimple.h3";
FileUtils.delete(fileName); FileUtils.delete(fileName);
BtreeMapStore s = BtreeMapStore.open(fileName); BtreeMapStore s = openStore(fileName);
BtreeMap<Integer, String> m = s.openMap("data", Integer.class, String.class); BtreeMap<Integer, String> m = s.openMap("data", Integer.class, String.class);
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
m.put(i, "hello " + i); m.put(i, "hello " + i);
...@@ -281,10 +318,10 @@ public class TestTreeMapStore extends TestBase { ...@@ -281,10 +318,10 @@ public class TestTreeMapStore extends TestBase {
for (int i = 1; i < 3; i++) { for (int i = 1; i < 3; i++) {
assertEquals("hello " + i, m.get(i)); assertEquals("hello " + i, m.get(i));
} }
s.store();
s.close(); s.close();
s = BtreeMapStore.open(fileName); s = openStore(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 < 3; i++) { for (int i = 1; i < 3; i++) {
...@@ -293,4 +330,19 @@ public class TestTreeMapStore extends TestBase { ...@@ -293,4 +330,19 @@ public class TestTreeMapStore extends TestBase {
s.close(); s.close();
} }
private static BtreeMapStore openStore(String fileName) {
BtreeMapStore store = BtreeMapStore.open(fileName, new TestTypeFactory());
store.setMaxPageSize(10);
return store;
}
/**
* Log the message.
*
* @param msg the message
*/
private static void log(String msg) {
// System.out.println(msg);
}
} }
/*
* 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.store;
import org.h2.dev.store.btree.DataType;
import org.h2.dev.store.btree.DataTypeFactory;
import org.h2.dev.store.btree.StringType;
/**
* A data type factory.
*/
public class TestTypeFactory implements DataTypeFactory {
public DataType fromString(String s) {
if (s.length() == 0) {
return new StringType();
}
switch (s.charAt(0)) {
case 'i':
return new IntegerType();
case 'r':
return RowType.fromString(s, this);
}
throw new RuntimeException("Unknown data type " + s);
}
public DataType getDataType(Class<?> objectClass) {
if (objectClass == Integer.class) {
return new IntegerType();
}
throw new RuntimeException("Unsupported object class " + objectClass.toString());
}
}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!--
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
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" /><title>
Javadoc package documentation
</title></head><body style="font: 9pt/130% Tahoma, Arial, Helvetica, sans-serif; font-weight: normal;"><p>
This package contains tests for the map store.
</p></body></html>
\ No newline at end of file
...@@ -17,13 +17,15 @@ import java.util.Iterator; ...@@ -17,13 +17,15 @@ import java.util.Iterator;
public class BtreeMap<K, V> { public class BtreeMap<K, V> {
private final BtreeMapStore store; private final BtreeMapStore store;
private final long id;
private final String name; private final String name;
private final DataType keyType; private final DataType keyType;
private final DataType valueType; private final DataType valueType;
private Page root; private Page root;
private BtreeMap(BtreeMapStore store, String name, DataType keyType, DataType valueType) { private BtreeMap(BtreeMapStore store, long id, String name, DataType keyType, DataType valueType) {
this.store = store; this.store = store;
this.id = id;
this.name = name; this.name = name;
this.keyType = keyType; this.keyType = keyType;
this.valueType = valueType; this.valueType = valueType;
...@@ -40,8 +42,8 @@ public class BtreeMap<K, V> { ...@@ -40,8 +42,8 @@ public class BtreeMap<K, V> {
* @param valueClass the value class * @param valueClass the value class
* @return the map * @return the map
*/ */
static <K, V> BtreeMap<K, V> open(BtreeMapStore store, String name, DataType keyType, DataType valueType) { static <K, V> BtreeMap<K, V> open(BtreeMapStore store, long id, String name, DataType keyType, DataType valueType) {
return new BtreeMap<K, V>(store, name, keyType, valueType); return new BtreeMap<K, V>(store, id, name, keyType, valueType);
} }
/** /**
...@@ -51,9 +53,7 @@ public class BtreeMap<K, V> { ...@@ -51,9 +53,7 @@ public class BtreeMap<K, V> {
* @param data the value * @param data the value
*/ */
public void put(K key, V data) { public void put(K key, V data) {
if (!isChanged()) { markChanged();
store.markChanged(name, this);
}
root = Page.put(this, root, key, data); root = Page.put(this, root, key, data);
} }
...@@ -77,23 +77,32 @@ public class BtreeMap<K, V> { ...@@ -77,23 +77,32 @@ public class BtreeMap<K, V> {
* @param key the key * @param key the key
* @return the value, or null if not found * @return the value, or null if not found
*/ */
public Page getPage(K key) { Page getPage(K key) {
if (root == null) { if (root == null) {
return null; return null;
} }
return root.findPage(key); return root.findPage(key);
} }
/**
* Remove all entries.
*/
public void clear() {
if (root != null) {
markChanged();
root.removeAllRecursive();
root = null;
}
}
/** /**
* Remove a key-value pair. * Remove a key-value pair.
* *
* @param key the key * @param key the key
*/ */
public void remove(K key) { public void remove(K key) {
if (!isChanged()) {
store.markChanged(name, this);
}
if (root != null) { if (root != null) {
markChanged();
root = Page.remove(root, key); root = Page.remove(root, key);
} }
} }
...@@ -107,6 +116,12 @@ public class BtreeMap<K, V> { ...@@ -107,6 +116,12 @@ public class BtreeMap<K, V> {
return root != null && root.getId() < 0; return root != null && root.getId() < 0;
} }
private void markChanged() {
if (!isChanged()) {
store.markChanged(name, this);
}
}
/** /**
* Compare two keys. * Compare two keys.
* *
...@@ -206,4 +221,12 @@ public class BtreeMap<K, V> { ...@@ -206,4 +221,12 @@ public class BtreeMap<K, V> {
return name; return name;
} }
int getMaxPageSize() {
return store.getMaxPageSize();
}
long getId() {
return id;
}
} }
...@@ -11,12 +11,12 @@ import java.io.IOException; ...@@ -11,12 +11,12 @@ import java.io.IOException;
import java.util.Properties; import java.util.Properties;
/** /**
* A block of data. * A chunk of data, containing one or multiple pages
*/ */
class Block { class Chunk {
/** /**
* The block id. * The chunk id.
*/ */
int id; int id;
...@@ -26,7 +26,7 @@ class Block { ...@@ -26,7 +26,7 @@ class Block {
long start; long start;
/** /**
* The length in bytes. * The length in bytes (may be larger than the actual value).
*/ */
long length; long length;
...@@ -45,7 +45,12 @@ class Block { ...@@ -45,7 +45,12 @@ class Block {
*/ */
int collectPriority; int collectPriority;
Block(int id) { /**
* The offset of the meta root.
*/
int metaRootOffset;
Chunk(int id) {
this.id = id; this.id = id;
} }
...@@ -55,17 +60,18 @@ class Block { ...@@ -55,17 +60,18 @@ class Block {
* @param s the string * @param s the string
* @return the block * @return the block
*/ */
static Block fromString(String s) { static Chunk fromString(String s) {
Block b = new Block(0); Chunk c = new Chunk(0);
Properties prop = new Properties(); Properties prop = new Properties();
try { try {
prop.load(new ByteArrayInputStream(s.getBytes("UTF-8"))); prop.load(new ByteArrayInputStream(s.getBytes("UTF-8")));
b.id = Integer.parseInt(prop.get("id").toString()); c.id = Integer.parseInt(prop.get("id").toString());
b.start = Long.parseLong(prop.get("start").toString()); c.start = Long.parseLong(prop.get("start").toString());
b.length = Long.parseLong(prop.get("length").toString()); c.length = Long.parseLong(prop.get("length").toString());
b.entryCount = Integer.parseInt(prop.get("entryCount").toString()); c.entryCount = Integer.parseInt(prop.get("entryCount").toString());
b.liveCount = Integer.parseInt(prop.get("liveCount").toString()); c.liveCount = Integer.parseInt(prop.get("liveCount").toString());
return b; c.metaRootOffset = Integer.parseInt(prop.get("metaRoot").toString());
return c;
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
...@@ -80,7 +86,7 @@ class Block { ...@@ -80,7 +86,7 @@ class Block {
} }
public boolean equals(Object o) { public boolean equals(Object o) {
return o instanceof Block && ((Block) o).id == id; return o instanceof Chunk && ((Chunk) o).id == id;
} }
public String toString() { public String toString() {
...@@ -89,7 +95,8 @@ class Block { ...@@ -89,7 +95,8 @@ class Block {
"start:" + start + "\n" + "start:" + start + "\n" +
"length:" + length + "\n" + "length:" + length + "\n" +
"entryCount:" + entryCount + "\n" + "entryCount:" + entryCount + "\n" +
"liveCount:" + liveCount + "\n"; "liveCount:" + liveCount + "\n" +
"metaRoot:" + metaRootOffset + "\n";
} }
} }
......
...@@ -11,8 +11,11 @@ import java.util.Iterator; ...@@ -11,8 +11,11 @@ import java.util.Iterator;
/** /**
* A cursor to iterate over elements in ascending order. * A cursor to iterate over elements in ascending order.
*
* @param <K> the key type
*/ */
class Cursor<K> implements Iterator<K> { class Cursor<K> implements Iterator<K> {
private ArrayList<CursorPos> parents = new ArrayList<CursorPos>(); private ArrayList<CursorPos> parents = new ArrayList<CursorPos>();
private K current; private K current;
......
...@@ -23,20 +23,37 @@ public interface DataType { ...@@ -23,20 +23,37 @@ public interface DataType {
int compare(Object a, Object b); int compare(Object a, Object b);
/** /**
* Get the length in bytes. * Get the length in bytes used to store an object.
* *
* @param obj the object * @param obj the object
* @return the length * @return the length
*/ */
int length(Object obj); int length(Object obj);
/**
* Get the maximum length in bytes used to store an object. In many cases,
* this method can be faster than calculating the exact length.
*
* @param obj the object
* @return the maximum length
*/
int getMaxLength(Object obj);
/**
* Estimate the used memory in bytes.
*
* @param obj the object
* @return the used memory
*/
int getMemory(Object obj);
/** /**
* Write the object. * Write the object.
* *
* @param buff the target buffer * @param buff the target buffer
* @param x the value * @param x the value
*/ */
void write(ByteBuffer buff, Object x); void write(ByteBuffer buff, Object obj);
/** /**
* Read an object. * Read an object.
......
...@@ -6,34 +6,18 @@ ...@@ -6,34 +6,18 @@
*/ */
package org.h2.dev.store.btree; package org.h2.dev.store.btree;
import java.io.IOException;
import java.io.StringReader;
/** /**
* A factory for data types. * A factory for data types.
*/ */
public class DataTypeFactory { public interface DataTypeFactory {
/** /**
* Read the data type. * Read the data type.
* *
* @param buff the buffer * @param s the string
* @return the type * @return the type
*/ */
static DataType fromString(String s) { DataType fromString(String s);
StringReader r = new StringReader(s);
char c;
try {
c = (char) r.read();
} catch (IOException e) {
throw new RuntimeException(e);
}
switch (c) {
case 'i':
return new IntegerType();
}
throw new RuntimeException("Unknown data type " + c);
}
/** /**
* Get the data type object for the given class. * Get the data type object for the given class.
...@@ -41,13 +25,6 @@ public class DataTypeFactory { ...@@ -41,13 +25,6 @@ public class DataTypeFactory {
* @param objectClass the class * @param objectClass the class
* @return the data type object * @return the data type object
*/ */
static DataType getDataType(Class<?> objectClass) { DataType getDataType(Class<?> objectClass);
if (objectClass == Integer.class) {
return new IntegerType();
} else if (objectClass == String.class) {
return new StringType();
}
throw new RuntimeException("Unsupported object class " + objectClass.toString());
}
} }
...@@ -13,13 +13,18 @@ import java.nio.ByteBuffer; ...@@ -13,13 +13,18 @@ import java.nio.ByteBuffer;
*/ */
public class DataUtils { public class DataUtils {
/**
* The maximum length of a variable size int.
*/
public static final int MAX_VAR_INT_LEN = 5;
/** /**
* Get the length of the variable size int. * Get the length of the variable size int.
* *
* @param x the value * @param x the value
* @return the length in bytes * @return the length in bytes
*/ */
static int getVarIntLen(int x) { public static int getVarIntLen(int x) {
if ((x & (-1 << 7)) == 0) { if ((x & (-1 << 7)) == 0) {
return 1; return 1;
} else if ((x & (-1 << 14)) == 0) { } else if ((x & (-1 << 14)) == 0) {
...@@ -55,7 +60,7 @@ public class DataUtils { ...@@ -55,7 +60,7 @@ public class DataUtils {
* @param buff the source buffer * @param buff the source buffer
* @return the value * @return the value
*/ */
static int readVarInt(ByteBuffer buff) { public static int readVarInt(ByteBuffer buff) {
int b = buff.get(); int b = buff.get();
if (b >= 0) { if (b >= 0) {
return b; return b;
...@@ -111,7 +116,7 @@ public class DataUtils { ...@@ -111,7 +116,7 @@ public class DataUtils {
* @param buff the target buffer * @param buff the target buffer
* @param x the value * @param x the value
*/ */
static void writeVarInt(ByteBuffer buff, int x) { public static void writeVarInt(ByteBuffer buff, int x) {
while ((x & ~0x7f) != 0) { while ((x & ~0x7f) != 0) {
buff.put((byte) (0x80 | (x & 0x7f))); buff.put((byte) (0x80 | (x & 0x7f)));
x >>>= 7; x >>>= 7;
...@@ -133,4 +138,22 @@ public class DataUtils { ...@@ -133,4 +138,22 @@ public class DataUtils {
buff.put((byte) x); buff.put((byte) x);
} }
static void copyWithGap(Object src, Object dst, int oldSize, int gapIndex) {
if (gapIndex > 0) {
System.arraycopy(src, 0, dst, 0, gapIndex);
}
if (gapIndex < oldSize) {
System.arraycopy(src, gapIndex, dst, gapIndex + 1, oldSize - gapIndex);
}
}
static void copyExcept(Object src, Object dst, int oldSize, int removeIndex) {
if (removeIndex > 0 && oldSize > 0) {
System.arraycopy(src, 0, dst, 0, removeIndex);
}
if (removeIndex < oldSize) {
System.arraycopy(src, removeIndex + 1, dst, removeIndex, oldSize - removeIndex - 1);
}
}
} }
/*
* 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.btree;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringReader;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.Properties;
import org.h2.store.fs.FilePath;
import org.h2.store.fs.FileUtils;
/**
* Convert a database file to a human-readable text dump.
*/
public class Dump {
private static int blockSize = 4 * 1024;
public static void main(String... args) {
String fileName = "test.h3";
for (int i = 0; i < args.length; i++) {
if ("-file".equals(args[i])) {
fileName = args[++i];
}
}
dump(fileName);
}
public static void dump(String fileName) {
if (!FileUtils.exists(fileName)) {
System.out.println("File not found: " + fileName);
return;
}
FileChannel file = null;
try {
file = FilePath.get(fileName).open("r");
long fileLength = file.size();
file.position(0);
byte[] header = new byte[blockSize];
file.read(ByteBuffer.wrap(header));
Properties prop = new Properties();
prop.load(new ByteArrayInputStream(header));
prop.load(new StringReader(new String(header, "UTF-8")));
System.out.println("file " + fileName);
System.out.println(" length " + fileLength);
System.out.println(" " + prop);
ByteBuffer block = ByteBuffer.wrap(new byte[16]);
for (long pos = 0; pos < fileLength;) {
file.position(pos);
block.rewind();
FileUtils.readFully(file, block);
block.rewind();
if (block.get() != 'c') {
pos += blockSize;
continue;
}
int length = block.getInt();
int chunkId = block.getInt();
int metaRootOffset = block.getInt();
System.out.println(" chunk " + chunkId + " at " + pos +
" length " + length + " offset " + metaRootOffset);
ByteBuffer chunk = ByteBuffer.allocate(length);
file.position(pos);
FileUtils.readFully(file, chunk);
int p = block.position();
pos = (pos + length + blockSize) / blockSize * blockSize;
length -= p;
while (length > 0) {
chunk.position(p);
int len = chunk.getInt();
long mapId = chunk.getLong();
int type = chunk.get();
int count = DataUtils.readVarInt(chunk);
if (type == 1) {
long[] children = new long[count];
for (int i = 0; i < count; i++) {
children[i] = chunk.getLong();
}
System.out.println(" map " + mapId + " at " + p + " node, " + count + " children: " + Arrays.toString(children));
} else {
System.out.println(" map " + mapId + " at " + p + " leaf, " + count + " rows");
}
p += len;
length -= len;
}
}
} catch (IOException e) {
System.out.println("ERROR: " + e);
} finally {
try {
file.close();
} catch (IOException e) {
// ignore
}
}
System.out.println();
}
}
...@@ -11,39 +11,67 @@ import java.nio.ByteBuffer; ...@@ -11,39 +11,67 @@ import java.nio.ByteBuffer;
/** /**
* A string type. * A string type.
*/ */
class StringType implements DataType { public class StringType implements DataType {
public int compare(Object a, Object b) { public int compare(Object a, Object b) {
return a.toString().compareTo(b.toString()); return a.toString().compareTo(b.toString());
} }
public int length(Object obj) { public int length(Object obj) {
try { int plus = 0;
byte[] bytes = obj.toString().getBytes("UTF-8"); String s = obj.toString();
return DataUtils.getVarIntLen(bytes.length) + bytes.length; int len = s.length();
} catch (Exception e) { for (int i = 0; i < len; i++) {
throw new RuntimeException(e); char c = s.charAt(i);
if (c >= 0x800) {
plus += 2;
} else if (c >= 0x80) {
plus++;
} }
} }
return DataUtils.getVarIntLen(len) + len + plus;
}
public int getMaxLength(Object obj) {
return DataUtils.MAX_VAR_INT_LEN + obj.toString().length() * 3;
}
public int getMemory(Object obj) {
return obj.toString().length() * 2 + 48;
}
public String read(ByteBuffer buff) { public String read(ByteBuffer buff) {
int len = DataUtils.readVarInt(buff); int len = DataUtils.readVarInt(buff);
byte[] bytes = new byte[len]; char[] chars = new char[len];
buff.get(bytes); for (int i = 0; i < len; i++) {
try { int x = buff.get() & 0xff;
return new String(bytes, "UTF-8"); if (x < 0x80) {
} catch (Exception e) { chars[i] = (char) x;
throw new RuntimeException(e); } else if (x >= 0xe0) {
} chars[i] = (char) (((x & 0xf) << 12) + ((buff.get() & 0x3f) << 6) + (buff.get() & 0x3f));
} } else {
chars[i] = (char) (((x & 0x1f) << 6) + (buff.get() & 0x3f));
public void write(ByteBuffer buff, Object x) { }
try { }
byte[] bytes = x.toString().getBytes("UTF-8"); return new String(chars);
DataUtils.writeVarInt(buff, bytes.length); }
buff.put(bytes);
} catch (Exception e) { public void write(ByteBuffer buff, Object obj) {
throw new RuntimeException(e); String s = obj.toString();
int len = s.length();
DataUtils.writeVarInt(buff, len);
for (int i = 0; i < len; i++) {
int c = s.charAt(i);
if (c < 0x80) {
buff.put((byte) c);
} else if (c >= 0x800) {
buff.put((byte) (0xe0 | (c >> 12)));
buff.put((byte) (((c >> 6) & 0x3f)));
buff.put((byte) (c & 0x3f));
} else {
buff.put((byte) (0xc0 | (c >> 6)));
buff.put((byte) (c & 0x3f));
}
} }
} }
......
...@@ -132,7 +132,7 @@ public class StoredMap<K, V> { ...@@ -132,7 +132,7 @@ public class StoredMap<K, V> {
/** /**
* A value type. * A value type.
*/ */
static interface ValueType { interface ValueType {
/** /**
* Get the length in bytes. * Get the length in bytes.
...@@ -170,7 +170,7 @@ public class StoredMap<K, V> { ...@@ -170,7 +170,7 @@ public class StoredMap<K, V> {
/** /**
* A key type. * A key type.
*/ */
static interface KeyType extends ValueType { interface KeyType extends ValueType {
/** /**
* Compare two keys. * Compare two keys.
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论