提交 1217a01e authored 作者: Thomas Mueller's avatar Thomas Mueller

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

上级 747b2489
......@@ -9,6 +9,7 @@ package org.h2.test.store;
import java.util.ArrayList;
import org.h2.dev.store.btree.BtreeMap;
import org.h2.dev.store.btree.BtreeMapStore;
import org.h2.dev.store.btree.Cursor;
import org.h2.dev.store.btree.CursorPos;
import org.h2.dev.store.btree.DataType;
import org.h2.dev.store.btree.Page;
......@@ -35,7 +36,7 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> {
if (root == null) {
return null;
}
return (V) getSpatial(root, key);
return (V) get(root, key);
}
private boolean contains(Page p, int index, Object key) {
......@@ -53,11 +54,11 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> {
* @param key the key
* @return the value, or null if not found
*/
protected Object getSpatial(Page p, Object key) {
protected Object get(Page p, Object key) {
if (!p.isLeaf()) {
for (int i = 0; i < p.getKeyCount(); i++) {
if (contains(p, i, key)) {
Object o = getSpatial(p.getChildPage(i), key);
Object o = get(p.getChildPage(i), key);
if (o != null) {
return o;
}
......@@ -77,14 +78,14 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> {
if (root == null) {
return null;
}
return getPageSpatial(root, key);
return getPage(root, key);
}
protected Page getPageSpatial(Page p, Object key) {
protected Page getPage(Page p, Object key) {
if (!p.isLeaf()) {
for (int i = 0; i < p.getKeyCount(); i++) {
if (contains(p, i, key)) {
Page x = getPageSpatial(p.getChildPage(i), key);
Page x = getPage(p.getChildPage(i), key);
if (x != null) {
return x;
}
......@@ -111,7 +112,7 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> {
Page c2 = set(c, writeVersion, key, value);
if (c != c2) {
p = p.copyOnWrite(writeVersion);
setChildUpdateBox(p, i, c2);
setChildUpdateBox(p, i, c2, key);
break;
}
}
......@@ -136,10 +137,18 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> {
for (int i = 0; i < p.getKeyCount(); i++) {
if (contains(p, i, key)) {
Page c = p.getChildPage(i);
long oldSize = c.getTotalSize();
Page c2 = removeExisting(c, writeVersion, key);
if (c != c2) {
if (c2 == null) {
// this child was deleted
p.remove(i);
if (p.getKeyCount() == 0) {
removePage(p);
return null;
}
} else if (oldSize != c2.getTotalSize()) {
p = p.copyOnWrite(writeVersion);
setChildUpdateBox(p, i, c2);
setChildUpdateBox(p, i, c2, key);
break;
}
}
......@@ -147,6 +156,10 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> {
} else {
for (int i = 0; i < p.getKeyCount(); i++) {
if (keyType.equals(p.getKey(i), key)) {
if (p.getKeyCount() == 1) {
removePage(p);
return null;
}
p = p.copyOnWrite(writeVersion);
p.remove(i);
break;
......@@ -157,21 +170,26 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> {
}
/**
* Set the child and update the bounding box.
* Set the child and update the bounding box if required. The bounding box
* is only updated if the key touches or is outside the old bounding box.
*
* @param p the parent (this page is changed)
* @param index the child index
* @param c the child page
* @param key the new or old key
*/
private void setChildUpdateBox(Page p, int index, Page c) {
p.setKey(index, getBounds(c));
private void setChildUpdateBox(Page p, int index, Page c, Object key) {
Object oldBounds = p.getKey(index);
if (key == null || !keyType.isInside(key, oldBounds)) {
p.setKey(index, getBounds(c));
}
p.setChild(index, c);
}
private SpatialKey getBounds(Page x) {
SpatialKey bounds = keyType.copy((SpatialKey) x.getKey(0));
private Object getBounds(Page x) {
Object bounds = keyType.createBoundingBox(x.getKey(0));
for (int i = 1; i < x.getKeyCount(); i++) {
keyType.increase(bounds, (SpatialKey) x.getKey(i));
keyType.increaseBounds(bounds, x.getKey(i));
}
return bounds;
}
......@@ -221,15 +239,15 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> {
c = c.copyOnWrite(writeVersion);
Page split = split(c, writeVersion);
p = p.copyOnWrite(writeVersion);
setChildUpdateBox(p, index, c);
setChildUpdateBox(p, index, c, null);
p.insert(index, getBounds(split), 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
setChildUpdateBox(p, index, c2);
// the child might be the same, but maybe not the bounding box
setChildUpdateBox(p, index, c2, key);
return p;
}
......@@ -266,8 +284,8 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> {
while (p.getKeyCount() > 0) {
float diff = 0, bestA = 0, bestB = 0;
int best = 0;
SpatialKey ba = getBounds(newP);
SpatialKey bb = getBounds(split);
Object ba = getBounds(newP);
Object bb = getBounds(split);
for (int i = 0; i < p.getKeyCount(); i++) {
Object o = p.getKey(i);
float a = keyType.getAreaIncrease(ba, o);
......@@ -301,10 +319,11 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> {
source.remove(sourceIndex);
}
public void addNodeKeys(ArrayList<SpatialKey> list, Page p) {
@SuppressWarnings("unchecked")
public void addNodeKeys(ArrayList<K> list, Page p) {
if (p != null && !p.isLeaf()) {
for (int i = 0; i < p.getKeyCount(); i++) {
list.add((SpatialKey) p.getKey(i));
list.add((K) p.getKey(i));
addNodeKeys(list, p.getChildPage(i));
}
}
......@@ -317,16 +336,17 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> {
* @param parents the stack of parent page positions
* @param key the key
*/
protected void min(Page p, ArrayList<CursorPos> parents, Object key) {
protected CursorPos min(Page p, Cursor<K, V> cursor, Object key) {
while (p != null) {
CursorPos c = new CursorPos();
c.page = p;
parents.add(c);
if (p.isLeaf()) {
return;
return c;
}
cursor.push(c);
p = p.getChildPage(0);
}
return null;
}
/**
......@@ -335,29 +355,25 @@ public class RtreeMap<K, V> extends BtreeMap<K, V> {
* @param parents the stack of parent page positions
* @return the next key
*/
protected Object nextKey(ArrayList<CursorPos> parents) {
if (parents.size() == 0) {
return null;
}
protected Object nextKey(CursorPos p, Cursor<K, V> cursor) {
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);
Page x = p.page;
if (index < x.getKeyCount()) {
return x.getKey(index);
}
while (true) {
if (parents.size() == 0) {
p = cursor.pop();
if (p == null) {
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);
x = p.page;
if (index < x.getKeyCount()) {
cursor.push(p);
x = x.getChildPage(index);
p = min(x, cursor, null);
cursor.setCurrentPos(p);
break;
}
}
......
......@@ -6,38 +6,66 @@
*/
package org.h2.test.store;
import java.util.Arrays;
/**
* A unique spatial key.
*/
public class SpatialKey {
public float[] min;
public float[] max;
public long id;
private float[] minMax;
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;
/**
* Create a new key.
*
* @param id the id
* @param minMax min x, max x, min y, max y, and so on
*/
public SpatialKey(long id, float... minMax) {
this.id = id;
this.minMax = minMax;
}
public float min(int dim) {
return minMax[dim + dim];
}
public void setMin(int dim, float x) {
minMax[dim + dim] = x;
}
public float max(int dim) {
return minMax[dim + dim + 1];
}
public void setMax(int dim, float x) {
minMax[dim + dim + 1] = x;
}
public String toString() {
StringBuilder buff = new StringBuilder();
buff.append(id).append(": (");
for (int i = 0; i < min.length; i++) {
for (int i = 0; i < minMax.length; i += 2) {
if (i > 0) {
buff.append(", ");
}
buff.append(min[i]).append('/').append(max[i]);
buff.append(minMax[i]).append('/').append(minMax[i + 1]);
}
return buff.append(")").toString();
}
public int hashCode() {
return (int) ((id >>> 32) ^ id);
}
public boolean equals(Object other) {
if (!(other instanceof SpatialKey)) {
return false;
}
SpatialKey o = (SpatialKey) other;
return Arrays.equals(minMax, o.minMax);
}
}
......@@ -58,15 +58,15 @@ public class SpatialType implements DataType {
SpatialKey k = (SpatialKey) obj;
int flags = 0;
for (int i = 0; i < dimensions; i++) {
if (k.min[i] == k.max[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]);
buff.putFloat(k.min(i));
if ((flags & (1 << i)) == 0) {
buff.putFloat(k.max[i]);
buff.putFloat(k.max(i));
}
}
DataUtils.writeVarLong(buff, k.id);
......@@ -74,20 +74,21 @@ public class SpatialType implements DataType {
@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];
float[] minMax = new float[dimensions * 2];
for (int i = 0; i < dimensions; i++) {
k.min[i] = buff.getFloat();
float min = buff.getFloat();
float max;
if ((flags & (1 << i)) != 0) {
k.max[i] = k.min[i];
max = min;
} else {
k.max[i] = buff.getFloat();
max = buff.getFloat();
}
minMax[i + i] = min;
minMax[i + i + 1] = max;
}
k.id = DataUtils.readVarLong(buff);
return k;
long id = DataUtils.readVarLong(buff);
return new SpatialKey(id, minMax);
}
@Override
......@@ -99,26 +100,19 @@ public class SpatialType implements DataType {
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]) {
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) {
public void increaseBounds(Object bounds, Object add) {
SpatialKey b = (SpatialKey) bounds;
SpatialKey a = (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]);
b.setMin(i, Math.min(b.min(i), a.min(i)));
b.setMax(i, Math.max(b.max(i), a.max(i)));
}
}
......@@ -134,11 +128,11 @@ public class SpatialType implements DataType {
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];
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]);
min = Math.min(min, b.min(i));
max = Math.max(max, b.max(i));
areaNew *= max - min;
}
return areaNew - areaOld;
......@@ -149,8 +143,8 @@ public class SpatialType implements DataType {
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]);
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;
......@@ -167,11 +161,40 @@ public class SpatialType implements DataType {
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]) {
if (a.min(i) > b.min(i) || a.max(i) < b.max(i)) {
return false;
}
}
return true;
}
/**
* Check whether a given object is completely inside and does not touch the
* given bound.
*
* @param objA the object to check
* @param objB the bounds
* @return true if a is completely inside b
*/
public boolean isInside(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;
}
public Object createBoundingBox(Object objA) {
float[] minMax = new float[dimensions * 2];
SpatialKey a = (SpatialKey) objA;
for (int i = 0; i < dimensions; i++) {
minMax[i + i] = a.min(i);
minMax[i + i + 1] = a.max(i);
}
return new SpatialKey(0, minMax);
}
}
......@@ -7,7 +7,6 @@
package org.h2.dev.store.btree;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeMap;
......@@ -200,7 +199,7 @@ public class BtreeMap<K, V> {
* @param parents the stack of parent page positions
* @param key the key
*/
protected void min(Page p, ArrayList<CursorPos> parents, Object key) {
protected CursorPos min(Page p, Cursor<K, V> cursor, Object key) {
while (p != null) {
if (!p.isLeaf()) {
int x = key == null ? -1 : p.binarySearch(key);
......@@ -212,7 +211,7 @@ public class BtreeMap<K, V> {
CursorPos c = new CursorPos();
c.page = p;
c.index = x;
parents.add(c);
cursor.push(c);
p = p.getChildPage(x);
} else {
int x = key == null ? 0 : p.binarySearch(key);
......@@ -222,10 +221,10 @@ public class BtreeMap<K, V> {
CursorPos c = new CursorPos();
c.page = p;
c.index = x;
parents.add(c);
return;
return c;
}
}
return null;
}
/**
......@@ -234,29 +233,24 @@ public class BtreeMap<K, V> {
* @param parents the stack of parent page positions
* @return the next key
*/
protected Object nextKey(ArrayList<CursorPos> parents) {
if (parents.size() == 0) {
return null;
}
protected Object nextKey(CursorPos p, Cursor<K, V> cursor) {
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) {
p = cursor.pop();
if (p == null) {
return null;
}
p = parents.remove(parents.size() - 1);
index = ++p.index;
if (index <= p.page.getKeyCount()) {
parents.add(p);
cursor.push(p);
Page x = p.page;
x = x.getChildPage(index);
min(x, parents, null);
x = x.getChildPage(index);
p = min(x, cursor, null);
cursor.setCurrentPos(p);
break;
}
}
......
......@@ -15,16 +15,19 @@ import java.util.Iterator;
* @param <K> the key type
* @param <V> the value type
*/
class Cursor<K, V> implements Iterator<K> {
public class Cursor<K, V> implements Iterator<K> {
private final BtreeMap<K, V> map;
private final ArrayList<CursorPos> parents = new ArrayList<CursorPos>();
private CursorPos currentPos;
private K current;
Cursor(BtreeMap<K, V> map, Page root, K from) {
this.map = map;
map.min(root, parents, from);
fetchNext();
currentPos = map.min(root, this, from);
if (currentPos != null) {
fetchNext();
}
}
public K next() {
......@@ -37,7 +40,7 @@ class Cursor<K, V> implements Iterator<K> {
@SuppressWarnings("unchecked")
private void fetchNext() {
current = (K) map.nextKey(parents);
current = (K) map.nextKey(currentPos, this);
}
public boolean hasNext() {
......@@ -48,5 +51,18 @@ class Cursor<K, V> implements Iterator<K> {
throw new UnsupportedOperationException();
}
public void setCurrentPos(CursorPos p) {
currentPos = p;
}
public void push(CursorPos p) {
parents.add(p);
}
public CursorPos pop() {
int size = parents.size();
return size == 0 ? null : parents.remove(size - 1);
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论