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

A persistent multi-version map (work in progress): r-tree

上级 b1edabaa
...@@ -19,10 +19,6 @@ class IntegerType implements DataType { ...@@ -19,10 +19,6 @@ class IntegerType implements DataType {
return ((Integer) a).compareTo((Integer) b); return ((Integer) a).compareTo((Integer) b);
} }
public int length(Object obj) {
return DataUtils.getVarIntLen((Integer) obj);
}
public int getMaxLength(Object obj) { public int getMaxLength(Object obj) {
return DataUtils.MAX_VAR_INT_LEN; return DataUtils.MAX_VAR_INT_LEN;
} }
......
...@@ -46,16 +46,6 @@ public class RowType implements DataType { ...@@ -46,16 +46,6 @@ public class RowType implements DataType {
return 0; 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) { public int getMaxLength(Object obj) {
Object[] x = (Object[]) obj; Object[] x = (Object[]) obj;
int len = x.length; int len = x.length;
......
/*
* 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;
/**
* A unique spatial key.
*/
public class SpatialKey {
public float[] min;
public float[] max;
public long id;
public static SpatialKey create(long id, float... minMax) {
SpatialKey k = new SpatialKey();
k.id = id;
int dimensions = minMax.length / 2;
k.min = new float[dimensions];
k.max = new float[dimensions];
for (int i = 0; i < dimensions; i++) {
k.min[i] = minMax[i + i];
k.max[i] = minMax[i + i + 1];
}
return k;
}
public String toString() {
StringBuilder buff = new StringBuilder();
buff.append(id).append(": (");
for (int i = 0; i < min.length; i++) {
if (i > 0) {
buff.append(", ");
}
buff.append(min[i]).append('/').append(max[i]);
}
return buff.append(")").toString();
}
}
/*
* 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.DataUtils;
/**
* A spatial data type. This class supports up to 255 dimensions. Each dimension
* can have a minimum and a maximum value of type float. For each dimension, the
* maximum value is only stored when it is not the same as the minimum.
*/
public class SpatialType implements DataType {
private final int dimensions;
private SpatialType(int dimensions) {
if (dimensions <= 0 || dimensions > 255) {
throw new IllegalArgumentException("Dimensions: " + dimensions);
}
this.dimensions = dimensions;
}
public static SpatialType fromString(String s) {
return new SpatialType(Integer.parseInt(s.substring(1)));
}
@Override
public int compare(Object a, Object b) {
long la = ((SpatialKey) a).id;
long lb = ((SpatialKey) b).id;
return la < lb ? -1 : la > lb ? 1 : 0;
}
public boolean equals(Object a, Object b) {
long la = ((SpatialKey) a).id;
long lb = ((SpatialKey) b).id;
return la == lb;
}
@Override
public int getMaxLength(Object obj) {
return 1 + dimensions * 8 + DataUtils.MAX_VAR_LONG_LEN;
}
@Override
public int getMemory(Object obj) {
return 40 + dimensions * 4;
}
@Override
public void write(ByteBuffer buff, Object obj) {
SpatialKey k = (SpatialKey) obj;
int flags = 0;
for (int i = 0; i < dimensions; i++) {
if (k.min[i] == k.max[i]) {
flags |= 1 << i;
}
}
DataUtils.writeVarInt(buff, flags);
for (int i = 0; i < dimensions; i++) {
buff.putFloat(k.min[i]);
if ((flags & (1 << i)) == 0) {
buff.putFloat(k.max[i]);
}
}
DataUtils.writeVarLong(buff, k.id);
}
@Override
public Object read(ByteBuffer buff) {
SpatialKey k = new SpatialKey();
int flags = DataUtils.readVarInt(buff);
k.min = new float[dimensions];
k.max = new float[dimensions];
for (int i = 0; i < dimensions; i++) {
k.min[i] = buff.getFloat();
if ((flags & (1 << i)) != 0) {
k.max[i] = k.min[i];
} else {
k.max[i] = buff.getFloat();
}
}
k.id = DataUtils.readVarLong(buff);
return k;
}
@Override
public String asString() {
return "s" + dimensions;
}
public boolean isOverlap(Object objA, Object objB) {
SpatialKey a = (SpatialKey) objA;
SpatialKey b = (SpatialKey) objB;
for (int i = 0; i < dimensions; i++) {
if (a.max[i] < b.min[i] || a.min[i] > b.max[i]) {
return false;
}
}
return true;
}
public SpatialKey copy(SpatialKey old) {
SpatialKey k = new SpatialKey();
k.min = new float[dimensions];
k.max = new float[dimensions];
System.arraycopy(old.min, 0, k.min, 0, dimensions);
System.arraycopy(old.max, 0, k.max, 0, dimensions);
return k;
}
public void increase(SpatialKey bounds, SpatialKey add) {
for (int i = 0; i < dimensions; i++) {
bounds.min[i] = Math.min(bounds.min[i], add.min[i]);
bounds.max[i] = Math.max(bounds.max[i], add.max[i]);
}
}
/**
* Get the area increase by extending a to contain b.
*
* @param objA the bounding box
* @param objB the object
* @return the area
*/
public float getAreaIncrease(Object objA, Object objB) {
SpatialKey a = (SpatialKey) objA;
SpatialKey b = (SpatialKey) objB;
float areaOld = 1, areaNew = 1;
for (int i = 0; i < dimensions; i++) {
float min = a.min[i];
float max = a.max[i];
areaOld *= max - min;
min = Math.min(min, b.min[i]);
max = Math.max(max, b.max[i]);
areaNew *= max - min;
}
return areaNew - areaOld;
}
public float getCombinedArea(Object objA, Object objB) {
SpatialKey a = (SpatialKey) objA;
SpatialKey b = (SpatialKey) objB;
float area = 1;
for (int i = 0; i < dimensions; i++) {
float min = Math.min(a.min[i], b.min[i]);
float max = Math.max(a.max[i], b.max[i]);
area *= max - min;
}
return area;
}
/**
* Check whether a contains b.
*
* @param objA the bounding box
* @param objB the object
* @return the area
*/
public boolean contains(Object objA, Object objB) {
SpatialKey a = (SpatialKey) objA;
SpatialKey b = (SpatialKey) objB;
for (int i = 0; i < dimensions; i++) {
if (a.min[i] > b.min[i] || a.max[i] < b.max[i]) {
return false;
}
}
return true;
}
}
...@@ -5,14 +5,27 @@ ...@@ -5,14 +5,27 @@
*/ */
package org.h2.test.store; package org.h2.test.store;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.Random; import java.util.Random;
import java.util.TreeMap; import java.util.TreeMap;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;
import javax.imageio.stream.FileImageOutputStream;
import org.h2.dev.store.btree.BtreeMap; import org.h2.dev.store.btree.BtreeMap;
import org.h2.dev.store.btree.BtreeMapStore; import org.h2.dev.store.btree.BtreeMapStore;
import org.h2.jaqu.bytecode.Null; import org.h2.jaqu.bytecode.Null;
import org.h2.store.fs.FileUtils; import org.h2.store.fs.FileUtils;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.util.New;
/** /**
* Tests the tree map store. * Tests the tree map store.
...@@ -29,6 +42,8 @@ public class TestBtreeMapStore extends TestBase { ...@@ -29,6 +42,8 @@ public class TestBtreeMapStore extends TestBase {
} }
public void test() { public void test() {
testRtreeMany();
testRtree();
testCustomMapType(); testCustomMapType();
testTruncateFile(); testTruncateFile();
testFastDelete(); testFastDelete();
...@@ -45,12 +60,124 @@ public class TestBtreeMapStore extends TestBase { ...@@ -45,12 +60,124 @@ public class TestBtreeMapStore extends TestBase {
testSimple(); testSimple();
} }
private void testRtreeMany() {
String fileName = getBaseDir() + "/testMeta.h3";
FileUtils.delete(fileName);
BtreeMapStore s;
s = openStore(fileName);
// s.setMaxPageSize(100);
RtreeMap<SpatialKey, String> r = s.openMap("data", "r", "s2", "");
Random rand = new Random(1);
for (int i = 0; i < 1000; i++) {
float x = rand.nextFloat(), y = rand.nextFloat();
float p = (float) (rand.nextFloat() * 0.01);
SpatialKey k = SpatialKey.create(i, x - p, x + p, y - p, y + p);
r.put(k, "" + i);
if (i > 0 && i % 10000 == 0) {
render(r, getBaseDir() + "/test.png");
}
}
s.close();
}
private void testRtree() {
String fileName = getBaseDir() + "/testMeta.h3";
FileUtils.delete(fileName);
BtreeMapStore s;
s = openStore(fileName);
RtreeMap<SpatialKey, String> r = s.openMap("data", "r", "s2", "");
add(r, "Bern", 46.57, 7.27, 124381);
add(r, "Basel", 47.34, 7.36, 170903);
add(r, "Zurich", 47.22, 8.33, 376008);
add(r, "Lucerne", 47.03, 8.18, 77491);
add(r, "Geneva", 46.12, 6.09, 191803);
add(r, "Lausanne", 46.31, 6.38, 127821);
add(r, "Winterthur", 47.30, 8.45, 102966);
add(r, "St. Gallen", 47.25, 9.22, 73500);
add(r, "Biel/Bienne", 47.08, 7.15, 51203);
add(r, "Lugano", 46.00, 8.57, 54667);
add(r, "Thun", 46.46, 7.38, 42623);
add(r, "Bellinzona", 46.12, 9.01, 17373);
add(r, "Chur", 46.51, 9.32, 33756);
ArrayList<String> list = New.arrayList();
for (SpatialKey x : r.keySet()) {
list.add(r.get(x));
}
Collections.sort(list);
assertEquals("[Basel, Bellinzona, Bern, Biel/Bienne, Chur, Geneva, " +
"Lausanne, Lucerne, Lugano, St. Gallen, Thun, Winterthur, Zurich]",
list.toString());
// render(r, getBaseDir() + "/test.png");
s.close();
}
private static void add(RtreeMap<SpatialKey, String> r, String name, double y, double x, int population) {
int id = r.size();
float a = (float) ((int) x + (x - (int) x) * 5 / 3);
float b = 50 - (float) ((int) y + (y - (int) y) * 5 / 3);
float s = (float) Math.sqrt(population / 10000000.);
SpatialKey k = SpatialKey.create(id, a - s, a + s, b - s, b + s);
r.put(k, name);
}
private static void render(RtreeMap<SpatialKey, String> r, String fileName) {
int width = 1000, height = 500;
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = (Graphics2D) img.getGraphics();
g2d.setBackground(Color.WHITE);
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, width, height);
g2d.setComposite(AlphaComposite.SrcOver.derive(0.5f));
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.BLACK);
SpatialKey b = SpatialKey.create(0, Float.MAX_VALUE, Float.MIN_VALUE,
Float.MAX_VALUE, Float.MIN_VALUE);
for (SpatialKey x : r.keySet()) {
b.min[0] = Math.min(b.min[0], x.min[0]);
b.min[1] = Math.min(b.min[1], x.min[1]);
b.max[0] = Math.max(b.max[0], x.max[0]);
b.max[1] = Math.max(b.max[1], x.max[1]);
}
// System.out.println(b);
for (SpatialKey x : r.keySet()) {
int[] rect = scale(b, x, width, height);
g2d.drawRect(rect[0], rect[1], rect[2] - rect[0], rect[3] - rect[1]);
String s = r.get(x);
g2d.drawChars(s.toCharArray(), 0, s.length(), rect[0], rect[1] - 4);
}
g2d.setColor(Color.red);
ArrayList<SpatialKey> list = New.arrayList();
r.addNodeKeys(list, r.getRoot());
for (SpatialKey x : list) {
int[] rect = scale(b, x, width, height);
g2d.drawRect(rect[0], rect[1], rect[2] - rect[0], rect[3] - rect[1]);
}
ImageWriter out = ImageIO.getImageWritersByFormatName("png").next();
try {
out.setOutput(new FileImageOutputStream(new File(fileName)));
out.write(img);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static int[] scale(SpatialKey b, SpatialKey x, int width, int height) {
int[] rect = {
(int) ((x.min[0] - b.min[0]) * (width * 0.9) / (b.max[0] - b.min[0]) + width * 0.05),
(int) ((x.min[1] - b.min[1]) * (height * 0.9) / (b.max[1] - b.min[1]) + height * 0.05),
(int) ((x.max[0] - b.min[0]) * (width * 0.9) / (b.max[0] - b.min[0]) + width * 0.05),
(int) ((x.max[1] - b.min[1]) * (height * 0.9) / (b.max[1] - b.min[1]) + height * 0.05),
};
return rect;
}
private void testCustomMapType() { private void testCustomMapType() {
String fileName = getBaseDir() + "/testMeta.h3"; String fileName = getBaseDir() + "/testMeta.h3";
FileUtils.delete(fileName); FileUtils.delete(fileName);
BtreeMapStore s; BtreeMapStore s;
s = openStore(fileName); s = openStore(fileName);
SequenceMap<Integer, String> seq = s.openMap("data", "s", "i", "").cast(); SequenceMap<Integer, String> seq = s.openMap("data", "s", "i", "");
StringBuilder buff = new StringBuilder(); StringBuilder buff = new StringBuilder();
for (int x : seq.keySet()) { for (int x : seq.keySet()) {
buff.append(x).append(';'); buff.append(x).append(';');
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
*/ */
package org.h2.test.store; package org.h2.test.store;
import java.nio.ByteBuffer;
import org.h2.dev.store.btree.DataUtils; import org.h2.dev.store.btree.DataUtils;
import org.h2.test.TestBase; import org.h2.test.TestBase;
...@@ -23,11 +24,66 @@ public class TestDataUtils extends TestBase { ...@@ -23,11 +24,66 @@ public class TestDataUtils extends TestBase {
} }
public void test() throws Exception { public void test() throws Exception {
testVarIntVarLong();
testCheckValue(); testCheckValue();
testPagePos(); testPagePos();
testEncodeLength(); testEncodeLength();
} }
private void testVarIntVarLong() {
ByteBuffer buff = ByteBuffer.allocate(100);
for (long x = 0; x < 1000; x++) {
testVarIntVarLong(buff, x);
testVarIntVarLong(buff, -x);
}
for (long x = Long.MIN_VALUE, i = 0; i < 1000; x++, i++) {
testVarIntVarLong(buff, x);
}
for (long x = Long.MAX_VALUE, i = 0; i < 1000; x--, i++) {
testVarIntVarLong(buff, x);
}
for (int shift = 0; shift < 64; shift++) {
for (long x = 250; x < 260; x++) {
testVarIntVarLong(buff, x << shift);
testVarIntVarLong(buff, -(x << shift));
}
}
// invalid varInt / varLong
// should work, but not read far too much
for (int i = 0; i < 50; i++) {
buff.put((byte) 255);
}
buff.flip();
assertEquals(-1, DataUtils.readVarInt(buff));
assertEquals(5, buff.position());
buff.rewind();
assertEquals(-1, DataUtils.readVarLong(buff));
assertEquals(10, buff.position());
}
private void testVarIntVarLong(ByteBuffer buff, long x) {
int len;
DataUtils.writeVarLong(buff, x);
len = buff.position();
buff.flip();
long y = DataUtils.readVarLong(buff);
assertEquals(y, x);
assertEquals(len, buff.position());
assertEquals(len, DataUtils.getVarLongLen(x));
buff.clear();
int intX = (int) x;
DataUtils.writeVarInt(buff, intX);
len = buff.position();
buff.flip();
int intY = DataUtils.readVarInt(buff);
assertEquals(intY, intX);
assertEquals(len, buff.position());
assertEquals(len, DataUtils.getVarIntLen(intX));
buff.clear();
}
private void testCheckValue() { private void testCheckValue() {
// 0 xor 0 = 0 // 0 xor 0 = 0
assertEquals(0, DataUtils.getCheckValue(0)); assertEquals(0, DataUtils.getCheckValue(0));
......
...@@ -22,8 +22,11 @@ public class TestMapFactory implements MapFactory { ...@@ -22,8 +22,11 @@ public class TestMapFactory implements MapFactory {
long createVersion) { long createVersion) {
if (mapType.equals("s")) { if (mapType.equals("s")) {
return new SequenceMap<K, V>(store, id, name, keyType, valueType, createVersion); return new SequenceMap<K, V>(store, id, name, keyType, valueType, createVersion);
} else if (mapType.equals("r")) {
return new RtreeMap<K, V>(store, id, name, keyType, valueType, createVersion);
} else {
throw new RuntimeException("Unsupported map type " + mapType);
} }
throw new RuntimeException("Unsupported map type " + mapType);
} }
@Override @Override
...@@ -36,6 +39,8 @@ public class TestMapFactory implements MapFactory { ...@@ -36,6 +39,8 @@ public class TestMapFactory implements MapFactory {
return new IntegerType(); return new IntegerType();
case 'r': case 'r':
return RowType.fromString(s, this); return RowType.fromString(s, this);
case 's':
return SpatialType.fromString(s);
} }
throw new RuntimeException("Unknown data type " + s); throw new RuntimeException("Unknown data type " + s);
} }
......
...@@ -20,11 +20,14 @@ import java.util.TreeMap; ...@@ -20,11 +20,14 @@ import java.util.TreeMap;
*/ */
public class BtreeMap<K, V> { public class BtreeMap<K, V> {
static final IllegalArgumentException KEY_NOT_FOUND = new IllegalArgumentException( protected static final IllegalArgumentException KEY_NOT_FOUND = new IllegalArgumentException(
"Key not found"); "Key not found");
static final IllegalArgumentException KEY_ALREADY_EXISTS = new IllegalArgumentException( protected static final IllegalArgumentException KEY_ALREADY_EXISTS = new IllegalArgumentException(
"Key already exists"); "Key already exists");
protected Page root;
protected BtreeMapStore store;
private final int id; private final int id;
private final String name; private final String name;
private final DataType keyType; private final DataType keyType;
...@@ -36,8 +39,6 @@ public class BtreeMap<K, V> { ...@@ -36,8 +39,6 @@ public class BtreeMap<K, V> {
* before this version. * before this version.
*/ */
private final TreeMap<Long, Page> oldRoots = new TreeMap<Long, Page>(); private final TreeMap<Long, Page> oldRoots = new TreeMap<Long, Page>();
private BtreeMapStore store;
private Page root;
private boolean readOnly; private boolean readOnly;
protected BtreeMap(BtreeMapStore store, int id, String name, protected BtreeMap(BtreeMapStore store, int id, String name,
...@@ -79,8 +80,7 @@ public class BtreeMap<K, V> { ...@@ -79,8 +80,7 @@ public class BtreeMap<K, V> {
* @throws InvalidArgumentException if this key does not exist (without * @throws InvalidArgumentException if this key does not exist (without
* stack trace) * stack trace)
*/ */
private Page set(Page p, long writeVersion, Object key, protected Page set(Page p, long writeVersion, Object key, Object value) {
Object value) {
if (p == null) { if (p == null) {
throw KEY_NOT_FOUND; throw KEY_NOT_FOUND;
} }
...@@ -103,7 +103,7 @@ public class BtreeMap<K, V> { ...@@ -103,7 +103,7 @@ public class BtreeMap<K, V> {
Page c2 = set(c, writeVersion, key, value); Page c2 = set(c, writeVersion, key, value);
if (c != c2) { if (c != c2) {
p = p.copyOnWrite(writeVersion); p = p.copyOnWrite(writeVersion);
p.setChild(index, c2.getPos(), c2.getPos()); p.setChild(index, c2);
} }
return p; return p;
} }
...@@ -120,8 +120,7 @@ public class BtreeMap<K, V> { ...@@ -120,8 +120,7 @@ public class BtreeMap<K, V> {
* @throws InvalidArgumentException if this key already exists (without * @throws InvalidArgumentException if this key already exists (without
* stack trace) * stack trace)
*/ */
private Page add(Page p, long writeVersion, Object key, protected Page add(Page p, long writeVersion, Object key, Object value) {
Object value) {
if (p == null) { if (p == null) {
Object[] keys = { key }; Object[] keys = { key };
Object[] values = { value }; Object[] values = { value };
...@@ -167,15 +166,15 @@ public class BtreeMap<K, V> { ...@@ -167,15 +166,15 @@ public class BtreeMap<K, V> {
Object k = c.getKey(at); Object k = c.getKey(at);
Page split = c.split(at); Page split = c.split(at);
p = p.copyOnWrite(writeVersion); p = p.copyOnWrite(writeVersion);
p.setChild(index, c.getPos(), c.getTotalSize()); p.setChild(index, split);
p.insert(index, k, null, split.getPos(), split.getTotalSize()); p.insert(index, k, null, c.getPos(), c.getTotalSize());
// now we are not sure where to add // now we are not sure where to add
return add(p, writeVersion, key, value); return add(p, writeVersion, key, value);
} }
Page c2 = add(c, writeVersion, key, value); Page c2 = add(c, writeVersion, key, value);
p = p.copyOnWrite(writeVersion); p = p.copyOnWrite(writeVersion);
// the child might be the same, but not the size // the child might be the same, but not the size
p.setChild(index, c2.getPos(), c2.getTotalSize()); p.setChild(index, c2);
return p; return p;
} }
...@@ -201,7 +200,7 @@ public class BtreeMap<K, V> { ...@@ -201,7 +200,7 @@ public class BtreeMap<K, V> {
* @param parents the stack of parent page positions * @param parents the stack of parent page positions
* @param key the key * @param key the key
*/ */
void min(Page p, ArrayList<CursorPos> parents, Object key) { protected void min(Page p, ArrayList<CursorPos> parents, Object key) {
while (p != null) { while (p != null) {
if (!p.isLeaf()) { if (!p.isLeaf()) {
int x = key == null ? -1 : p.binarySearch(key); int x = key == null ? -1 : p.binarySearch(key);
...@@ -235,7 +234,7 @@ public class BtreeMap<K, V> { ...@@ -235,7 +234,7 @@ public class BtreeMap<K, V> {
* @param parents the stack of parent page positions * @param parents the stack of parent page positions
* @return the next key * @return the next key
*/ */
Object nextKey(ArrayList<CursorPos> parents) { protected Object nextKey(ArrayList<CursorPos> parents) {
if (parents.size() == 0) { if (parents.size() == 0) {
return null; return null;
} }
...@@ -270,7 +269,7 @@ public class BtreeMap<K, V> { ...@@ -270,7 +269,7 @@ public class BtreeMap<K, V> {
* @param key the key * @param key the key
* @return the value or null * @return the value or null
*/ */
private Object binarySearch(Page p, Object key) { protected Object binarySearch(Page p, Object key) {
int x = p.binarySearch(key); int x = p.binarySearch(key);
if (!p.isLeaf()) { if (!p.isLeaf()) {
if (x < 0) { if (x < 0) {
...@@ -287,7 +286,6 @@ public class BtreeMap<K, V> { ...@@ -287,7 +286,6 @@ public class BtreeMap<K, V> {
return null; return null;
} }
public boolean containsKey(Object key) { public boolean containsKey(Object key) {
return get(key) != null; return get(key) != null;
} }
...@@ -298,7 +296,7 @@ public class BtreeMap<K, V> { ...@@ -298,7 +296,7 @@ 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
*/ */
Page getPage(K key) { protected Page getPage(K key) {
if (root == null) { if (root == null) {
return null; return null;
} }
...@@ -312,7 +310,7 @@ public class BtreeMap<K, V> { ...@@ -312,7 +310,7 @@ public class BtreeMap<K, V> {
* @param key the key * @param key the key
* @return the page or null * @return the page or null
*/ */
private Page binarySearchPage(Page p, Object key) { protected Page binarySearchPage(Page p, Object key) {
int x = p.binarySearch(key); int x = p.binarySearch(key);
if (!p.isLeaf()) { if (!p.isLeaf()) {
if (x < 0) { if (x < 0) {
...@@ -379,7 +377,6 @@ public class BtreeMap<K, V> { ...@@ -379,7 +377,6 @@ public class BtreeMap<K, V> {
} }
} }
/** /**
* Remove an existing key-value pair. * Remove an existing key-value pair.
* *
...@@ -389,7 +386,7 @@ public class BtreeMap<K, V> { ...@@ -389,7 +386,7 @@ public class BtreeMap<K, V> {
* @return the new root page (null if empty) * @return the new root page (null if empty)
* @throws InvalidArgumentException if not found (without stack trace) * @throws InvalidArgumentException if not found (without stack trace)
*/ */
private Page removeExisting(Page p, long writeVersion, Object key) { protected Page removeExisting(Page p, long writeVersion, Object key) {
if (p == null) { if (p == null) {
throw KEY_NOT_FOUND; throw KEY_NOT_FOUND;
} }
...@@ -397,7 +394,7 @@ public class BtreeMap<K, V> { ...@@ -397,7 +394,7 @@ public class BtreeMap<K, V> {
if (p.isLeaf()) { if (p.isLeaf()) {
if (index >= 0) { if (index >= 0) {
if (p.getKeyCount() == 1) { if (p.getKeyCount() == 1) {
store.removePage(p.getPos()); removePage(p);
return null; return null;
} }
p = p.copyOnWrite(writeVersion); p = p.copyOnWrite(writeVersion);
...@@ -420,16 +417,16 @@ public class BtreeMap<K, V> { ...@@ -420,16 +417,16 @@ public class BtreeMap<K, V> {
// this child was deleted // this child was deleted
p.remove(index); p.remove(index);
if (p.getKeyCount() == 0) { if (p.getKeyCount() == 0) {
store.removePage(p.getPos()); removePage(p);
p = p.getChildPage(0); p = p.getChildPage(0);
} }
} else { } else {
p.setChild(index, c2.getPos(), c2.getTotalSize()); p.setChild(index, c2);
} }
return p; return p;
} }
private void markChanged(Page oldRoot) { protected void markChanged(Page oldRoot) {
if (oldRoot != root) { if (oldRoot != root) {
long v = store.getCurrentVersion(); long v = store.getCurrentVersion();
if (!oldRoots.containsKey(v)) { if (!oldRoots.containsKey(v)) {
...@@ -530,7 +527,7 @@ public class BtreeMap<K, V> { ...@@ -530,7 +527,7 @@ public class BtreeMap<K, V> {
* *
* @return the root page * @return the root page
*/ */
Page getRoot() { public Page getRoot() {
return root; return root;
} }
...@@ -585,13 +582,13 @@ public class BtreeMap<K, V> { ...@@ -585,13 +582,13 @@ public class BtreeMap<K, V> {
return readOnly; return readOnly;
} }
private void checkOpen() { protected void checkOpen() {
if (store == null) { if (store == null) {
throw new IllegalStateException("This map is closed"); throw new IllegalStateException("This map is closed");
} }
} }
private void checkWrite() { protected void checkWrite() {
if (readOnly) { if (readOnly) {
checkOpen(); checkOpen();
throw new IllegalStateException("This map is read-only"); throw new IllegalStateException("This map is read-only");
...@@ -631,9 +628,8 @@ public class BtreeMap<K, V> { ...@@ -631,9 +628,8 @@ public class BtreeMap<K, V> {
return createVersion; return createVersion;
} }
@SuppressWarnings("unchecked") protected void removePage(Page p) {
public <M> M cast() { store.removePage(p.getPos());
return (M) this;
} }
} }
...@@ -38,7 +38,6 @@ header: ...@@ -38,7 +38,6 @@ header:
blockSize=4096 blockSize=4096
TODO: TODO:
- 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)
...@@ -56,6 +55,9 @@ TODO: ...@@ -56,6 +55,9 @@ TODO:
- 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 - triggers
- compare with newest version of IndexedDb
- support database version / schema version
- implement more counted b-tree (skip, get positions)
*/ */
...@@ -142,8 +144,7 @@ public class BtreeMapStore { ...@@ -142,8 +144,7 @@ public class BtreeMapStore {
/** /**
* Open a map. * Open a map.
* *
* @param <K> the key type * @param <T> the map 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 mapType the map type
* @param keyType the key type * @param keyType the key type
...@@ -151,8 +152,8 @@ public class BtreeMapStore { ...@@ -151,8 +152,8 @@ public class BtreeMapStore {
* @return the map * @return the map
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <K, V> BtreeMap<K, V> openMap(String name, String mapType, String keyType, String valueType) { public <T extends BtreeMap<?, ?>> T openMap(String name, String mapType, String keyType, String valueType) {
BtreeMap<K, V> m = (BtreeMap<K, V>) maps.get(name); BtreeMap<?, ?> m = maps.get(name);
if (m == null) { if (m == null) {
String identifier = meta.get("map." + name); String identifier = meta.get("map." + name);
int id; int id;
...@@ -178,14 +179,14 @@ public class BtreeMapStore { ...@@ -178,14 +179,14 @@ public class BtreeMapStore {
DataType k = buildDataType(keyType); DataType k = buildDataType(keyType);
DataType v = buildDataType(valueType); DataType v = buildDataType(valueType);
if (mapType.equals("")) { if (mapType.equals("")) {
m = new BtreeMap<K, V>(this, id, name, k, v, createVersion); m = new BtreeMap<Object, Object>(this, id, name, k, v, createVersion);
} else { } else {
m = getMapFactory().buildMap(mapType, this, id, name, k, v, createVersion); m = getMapFactory().buildMap(mapType, this, id, name, k, v, createVersion);
} }
maps.put(name, m); maps.put(name, m);
m.setRootPos(root); m.setRootPos(root);
} }
return m; return (T) m;
} }
/** /**
...@@ -860,7 +861,7 @@ public class BtreeMapStore { ...@@ -860,7 +861,7 @@ public class BtreeMapStore {
* *
* @return the maximum number of entries * @return the maximum number of entries
*/ */
int getMaxPageSize() { public int getMaxPageSize() {
return maxPageSize; return maxPageSize;
} }
......
...@@ -9,16 +9,17 @@ package org.h2.dev.store.btree; ...@@ -9,16 +9,17 @@ package org.h2.dev.store.btree;
/** /**
* A position in a cursor * A position in a cursor
*/ */
class CursorPos { public class CursorPos {
/** /**
* The current page. * The current page.
*/ */
Page page; public Page page;
/** /**
* The current index. * The current index.
*/ */
int index; public int index;
} }
...@@ -22,14 +22,6 @@ public interface DataType { ...@@ -22,14 +22,6 @@ public interface DataType {
*/ */
int compare(Object a, Object b); int compare(Object a, Object b);
/**
* Get the length in bytes used to store an object.
*
* @param obj the object
* @return the length
*/
int length(Object obj);
/** /**
* Get the maximum length in bytes used to store an object. In many cases, * Get the maximum length in bytes used to store an object. In many cases,
* this method can be faster than calculating the exact length. * this method can be faster than calculating the exact length.
......
...@@ -123,13 +123,14 @@ public class DataUtils { ...@@ -123,13 +123,14 @@ public class DataUtils {
return x; return x;
} }
x &= 0x7f; x &= 0x7f;
for (int s = 7;; s += 7) { for (int s = 7; s < 64; s += 7) {
long b = buff.get(); long b = buff.get();
x |= (b & 0x7f) << s; x |= (b & 0x7f) << s;
if (b >= 0) { if (b >= 0) {
return x; break;
} }
} }
return x;
} }
/** /**
......
...@@ -17,21 +17,6 @@ public class StringType implements DataType { ...@@ -17,21 +17,6 @@ public class StringType implements DataType {
return a.toString().compareTo(b.toString()); return a.toString().compareTo(b.toString());
} }
public int length(Object obj) {
int plus = 0;
String s = obj.toString();
int len = s.length();
for (int i = 0; i < len; i++) {
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) { public int getMaxLength(Object obj) {
return DataUtils.MAX_VAR_INT_LEN + obj.toString().length() * 3; return DataUtils.MAX_VAR_INT_LEN + obj.toString().length() * 3;
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论