提交 32bae90e authored 作者: Thomas Mueller's avatar Thomas Mueller

A persistent multi-version map (work in progress).

上级 6ab864dc
...@@ -8,7 +8,7 @@ package org.h2.test.store; ...@@ -8,7 +8,7 @@ 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.DataType;
import org.h2.dev.store.btree.DataTypeFactory; import org.h2.dev.store.btree.MapFactory;
import org.h2.dev.store.btree.DataUtils; import org.h2.dev.store.btree.DataUtils;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
...@@ -115,7 +115,7 @@ public class RowType implements DataType { ...@@ -115,7 +115,7 @@ public class RowType implements DataType {
* @param factory the data type factory * @param factory the data type factory
* @return the row type * @return the row type
*/ */
static RowType fromString(String t, DataTypeFactory factory) { static RowType fromString(String t, MapFactory factory) {
if (!t.startsWith("r(") || !t.endsWith(")")) { if (!t.startsWith("r(") || !t.endsWith(")")) {
throw new RuntimeException("Unknown type: " + t); throw new RuntimeException("Unknown type: " + t);
} }
...@@ -123,7 +123,7 @@ public class RowType implements DataType { ...@@ -123,7 +123,7 @@ public class RowType implements DataType {
String[] array = StringUtils.arraySplit(t, ',', false); String[] array = StringUtils.arraySplit(t, ',', false);
DataType[] types = new DataType[array.length]; DataType[] types = new DataType[array.length];
for (int i = 0; i < array.length; i++) { for (int i = 0; i < array.length; i++) {
types[i] = factory.fromString(array[i]); types[i] = factory.buildDataType(array[i]);
} }
return new RowType(types); return new RowType(types);
} }
......
/*
* 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.BtreeMap;
import org.h2.dev.store.btree.BtreeMapStore;
import org.h2.dev.store.btree.DataType;
/**
* A stored r-tree.
*
* @param <K> the key class
* @param <V> the value class
*/
public class RtreeMap<K, V> extends BtreeMap<K, V> {
RtreeMap(BtreeMapStore store, int id, String name, DataType keyType,
DataType valueType, long createVersion) {
super(store, id, name, keyType, valueType, createVersion);
}
}
/*
* 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.util.AbstractSet;
import java.util.Iterator;
import java.util.Set;
import org.h2.dev.store.btree.BtreeMap;
import org.h2.dev.store.btree.BtreeMapStore;
import org.h2.dev.store.btree.DataType;
/**
* A custom map returning the values 1 .. 10.
*
* @param <K> the key type
* @param <V> the key type
*/
public class SequenceMap<K, V> extends BtreeMap<K, V> {
int min = 1, max = 10;
SequenceMap(BtreeMapStore store, int id, String name, DataType keyType,
DataType valueType, long createVersion) {
super(store, id, name, keyType, valueType, createVersion);
setReadOnly(true);
}
public Set<K> keySet() {
return new AbstractSet<K>() {
@Override
public Iterator<K> iterator() {
return new Iterator<K>() {
int x = min;
@Override
public boolean hasNext() {
return x <= max;
}
@SuppressWarnings("unchecked")
@Override
public K next() {
return (K) Integer.valueOf(x++);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public int size() {
return max - min + 1;
}
};
}
}
...@@ -29,6 +29,7 @@ public class TestBtreeMapStore extends TestBase { ...@@ -29,6 +29,7 @@ public class TestBtreeMapStore extends TestBase {
} }
public void test() { public void test() {
testCustomMapType();
testTruncateFile(); testTruncateFile();
testFastDelete(); testFastDelete();
testRollbackInMemory(); testRollbackInMemory();
...@@ -44,6 +45,20 @@ public class TestBtreeMapStore extends TestBase { ...@@ -44,6 +45,20 @@ public class TestBtreeMapStore extends TestBase {
testSimple(); testSimple();
} }
private void testCustomMapType() {
String fileName = getBaseDir() + "/testMeta.h3";
FileUtils.delete(fileName);
BtreeMapStore s;
s = openStore(fileName);
SequenceMap<Integer, String> seq = s.openMap("data", "s", "i", "").cast();
StringBuilder buff = new StringBuilder();
for (int x : seq.keySet()) {
buff.append(x).append(';');
}
assertEquals("1;2;3;4;5;6;7;8;9;10;", buff.toString());
s.close();
}
private void testTruncateFile() { private void testTruncateFile() {
String fileName = getBaseDir() + "/testMeta.h3"; String fileName = getBaseDir() + "/testMeta.h3";
FileUtils.delete(fileName); FileUtils.delete(fileName);
...@@ -233,11 +248,11 @@ public class TestBtreeMapStore extends TestBase { ...@@ -233,11 +248,11 @@ public class TestBtreeMapStore extends TestBase {
data.put("1", "Hello"); data.put("1", "Hello");
data.put("2", "World"); data.put("2", "World");
s.store(); s.store();
assertEquals("1/1//", m.get("map.data")); assertEquals("1/1///", m.get("map.data"));
assertTrue(m.containsKey("chunk.1")); assertTrue(m.containsKey("chunk.1"));
data.put("1", "Hallo"); data.put("1", "Hallo");
s.store(); s.store();
assertEquals("1/1//", m.get("map.data")); assertEquals("1/1///", m.get("map.data"));
assertTrue(m.get("root.1").length() > 0); assertTrue(m.get("root.1").length() > 0);
assertTrue(m.containsKey("chunk.1")); assertTrue(m.containsKey("chunk.1"));
assertTrue(m.containsKey("chunk.2")); assertTrue(m.containsKey("chunk.2"));
...@@ -255,8 +270,7 @@ public class TestBtreeMapStore extends TestBase { ...@@ -255,8 +270,7 @@ public class TestBtreeMapStore extends TestBase {
BtreeMapStore s = openStore(fileName); BtreeMapStore s = openStore(fileName);
// s.setCompressor(null); // s.setCompressor(null);
s.setMaxPageSize(40); s.setMaxPageSize(40);
RowType rowType = RowType.fromString("r(i,,)", new TestTypeFactory()); BtreeMap<Integer, Object[]> m = s.openMap("data", "", "i", "r(i,,)");
BtreeMap<Integer, Object[]> m = s.openMap("data", new IntegerType(), rowType);
int i = 0; int i = 0;
// long t = System.currentTimeMillis(); // long t = System.currentTimeMillis();
for (; i < len;) { for (; i < len;) {
...@@ -540,7 +554,7 @@ public class TestBtreeMapStore extends TestBase { ...@@ -540,7 +554,7 @@ public class TestBtreeMapStore extends TestBase {
} }
private static BtreeMapStore openStore(String fileName) { private static BtreeMapStore openStore(String fileName) {
BtreeMapStore store = BtreeMapStore.open(fileName, new TestTypeFactory()); BtreeMapStore store = BtreeMapStore.open(fileName, new TestMapFactory());
store.setMaxPageSize(10); store.setMaxPageSize(10);
return store; return store;
} }
......
...@@ -5,16 +5,29 @@ ...@@ -5,16 +5,29 @@
*/ */
package org.h2.test.store; package org.h2.test.store;
import org.h2.dev.store.btree.BtreeMap;
import org.h2.dev.store.btree.BtreeMapStore;
import org.h2.dev.store.btree.DataType; import org.h2.dev.store.btree.DataType;
import org.h2.dev.store.btree.DataTypeFactory; import org.h2.dev.store.btree.MapFactory;
import org.h2.dev.store.btree.StringType; import org.h2.dev.store.btree.StringType;
/** /**
* A data type factory. * A data type factory.
*/ */
public class TestTypeFactory implements DataTypeFactory { public class TestMapFactory implements MapFactory {
public DataType fromString(String s) { @Override
public <K, V> BtreeMap<K, V> buildMap(String mapType, BtreeMapStore store,
int id, String name, DataType keyType, DataType valueType,
long createVersion) {
if (mapType.equals("s")) {
return new SequenceMap<K, V>(store, id, name, keyType, valueType, createVersion);
}
throw new RuntimeException("Unsupported map type " + mapType);
}
@Override
public DataType buildDataType(String s) {
if (s.length() == 0) { if (s.length() == 0) {
return new StringType(); return new StringType();
} }
...@@ -27,9 +40,10 @@ public class TestTypeFactory implements DataTypeFactory { ...@@ -27,9 +40,10 @@ public class TestTypeFactory implements DataTypeFactory {
throw new RuntimeException("Unknown data type " + s); throw new RuntimeException("Unknown data type " + s);
} }
public DataType getDataType(Class<?> objectClass) { @Override
public String getDataType(Class<?> objectClass) {
if (objectClass == Integer.class) { if (objectClass == Integer.class) {
return new IntegerType(); return "i";
} }
throw new RuntimeException("Unsupported object class " + objectClass.toString()); throw new RuntimeException("Unsupported object class " + objectClass.toString());
} }
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
package org.h2.dev.store.btree; package org.h2.dev.store.btree;
import java.util.AbstractSet; import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
...@@ -19,6 +20,11 @@ import java.util.TreeMap; ...@@ -19,6 +20,11 @@ import java.util.TreeMap;
*/ */
public class BtreeMap<K, V> { public class BtreeMap<K, V> {
static final IllegalArgumentException KEY_NOT_FOUND = new IllegalArgumentException(
"Key not found");
static final IllegalArgumentException KEY_ALREADY_EXISTS = new IllegalArgumentException(
"Key already exists");
private final int id; private final int id;
private final String name; private final String name;
private final DataType keyType; private final DataType keyType;
...@@ -34,7 +40,8 @@ public class BtreeMap<K, V> { ...@@ -34,7 +40,8 @@ public class BtreeMap<K, V> {
private Page root; private Page root;
private boolean readOnly; private boolean readOnly;
BtreeMap(BtreeMapStore store, int id, String name, DataType keyType, DataType valueType, long createVersion) { protected BtreeMap(BtreeMapStore store, int id, String name,
DataType keyType, DataType valueType, long createVersion) {
this.store = store; this.store = store;
this.id = id; this.id = id;
this.name = name; this.name = name;
...@@ -53,13 +60,125 @@ public class BtreeMap<K, V> { ...@@ -53,13 +60,125 @@ public class BtreeMap<K, V> {
checkWrite(); checkWrite();
Page oldRoot = root; Page oldRoot = root;
if (containsKey(key)) { if (containsKey(key)) {
root = Page.set(this, root, store.getCurrentVersion(), key, data); root = set(root, store.getCurrentVersion(), key, data);
} else { } else {
root = Page.add(this, root, store.getCurrentVersion(), key, data); root = add(root, store.getCurrentVersion(), key, data);
} }
markChanged(oldRoot); markChanged(oldRoot);
} }
/**
* Update a value for an existing key.
*
* @param map the map
* @param p the page (may not be null)
* @param writeVersion the write version
* @param key the key
* @param value the value
* @return the root page
* @throws InvalidArgumentException if this key does not exist (without
* stack trace)
*/
private Page set(Page p, long writeVersion, Object key,
Object value) {
if (p == null) {
throw KEY_NOT_FOUND;
}
int index = p.binarySearch(key);
if (p.isLeaf()) {
if (index < 0) {
throw KEY_NOT_FOUND;
}
p = p.copyOnWrite(writeVersion);
p.setValue(index, value);
return p;
}
// it is a node
if (index < 0) {
index = -index - 1;
} else {
index++;
}
Page c = p.getChildPage(index);
Page c2 = set(c, writeVersion, key, value);
if (c != c2) {
p = p.copyOnWrite(writeVersion);
p.setChild(index, c2.getPos(), c2.getPos());
}
return p;
}
/**
* Add a new key-value pair.
*
* @param map the map
* @param p the page (may be null)
* @param writeVersion the write version
* @param key the key
* @param value the value
* @return the root page
* @throws InvalidArgumentException if this key already exists (without
* stack trace)
*/
private Page add(Page p, long writeVersion, Object key,
Object value) {
if (p == null) {
Object[] keys = { key };
Object[] values = { value };
p = Page.create(this, writeVersion, keys, values, null, null, 1);
return p;
}
if (p.getKeyCount() >= store.getMaxPageSize()) {
// only possible if this is the root,
// otherwise we would have split earlier
p = p.copyOnWrite(writeVersion);
int at = p.getKeyCount() / 2;
long totalSize = p.getTotalSize();
Object k = p.getKey(at);
Page split = p.split(at);
Object[] keys = { k };
long[] children = { p.getPos(), split.getPos() };
long[] childrenSize = { p.getTotalSize(), split.getTotalSize() };
p = Page.create(this, writeVersion, keys, null, children, childrenSize,
totalSize);
// now p is a node; insert continues
} else if (p.isLeaf()) {
int index = p.binarySearch(key);
if (index >= 0) {
throw KEY_ALREADY_EXISTS;
}
index = -index - 1;
p = p.copyOnWrite(writeVersion);
p.insert(index, key, value, 0, 0);
return p;
}
// p is a node
int index = p.binarySearch(key);
if (index < 0) {
index = -index - 1;
} else {
index++;
}
Page c = p.getChildPage(index);
if (c.getKeyCount() >= store.getMaxPageSize()) {
// split on the way down
c = c.copyOnWrite(writeVersion);
int at = c.getKeyCount() / 2;
Object k = c.getKey(at);
Page split = c.split(at);
p = p.copyOnWrite(writeVersion);
p.setChild(index, c.getPos(), c.getTotalSize());
p.insert(index, k, null, split.getPos(), split.getTotalSize());
// now we are not sure where to add
return add(p, writeVersion, key, value);
}
Page c2 = add(c, writeVersion, key, value);
p = p.copyOnWrite(writeVersion);
// the child might be the same, but not the size
p.setChild(index, c2.getPos(), c2.getTotalSize());
return p;
}
/** /**
* Get a value. * Get a value.
* *
...@@ -72,8 +191,102 @@ public class BtreeMap<K, V> { ...@@ -72,8 +191,102 @@ public class BtreeMap<K, V> {
if (root == null) { if (root == null) {
return null; return null;
} }
return (V) root.find(key); return (V) binarySearch(root, key);
}
/**
* Go to the first element for the given key.
*
* @param p the current page
* @param parents the stack of parent page positions
* @param key the key
*/
void min(Page p, ArrayList<CursorPos> parents, Object key) {
while (p != null) {
if (!p.isLeaf()) {
int x = key == null ? -1 : p.binarySearch(key);
if (x < 0) {
x = -x - 1;
} else {
x++;
}
CursorPos c = new CursorPos();
c.page = p;
c.index = x;
parents.add(c);
p = p.getChildPage(x);
} else {
int x = key == null ? 0 : p.binarySearch(key);
if (x < 0) {
x = -x - 1;
}
CursorPos c = new CursorPos();
c.page = p;
c.index = x;
parents.add(c);
return;
}
}
}
/**
* Get the next key.
*
* @param parents the stack of parent page positions
* @return the next key
*/
Object nextKey(ArrayList<CursorPos> parents) {
if (parents.size() == 0) {
return null;
}
while (true) {
// TODO performance: avoid remove/add pairs if possible
CursorPos p = parents.remove(parents.size() - 1);
int index = p.index++;
if (index < p.page.getKeyCount()) {
parents.add(p);
return p.page.getKey(index);
}
while (true) {
if (parents.size() == 0) {
return null;
}
p = parents.remove(parents.size() - 1);
index = ++p.index;
if (index <= p.page.getKeyCount()) {
parents.add(p);
Page x = p.page;
x = x.getChildPage(index);
min(x, parents, null);
break;
}
}
} }
}
/**
* Get the value for the given key, or null if not found.
*
* @param key the key
* @return the value or null
*/
private Object binarySearch(Page p, Object key) {
int x = p.binarySearch(key);
if (!p.isLeaf()) {
if (x < 0) {
x = -x - 1;
} else {
x++;
}
p = p.getChildPage(x);
return binarySearch(p, key);
}
if (x >= 0) {
return p.getValue(x);
}
return null;
}
public boolean containsKey(Object key) { public boolean containsKey(Object key) {
return get(key) != null; return get(key) != null;
...@@ -89,7 +302,31 @@ public class BtreeMap<K, V> { ...@@ -89,7 +302,31 @@ public class BtreeMap<K, V> {
if (root == null) { if (root == null) {
return null; return null;
} }
return root.findPage(key); return binarySearchPage(root, key);
}
/**
* Get the value for the given key, or null if not found.
*
* @param p the parent page
* @param key the key
* @return the page or null
*/
private Page binarySearchPage(Page p, Object key) {
int x = p.binarySearch(key);
if (!p.isLeaf()) {
if (x < 0) {
x = -x - 1;
} else {
x++;
}
p = p.getChildPage(x);
return binarySearchPage(p, key);
}
if (x >= 0) {
return p;
}
return null;
} }
/** /**
...@@ -137,11 +374,61 @@ public class BtreeMap<K, V> { ...@@ -137,11 +374,61 @@ public class BtreeMap<K, V> {
checkWrite(); checkWrite();
if (containsKey(key)) { if (containsKey(key)) {
Page oldRoot = root; Page oldRoot = root;
root = Page.removeExisting(root, store.getCurrentVersion(), key); root = removeExisting(root, store.getCurrentVersion(), key);
markChanged(oldRoot); markChanged(oldRoot);
} }
} }
/**
* Remove an existing key-value pair.
*
* @param p the page (may not be null)
* @param writeVersion the write version
* @param key the key
* @return the new root page (null if empty)
* @throws InvalidArgumentException if not found (without stack trace)
*/
private Page removeExisting(Page p, long writeVersion, Object key) {
if (p == null) {
throw KEY_NOT_FOUND;
}
int index = p.binarySearch(key);
if (p.isLeaf()) {
if (index >= 0) {
if (p.getKeyCount() == 1) {
store.removePage(p.getPos());
return null;
}
p = p.copyOnWrite(writeVersion);
p.remove(index);
} else {
throw KEY_NOT_FOUND;
}
return p;
}
// node
if (index < 0) {
index = -index - 1;
} else {
index++;
}
Page c = p.getChildPage(index);
Page c2 = removeExisting(c, writeVersion, key);
p = p.copyOnWrite(writeVersion);
if (c2 == null) {
// this child was deleted
p.remove(index);
if (p.getKeyCount() == 0) {
store.removePage(p.getPos());
p = p.getChildPage(0);
}
} else {
p.setChild(index, c2.getPos(), c2.getTotalSize());
}
return p;
}
private void markChanged(Page oldRoot) { private void markChanged(Page oldRoot) {
if (oldRoot != root) { if (oldRoot != root) {
long v = store.getCurrentVersion(); long v = store.getCurrentVersion();
...@@ -212,16 +499,17 @@ public class BtreeMap<K, V> { ...@@ -212,16 +499,17 @@ public class BtreeMap<K, V> {
*/ */
public Iterator<K> keyIterator(K from) { public Iterator<K> keyIterator(K from) {
checkOpen(); checkOpen();
return new Cursor<K>(root, from); return new Cursor<K, V>(this, root, from);
} }
public Set<K> keySet() { public Set<K> keySet() {
checkOpen(); checkOpen();
final Page root = this.root;
return new AbstractSet<K>() { return new AbstractSet<K>() {
@Override @Override
public Iterator<K> iterator() { public Iterator<K> iterator() {
return new Cursor<K>(getRoot(), null); return new Cursor<K, V>(BtreeMap.this, root, null);
} }
@Override @Override
...@@ -285,7 +573,7 @@ public class BtreeMap<K, V> { ...@@ -285,7 +573,7 @@ public class BtreeMap<K, V> {
} }
} }
void stored() { void revertTemp() {
oldRoots.clear(); oldRoots.clear();
} }
...@@ -339,8 +627,13 @@ public class BtreeMap<K, V> { ...@@ -339,8 +627,13 @@ public class BtreeMap<K, V> {
return this == o; return this == o;
} }
long getCreatedVersion() { long getCreateVersion() {
return createVersion; return createVersion;
} }
@SuppressWarnings("unchecked")
public <M> M cast() {
return (M) this;
}
} }
...@@ -38,7 +38,7 @@ header: ...@@ -38,7 +38,7 @@ header:
blockSize=4096 blockSize=4096
TODO: TODO:
- support custom map types (page types); pager for r-tree, kd-tree - support custom map types: b-tree, r-tree
- ability to diff / merge versions - ability to diff / merge versions
- map.getVersion and opening old maps read-only - map.getVersion and opening old maps read-only
- limited support for writing to old versions (branches) - limited support for writing to old versions (branches)
...@@ -55,6 +55,7 @@ TODO: ...@@ -55,6 +55,7 @@ TODO:
- check what happens on concurrent reads and 1 write; multiple writes - check what happens on concurrent reads and 1 write; multiple writes
- support large binaries - support large binaries
- support stores that span multiple files (chunks stored in other files) - support stores that span multiple files (chunks stored in other files)
- triggers
*/ */
...@@ -68,7 +69,7 @@ public class BtreeMapStore { ...@@ -68,7 +69,7 @@ public class BtreeMapStore {
private static final StringType STRING_TYPE = new StringType(); private static final StringType STRING_TYPE = new StringType();
private final String fileName; private final String fileName;
private final DataTypeFactory typeFactory; private final MapFactory mapFactory;
private int readCacheSize = 2 * 1024 * 1024; private int readCacheSize = 2 * 1024 * 1024;
...@@ -110,9 +111,9 @@ public class BtreeMapStore { ...@@ -110,9 +111,9 @@ public class BtreeMapStore {
private int readCount; private int readCount;
private int writeCount; private int writeCount;
private BtreeMapStore(String fileName, DataTypeFactory typeFactory) { private BtreeMapStore(String fileName, MapFactory mapFactory) {
this.fileName = fileName; this.fileName = fileName;
this.typeFactory = typeFactory; this.mapFactory = mapFactory;
} }
/** /**
...@@ -129,11 +130,11 @@ public class BtreeMapStore { ...@@ -129,11 +130,11 @@ public class BtreeMapStore {
* Open a tree store. * Open a tree store.
* *
* @param fileName the file name * @param fileName the file name
* @param typeFactory the type factory * @param mapFactory the map factory
* @return the store * @return the store
*/ */
public static BtreeMapStore open(String fileName, DataTypeFactory typeFactory) { public static BtreeMapStore open(String fileName, MapFactory mapFactory) {
BtreeMapStore s = new BtreeMapStore(fileName, typeFactory); BtreeMapStore s = new BtreeMapStore(fileName, mapFactory);
s.open(); s.open();
return s; return s;
} }
...@@ -144,12 +145,13 @@ public class BtreeMapStore { ...@@ -144,12 +145,13 @@ public class BtreeMapStore {
* @param <K> the key type * @param <K> the key type
* @param <V> the value type * @param <V> the value type
* @param name the name of the map * @param name the name of the map
* @param mapType the map type
* @param keyType the key type * @param keyType the key type
* @param valueType the value type * @param valueType the value type
* @return the map * @return the map
*/ */
public <K, V> BtreeMap<K, V> openMap(String name, DataType keyType, DataType valueType) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <K, V> BtreeMap<K, V> openMap(String name, String mapType, String keyType, String valueType) {
BtreeMap<K, V> m = (BtreeMap<K, V>) maps.get(name); BtreeMap<K, V> m = (BtreeMap<K, V>) maps.get(name);
if (m == null) { if (m == null) {
String identifier = meta.get("map." + name); String identifier = meta.get("map." + name);
...@@ -158,21 +160,28 @@ public class BtreeMapStore { ...@@ -158,21 +160,28 @@ public class BtreeMapStore {
long createVersion; long createVersion;
if (identifier == null) { if (identifier == null) {
id = ++lastMapId; id = ++lastMapId;
String types = id + "/" + currentVersion + "/" + keyType.asString() + "/" + valueType.asString(); createVersion = currentVersion;
String types = id + "/" + createVersion + "/" + mapType + "/" + keyType + "/" + valueType;
meta.put("map." + name, types); meta.put("map." + name, types);
root = 0; root = 0;
createVersion = currentVersion;
} else { } else {
String types = meta.get("map." + name); String types = meta.get("map." + name);
String[] idTypeList = StringUtils.arraySplit(types, '/', false); String[] idTypeList = StringUtils.arraySplit(types, '/', false);
id = Integer.parseInt(idTypeList[0]); id = Integer.parseInt(idTypeList[0]);
createVersion = Long.parseLong(idTypeList[1]); createVersion = Long.parseLong(idTypeList[1]);
keyType = getDataType(idTypeList[2]); mapType = idTypeList[2];
valueType = getDataType(idTypeList[3]); keyType = idTypeList[3];
valueType = idTypeList[4];
String r = meta.get("root." + id); String r = meta.get("root." + id);
root = r == null ? 0 : Long.parseLong(r); root = r == null ? 0 : Long.parseLong(r);
} }
m = new BtreeMap<K, V>(this, id, name, keyType, valueType, createVersion); DataType k = buildDataType(keyType);
DataType v = buildDataType(valueType);
if (mapType.equals("")) {
m = new BtreeMap<K, V>(this, id, name, k, v, createVersion);
} else {
m = getMapFactory().buildMap(mapType, this, id, name, k, v, createVersion);
}
maps.put(name, m); maps.put(name, m);
m.setRootPos(root); m.setRootPos(root);
} }
...@@ -223,9 +232,11 @@ public class BtreeMapStore { ...@@ -223,9 +232,11 @@ public class BtreeMapStore {
* @return the map * @return the map
*/ */
public <K, V> BtreeMap<K, V> openMap(String name, Class<K> keyClass, Class<V> valueClass) { public <K, V> BtreeMap<K, V> openMap(String name, Class<K> keyClass, Class<V> valueClass) {
DataType keyType = getDataType(keyClass); String keyType = getDataType(keyClass);
DataType valueType = getDataType(valueClass); String valueType = getDataType(valueClass);
return openMap(name, keyType, valueType); @SuppressWarnings("unchecked")
BtreeMap<K, V> m = (BtreeMap<K, V>) openMap(name, "", keyType, valueType);
return m;
} }
void removeMap(String name) { void removeMap(String name) {
...@@ -233,25 +244,25 @@ public class BtreeMapStore { ...@@ -233,25 +244,25 @@ public class BtreeMapStore {
mapsChanged.remove(m); mapsChanged.remove(m);
} }
private DataType getDataType(Class<?> clazz) { private String getDataType(Class<?> clazz) {
if (clazz == String.class) { if (clazz == String.class) {
return STRING_TYPE; return "";
} }
return getTypeFactory().getDataType(clazz); return getMapFactory().getDataType(clazz);
} }
private DataType getDataType(String s) { private DataType buildDataType(String dataType) {
if (s.equals("")) { if (dataType.equals("")) {
return STRING_TYPE; return STRING_TYPE;
} }
return getTypeFactory().fromString(s); return getMapFactory().buildDataType(dataType);
} }
private DataTypeFactory getTypeFactory() { private MapFactory getMapFactory() {
if (typeFactory == null) { if (mapFactory == null) {
throw new RuntimeException("No data type factory set"); throw new RuntimeException("No factory set");
} }
return typeFactory; return mapFactory;
} }
/** /**
...@@ -657,6 +668,7 @@ public class BtreeMapStore { ...@@ -657,6 +668,7 @@ public class BtreeMapStore {
if (percentTotal > fillRate) { if (percentTotal > fillRate) {
return false; return false;
} }
// calculate how many entries a chunk has on average // calculate how many entries a chunk has on average
// TODO use the max size instead of the count // TODO use the max size instead of the count
int averageEntryCount = (int) (entryCountTotal / chunks.size()); int averageEntryCount = (int) (entryCountTotal / chunks.size());
...@@ -664,20 +676,27 @@ public class BtreeMapStore { ...@@ -664,20 +676,27 @@ public class BtreeMapStore {
// the 'old' list contains the chunks we want to free up // the 'old' list contains the chunks we want to free up
ArrayList<Chunk> old = New.arrayList(); ArrayList<Chunk> old = New.arrayList();
for (Chunk c : chunks.values()) { for (Chunk c : chunks.values()) {
if (retainChunk == -1 || c.id < retainChunk) {
int age = lastChunkId - c.id + 1; int age = lastChunkId - c.id + 1;
c.collectPriority = c.getFillRate() / age; c.collectPriority = c.getFillRate() / age;
old.add(c); old.add(c);
} }
}
if (old.size() == 0) {
return false;
}
// sort the list, so the first entry should be collected first // sort the list, so the first entry should be collected first
Collections.sort(old, new Comparator<Chunk>() { Collections.sort(old, new Comparator<Chunk>() {
public int compare(Chunk o1, Chunk o2) { public int compare(Chunk o1, Chunk o2) {
return new Integer(o1.collectPriority).compareTo(o2.collectPriority); return new Integer(o1.collectPriority).compareTo(o2.collectPriority);
} }
}); });
int moveCount = 0;
Chunk move = null;
// find out up to were we need to move // find out up to were we need to move
// try to move one (average sized) chunk // try to move one (average sized) chunk
int moveCount = 0;
Chunk move = null;
for (Chunk c : old) { for (Chunk c : old) {
if (moveCount + c.liveCount > averageEntryCount) { if (moveCount + c.liveCount > averageEntryCount) {
break; break;
...@@ -711,39 +730,39 @@ public class BtreeMapStore { ...@@ -711,39 +730,39 @@ public class BtreeMapStore {
} }
} }
} }
// the metaRootPos might not be set // the metaRootPos might not be set
move = readChunkHeader(move.start); move = readChunkHeader(move.start);
log(" meta:" + move.id + "/" + move.metaRootPos + " start: " + move.start); log(" meta:" + move.id + "/" + move.metaRootPos + " start: " + move.start);
// change at least one entry in the map // change at least one entry in the map
// to ensure a chunk will be written // to ensure a chunk will be written
// (even if there is nothing to move) // (even if there is nothing to move)
meta.put("chunk." + move.id, move.toString()); meta.put("chunk." + move.id, move.toString());
BtreeMap<String, String> oldMeta = new BtreeMap<String, String>(this, 0, "old-meta", STRING_TYPE, STRING_TYPE, 0); BtreeMap<String, String> oldMeta = new BtreeMap<String, String>(this, 0, "old-meta", STRING_TYPE, STRING_TYPE, 0);
oldMeta.setRootPos(move.metaRootPos); oldMeta.setRootPos(move.metaRootPos);
Iterator<String> it = oldMeta.keyIterator(null); Iterator<String> it = oldMeta.keyIterator("map.");
while (it.hasNext()) { while (it.hasNext()) {
String k = it.next(); String k = it.next();
String s = oldMeta.get(k);
log(" " + k + " " + s.replace('\n', ' '));
if (!k.startsWith("map.")) { if (!k.startsWith("map.")) {
continue; break;
} }
String s = oldMeta.get(k);
k = k.substring("map.".length()); k = k.substring("map.".length());
if (!maps.containsKey(k)) { @SuppressWarnings("unchecked")
BtreeMap<Object, Object> data = (BtreeMap<Object, Object>) maps.get(k);
if (data == null) {
continue; continue;
} }
String[] idTypesList = StringUtils.arraySplit(s, '/', false); log(" " + k + " " + s.replace('\n', ' '));
int id = Integer.parseInt(idTypesList[0]); String[] idTypeList = StringUtils.arraySplit(s, '/', false);
DataType kt = getDataType(idTypesList[2]); int id = Integer.parseInt(idTypeList[0]);
DataType vt = getDataType(idTypesList[3]); DataType kt = buildDataType(idTypeList[3]);
DataType vt = buildDataType(idTypeList[4]);
long oldDataRoot = Long.parseLong(oldMeta.get("root." + id)); long oldDataRoot = Long.parseLong(oldMeta.get("root." + id));
if (oldDataRoot != 0) {
BtreeMap<?, ?> oldData = new BtreeMap<Object, Object>(this, id, "old-" + k, kt, vt, 0); BtreeMap<?, ?> oldData = new BtreeMap<Object, Object>(this, id, "old-" + k, kt, vt, 0);
if (oldDataRoot == 0) {
// no rows
} else {
oldData.setRootPos(oldDataRoot); oldData.setRootPos(oldDataRoot);
@SuppressWarnings("unchecked")
BtreeMap<Object, Object> data = (BtreeMap<Object, Object>) maps.get(k);
Iterator<?> dataIt = oldData.keyIterator(null); Iterator<?> dataIt = oldData.keyIterator(null);
while (dataIt.hasNext()) { while (dataIt.hasNext()) {
Object o = dataIt.next(); Object o = dataIt.next();
...@@ -952,7 +971,7 @@ public class BtreeMapStore { ...@@ -952,7 +971,7 @@ public class BtreeMapStore {
} }
} }
for (BtreeMap<?, ?> m : maps.values()) { for (BtreeMap<?, ?> m : maps.values()) {
if (m.getCreatedVersion() > version) { if (m.getCreateVersion() > version) {
m.close(); m.close();
removeMap(m.getName()); removeMap(m.getName());
} else { } else {
...@@ -969,7 +988,7 @@ public class BtreeMapStore { ...@@ -969,7 +988,7 @@ public class BtreeMapStore {
private void revertTemp() { private void revertTemp() {
freedChunks.clear(); freedChunks.clear();
for (BtreeMap<?, ?> m : mapsChanged.values()) { for (BtreeMap<?, ?> m : mapsChanged.values()) {
m.stored(); m.revertTemp();
} }
mapsChanged.clear(); mapsChanged.clear();
temp.clear(); temp.clear();
......
...@@ -13,14 +13,17 @@ import java.util.Iterator; ...@@ -13,14 +13,17 @@ 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 * @param <K> the key type
* @param <V> the value type
*/ */
class Cursor<K> implements Iterator<K> { class Cursor<K, V> implements Iterator<K> {
private ArrayList<CursorPos> parents = new ArrayList<CursorPos>(); private final BtreeMap<K, V> map;
private final ArrayList<CursorPos> parents = new ArrayList<CursorPos>();
private K current; private K current;
Cursor(Page root, K from) { Cursor(BtreeMap<K, V> map, Page root, K from) {
Page.min(root, parents, from); this.map = map;
map.min(root, parents, from);
fetchNext(); fetchNext();
} }
...@@ -34,7 +37,7 @@ class Cursor<K> implements Iterator<K> { ...@@ -34,7 +37,7 @@ class Cursor<K> implements Iterator<K> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private void fetchNext() { private void fetchNext() {
current = (K) Page.nextKey(parents); current = (K) map.nextKey(parents);
} }
public boolean hasNext() { public boolean hasNext() {
......
...@@ -64,9 +64,10 @@ public interface DataType { ...@@ -64,9 +64,10 @@ public interface DataType {
Object read(ByteBuffer buff); Object read(ByteBuffer buff);
/** /**
* Get the string representation of this type. * Get the stable string representation that is used to build this data
* type.
* *
* @return the string * @return the string representation
*/ */
String asString(); String asString();
......
...@@ -9,15 +9,31 @@ package org.h2.dev.store.btree; ...@@ -9,15 +9,31 @@ package org.h2.dev.store.btree;
/** /**
* A factory for data types. * A factory for data types.
*/ */
public interface DataTypeFactory { public interface MapFactory {
/** /**
* Read the data type. * Build a map.
* *
* @param s the string * @param mapType the map type and type specific meta data
* @param store the store
* @param id the unique map id
* @param name the map name
* @param keyType the key type
* @param valueType the value type
* @param createVersion when the map was created
* @return the map
*/
<K, V> BtreeMap<K, V> buildMap(
String mapType, BtreeMapStore store, int id, String name,
DataType keyType, DataType valueType, long createVersion);
/**
* Parse the data type.
*
* @param dataType the string and type specific meta data
* @return the type * @return the type
*/ */
DataType fromString(String s); DataType buildDataType(String dataType);
/** /**
* Get the data type object for the given class. * Get the data type object for the given class.
...@@ -25,6 +41,6 @@ public interface DataTypeFactory { ...@@ -25,6 +41,6 @@ public interface DataTypeFactory {
* @param objectClass the class * @param objectClass the class
* @return the data type object * @return the data type object
*/ */
DataType getDataType(Class<?> objectClass); String getDataType(Class<?> objectClass);
} }
...@@ -9,13 +9,12 @@ package org.h2.dev.store.btree; ...@@ -9,13 +9,12 @@ package org.h2.dev.store.btree;
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 org.h2.compress.Compressor; import org.h2.compress.Compressor;
/** /**
* A btree page (a node or a leaf). * A page (a node or a leaf).
* <p> * <p>
* For nodes, the key at a given index is larger than the largest key of the * For b-tree nodes, the key at a given index is larger than the largest key of the
* child at the same index. * child at the same index.
* <p> * <p>
* File format: page length (including length): int check value: short map id: * File format: page length (including length): int check value: short map id:
...@@ -25,11 +24,6 @@ import org.h2.compress.Compressor; ...@@ -25,11 +24,6 @@ import org.h2.compress.Compressor;
*/ */
public class Page { public class Page {
private static final IllegalArgumentException KEY_NOT_FOUND = new IllegalArgumentException(
"Key not found");
private static final IllegalArgumentException KEY_ALREADY_EXISTS = new IllegalArgumentException(
"Key already exists");
private final BtreeMap<?, ?> map; private final BtreeMap<?, ?> map;
private final long version; private final long version;
private long pos; private long pos;
...@@ -101,15 +95,33 @@ public class Page { ...@@ -101,15 +95,33 @@ public class Page {
return p; return p;
} }
private Page copyOnWrite(long writeVersion) { Object getKey(int index) {
if (version == writeVersion) { return keys[index];
return this;
} }
getStore().removePage(pos);
Page newPage = create(map, writeVersion, keys, values, children, Page getChildPage(int index) {
childrenSize, totalSize); return map.readPage(children[index]);
newPage.cachedCompare = cachedCompare; }
return newPage;
Object getValue(int x) {
return values[x];
}
int getKeyCount() {
return keys.length;
}
boolean isLeaf() {
return children == null;
}
/**
* Get the position of the page
*
* @return the position
*/
long getPos() {
return pos;
} }
public String toString() { public String toString() {
...@@ -133,62 +145,28 @@ public class Page { ...@@ -133,62 +145,28 @@ public class Page {
return buff.toString(); return buff.toString();
} }
/** Page copyOnWrite(long writeVersion) {
* Get the position of the page if (version == writeVersion) {
* return this;
* @return the position
*/
long getPos() {
return pos;
}
/**
* Get the value for the given key, or null if not found.
*
* @param key the key
* @return the value or null
*/
Object find(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.find(key);
}
if (x >= 0) {
return values[x];
} }
return null; map.getStore().removePage(pos);
Page newPage = create(map, writeVersion, keys, values, children,
childrenSize, totalSize);
newPage.cachedCompare = cachedCompare;
return newPage;
} }
/** /**
* Get the value for the given key, or null if not found. * Search the key in this page using a binary search. Instead of always
* starting the search in the middle, the last found index is cached. If the
* key was found, the returned value is the index in the key array. If not
* found, the returned value is negative, where -1 means the provided key is
* smaller than any keys in this page. See also Arrays.binarySearch.
* *
* @param key the key * @param key the key
* @return the page or null * @return the value or null
*/ */
Page findPage(Object key) { int binarySearch(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; int low = 0, high = keys.length - 1;
int x = cachedCompare - 1; int x = cachedCompare - 1;
if (x < 0 || x > high) { if (x < 0 || x > high) {
...@@ -225,85 +203,7 @@ public class Page { ...@@ -225,85 +203,7 @@ public class Page {
// return -(low + 1); // return -(low + 1);
} }
/** Page split(int at) {
* Go to the first element for the given key.
*
* @param p the current page
* @param parents the stack of parent page positions
* @param key the key
*/
static void min(Page p, ArrayList<CursorPos> parents, Object key) {
while (p != null) {
if (p.children != null) {
int x = key == null ? -1 : p.findKey(key);
if (x < 0) {
x = -x - 1;
} else {
x++;
}
CursorPos c = new CursorPos();
c.page = p;
c.index = x;
parents.add(c);
p = p.map.readPage(p.children[x]);
} else {
int x = key == null ? 0 : p.findKey(key);
if (x < 0) {
x = -x - 1;
}
CursorPos c = new CursorPos();
c.page = p;
c.index = x;
parents.add(c);
return;
}
}
}
/**
* Get the next key.
*
* @param parents the stack of parent page positions
* @return the next key
*/
static Object nextKey(ArrayList<CursorPos> parents) {
if (parents.size() == 0) {
return null;
}
while (true) {
// TODO performance: avoid remove/add pairs if 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);
Page x = p.page;
x = x.map.readPage(x.children[index]);
min(x, parents, null);
break;
}
}
}
}
private int keyCount() {
return keys.length;
}
private boolean isLeaf() {
return children == null;
}
private Page split(int at) {
return isLeaf() ? splitLeaf(at) : splitNode(at); return isLeaf() ? splitLeaf(at) : splitNode(at);
} }
...@@ -357,118 +257,6 @@ public class Page { ...@@ -357,118 +257,6 @@ public class Page {
return newPage; return newPage;
} }
/**
* Update a value for an existing key.
*
* @param map the map
* @param p the page (may not be null)
* @param writeVersion the write version
* @param key the key
* @param value the value
* @return the root page
* @throws InvalidArgumentException if this key does not exist (without
* stack trace)
*/
static Page set(BtreeMap<?, ?> map, Page p, long writeVersion, Object key,
Object value) {
if (p == null) {
throw KEY_NOT_FOUND;
}
int index = p.findKey(key);
if (p.isLeaf()) {
if (index < 0) {
throw KEY_NOT_FOUND;
}
p = p.copyOnWrite(writeVersion);
p.setValue(index, value);
return p;
}
// it is a node
if (index < 0) {
index = -index - 1;
} else {
index++;
}
Page c = map.readPage(p.children[index]);
Page c2 = set(map, c, writeVersion, key, value);
if (c != c2) {
p = p.copyOnWrite(writeVersion);
p.setChild(index, c2.getPos(), c2.getPos());
}
return p;
}
/**
* Add a new key-value pair.
*
* @param map the map
* @param p the page (may be null)
* @param writeVersion the write version
* @param key the key
* @param value the value
* @return the root page
* @throws InvalidArgumentException if this key already exists (without
* stack trace)
*/
static Page add(BtreeMap<?, ?> map, Page p, long writeVersion, Object key,
Object value) {
if (p == null) {
Object[] keys = { key };
Object[] values = { value };
p = create(map, writeVersion, keys, values, null, null, 1);
return p;
}
if (p.keyCount() >= map.getStore().getMaxPageSize()) {
// only possible if this is the root,
// otherwise we would have split earlier
p = p.copyOnWrite(writeVersion);
int at = p.keyCount() / 2;
long totalSize = p.getTotalSize();
Object k = p.keys[at];
Page split = p.split(at);
Object[] keys = { k };
long[] children = { p.getPos(), split.getPos() };
long[] childrenSize = { p.getTotalSize(), split.getTotalSize() };
p = create(map, writeVersion, keys, null, children, childrenSize,
totalSize);
// now p is a node; insert continues
} else if (p.isLeaf()) {
int index = p.findKey(key);
if (index >= 0) {
throw KEY_ALREADY_EXISTS;
}
index = -index - 1;
p = p.copyOnWrite(writeVersion);
p.insert(index, key, value, 0, 0);
return p;
}
// p is a node
int index = p.findKey(key);
if (index < 0) {
index = -index - 1;
} else {
index++;
}
Page c = map.readPage(p.children[index]);
if (c.keyCount() >= map.getStore().getMaxPageSize()) {
// split on the way down
c = c.copyOnWrite(writeVersion);
int at = c.keyCount() / 2;
Object k = c.keys[at];
Page split = c.split(at);
p = p.copyOnWrite(writeVersion);
p.setChild(index, c.getPos(), c.getTotalSize());
p.insert(index, k, null, split.getPos(), split.getTotalSize());
// now we are not sure where to add
return add(map, p, writeVersion, key, value);
}
Page c2 = add(map, c, writeVersion, key, value);
p = p.copyOnWrite(writeVersion);
// the child might be the same, but not the size
p.setChild(index, c2.getPos(), c2.getTotalSize());
return p;
}
long getTotalSize() { long getTotalSize() {
if (BtreeMapStore.ASSERT) { if (BtreeMapStore.ASSERT) {
long check = 0; long check = 0;
...@@ -487,7 +275,7 @@ public class Page { ...@@ -487,7 +275,7 @@ public class Page {
return totalSize; return totalSize;
} }
private void setChild(int index, long pos, long childSize) { void setChild(int index, long pos, long childSize) {
if (pos != children[index]) { if (pos != children[index]) {
long[] newChildren = new long[children.length]; long[] newChildren = new long[children.length];
System.arraycopy(children, 0, newChildren, 0, newChildren.length); System.arraycopy(children, 0, newChildren, 0, newChildren.length);
...@@ -504,7 +292,7 @@ public class Page { ...@@ -504,7 +292,7 @@ public class Page {
} }
} }
private void setValue(int index, Object value) { void setValue(int index, Object value) {
// create a copy - not required if already cloned once in this version, // create a copy - not required if already cloned once in this version,
// but avoid unnecessary cloning would require a "modified" flag // but avoid unnecessary cloning would require a "modified" flag
Object[] newValues = new Object[values.length]; Object[] newValues = new Object[values.length];
...@@ -521,65 +309,16 @@ public class Page { ...@@ -521,65 +309,16 @@ public class Page {
for (long c : children) { for (long c : children) {
int type = DataUtils.getPageType(c); int type = DataUtils.getPageType(c);
if (type == DataUtils.PAGE_TYPE_LEAF) { if (type == DataUtils.PAGE_TYPE_LEAF) {
getStore().removePage(c); map.getStore().removePage(c);
} else { } else {
map.readPage(c).removeAllRecursive(); map.readPage(c).removeAllRecursive();
} }
} }
} }
getStore().removePage(pos); map.getStore().removePage(pos);
}
/**
* Remove an existing key-value pair.
*
* @param p the page (may not be null)
* @param writeVersion the write version
* @param key the key
* @return the new root page (null if empty)
* @throws InvalidArgumentException if not found (without stack trace)
*/
static Page removeExisting(Page p, long writeVersion, Object key) {
if (p == null) {
throw KEY_NOT_FOUND;
}
int index = p.findKey(key);
if (p.isLeaf()) {
if (index >= 0) {
if (p.keyCount() == 1) {
p.getStore().removePage(p.pos);
return null;
}
p = p.copyOnWrite(writeVersion);
p.remove(index);
} else {
throw KEY_NOT_FOUND;
}
return p;
}
// node
if (index < 0) {
index = -index - 1;
} else {
index++;
}
Page c = p.map.readPage(p.children[index]);
Page c2 = removeExisting(c, writeVersion, key);
p = p.copyOnWrite(writeVersion);
if (c2 == null) {
// this child was deleted
p.remove(index);
if (p.keyCount() == 0) {
p.getStore().removePage(p.pos);
p = p.map.readPage(p.children[0]);
}
} else {
p.setChild(index, c2.getPos(), c2.getTotalSize());
}
return p;
} }
private void insert(int index, Object key, Object value, long child, void insert(int index, Object key, Object value, long child,
long childSize) { long childSize) {
Object[] newKeys = new Object[keys.length + 1]; Object[] newKeys = new Object[keys.length + 1];
DataUtils.copyWithGap(keys, newKeys, keys.length, index); DataUtils.copyWithGap(keys, newKeys, keys.length, index);
...@@ -607,7 +346,7 @@ public class Page { ...@@ -607,7 +346,7 @@ public class Page {
} }
} }
private void remove(int index) { void remove(int index) {
Object[] newKeys = new Object[keys.length - 1]; Object[] newKeys = new Object[keys.length - 1];
int keyIndex = index >= keys.length ? index - 1 : index; int keyIndex = index >= keys.length ? index - 1 : index;
DataUtils.copyExcept(keys, newKeys, keys.length, keyIndex); DataUtils.copyExcept(keys, newKeys, keys.length, keyIndex);
...@@ -644,16 +383,14 @@ public class Page { ...@@ -644,16 +383,14 @@ public class Page {
throw new RuntimeException("Error reading page, expected map " throw new RuntimeException("Error reading page, expected map "
+ map.getId() + " got " + mapId); + map.getId() + " got " + mapId);
} }
int len = DataUtils.readVarInt(buff);
int checkTest = DataUtils.getCheckValue(chunkId) int checkTest = DataUtils.getCheckValue(chunkId)
^ DataUtils.getCheckValue(map.getId())
^ DataUtils.getCheckValue(offset) ^ DataUtils.getCheckValue(offset)
^ DataUtils.getCheckValue(pageLength) ^ DataUtils.getCheckValue(pageLength);
^ DataUtils.getCheckValue(len);
if (check != (short) checkTest) { if (check != (short) checkTest) {
throw new RuntimeException("Error in check value, expected " throw new RuntimeException("Error in check value, expected "
+ checkTest + " got " + check); + checkTest + " got " + check);
} }
int len = DataUtils.readVarInt(buff);
keys = new Object[len]; keys = new Object[len];
int type = buff.get(); int type = buff.get();
boolean node = (type & 1) == DataUtils.PAGE_TYPE_NODE; boolean node = (type & 1) == DataUtils.PAGE_TYPE_NODE;
...@@ -743,10 +480,8 @@ public class Page { ...@@ -743,10 +480,8 @@ public class Page {
int pageLength = buff.position() - start; int pageLength = buff.position() - start;
buff.putInt(start, pageLength); buff.putInt(start, pageLength);
int check = DataUtils.getCheckValue(chunkId) int check = DataUtils.getCheckValue(chunkId)
^ DataUtils.getCheckValue(map.getId())
^ DataUtils.getCheckValue(start) ^ DataUtils.getCheckValue(start)
^ DataUtils.getCheckValue(pageLength) ^ DataUtils.getCheckValue(pageLength);
^ DataUtils.getCheckValue(len);
buff.putShort(start + 4, (short) check); buff.putShort(start + 4, (short) check);
this.pos = DataUtils.getPagePos(chunkId, start, pageLength, type); this.pos = DataUtils.getPagePos(chunkId, start, pageLength, type);
} }
...@@ -823,12 +558,4 @@ public class Page { ...@@ -823,12 +558,4 @@ public class Page {
return count; return count;
} }
BtreeMapStore getStore() {
return map.getStore();
}
long getVersion() {
return version;
}
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论