Skip to content
项目
群组
代码片段
帮助
正在加载...
帮助
为 GitLab 提交贡献
登录/注册
切换导航
H
h2database
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
计划
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
Administrator
h2database
Commits
32bae90e
提交
32bae90e
authored
8月 17, 2012
作者:
Thomas Mueller
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
A persistent multi-version map (work in progress).
上级
6ab864dc
显示空白字符变更
内嵌
并排
正在显示
11 个修改的文件
包含
616 行增加
和
406 行删除
+616
-406
RowType.java
h2/src/test/org/h2/test/store/RowType.java
+3
-3
RtreeMap.java
h2/src/test/org/h2/test/store/RtreeMap.java
+26
-0
SequenceMap.java
h2/src/test/org/h2/test/store/SequenceMap.java
+67
-0
TestBtreeMapStore.java
h2/src/test/org/h2/test/store/TestBtreeMapStore.java
+19
-5
TestMapFactory.java
h2/src/test/org/h2/test/store/TestMapFactory.java
+19
-5
BtreeMap.java
h2/src/tools/org/h2/dev/store/btree/BtreeMap.java
+303
-10
BtreeMapStore.java
h2/src/tools/org/h2/dev/store/btree/BtreeMapStore.java
+68
-49
Cursor.java
h2/src/tools/org/h2/dev/store/btree/Cursor.java
+8
-5
DataType.java
h2/src/tools/org/h2/dev/store/btree/DataType.java
+3
-2
MapFactory.java
h2/src/tools/org/h2/dev/store/btree/MapFactory.java
+46
-0
Page.java
h2/src/tools/org/h2/dev/store/btree/Page.java
+54
-327
没有找到文件。
h2/src/test/org/h2/test/store/RowType.java
浏览文件 @
32bae90e
...
...
@@ -8,7 +8,7 @@ package org.h2.test.store;
import
java.nio.ByteBuffer
;
import
org.h2.dev.store.btree.DataType
;
import
org.h2.dev.store.btree.
DataType
Factory
;
import
org.h2.dev.store.btree.
Map
Factory
;
import
org.h2.dev.store.btree.DataUtils
;
import
org.h2.util.StringUtils
;
...
...
@@ -115,7 +115,7 @@ public class RowType implements DataType {
* @param factory the data type factory
* @return the row type
*/
static
RowType
fromString
(
String
t
,
DataType
Factory
factory
)
{
static
RowType
fromString
(
String
t
,
Map
Factory
factory
)
{
if
(!
t
.
startsWith
(
"r("
)
||
!
t
.
endsWith
(
")"
))
{
throw
new
RuntimeException
(
"Unknown type: "
+
t
);
}
...
...
@@ -123,7 +123,7 @@ public class RowType implements DataType {
String
[]
array
=
StringUtils
.
arraySplit
(
t
,
','
,
false
);
DataType
[]
types
=
new
DataType
[
array
.
length
];
for
(
int
i
=
0
;
i
<
array
.
length
;
i
++)
{
types
[
i
]
=
factory
.
fromString
(
array
[
i
]);
types
[
i
]
=
factory
.
buildDataType
(
array
[
i
]);
}
return
new
RowType
(
types
);
}
...
...
h2/src/test/org/h2/test/store/RtreeMap.java
0 → 100644
浏览文件 @
32bae90e
/*
* 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
);
}
}
h2/src/test/org/h2/test/store/SequenceMap.java
0 → 100644
浏览文件 @
32bae90e
/*
* 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
;
}
};
}
}
h2/src/test/org/h2/test/store/TestBtreeMapStore.java
浏览文件 @
32bae90e
...
...
@@ -29,6 +29,7 @@ public class TestBtreeMapStore extends TestBase {
}
public
void
test
()
{
testCustomMapType
();
testTruncateFile
();
testFastDelete
();
testRollbackInMemory
();
...
...
@@ -44,6 +45,20 @@ public class TestBtreeMapStore extends TestBase {
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
()
{
String
fileName
=
getBaseDir
()
+
"/testMeta.h3"
;
FileUtils
.
delete
(
fileName
);
...
...
@@ -233,11 +248,11 @@ public class TestBtreeMapStore extends TestBase {
data
.
put
(
"1"
,
"Hello"
);
data
.
put
(
"2"
,
"World"
);
s
.
store
();
assertEquals
(
"1/1//"
,
m
.
get
(
"map.data"
));
assertEquals
(
"1/1//
/
"
,
m
.
get
(
"map.data"
));
assertTrue
(
m
.
containsKey
(
"chunk.1"
));
data
.
put
(
"1"
,
"Hallo"
);
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
.
containsKey
(
"chunk.1"
));
assertTrue
(
m
.
containsKey
(
"chunk.2"
));
...
...
@@ -255,8 +270,7 @@ public class TestBtreeMapStore extends TestBase {
BtreeMapStore
s
=
openStore
(
fileName
);
// s.setCompressor(null);
s
.
setMaxPageSize
(
40
);
RowType
rowType
=
RowType
.
fromString
(
"r(i,,)"
,
new
TestTypeFactory
());
BtreeMap
<
Integer
,
Object
[]>
m
=
s
.
openMap
(
"data"
,
new
IntegerType
(),
rowType
);
BtreeMap
<
Integer
,
Object
[]>
m
=
s
.
openMap
(
"data"
,
""
,
"i"
,
"r(i,,)"
);
int
i
=
0
;
// long t = System.currentTimeMillis();
for
(;
i
<
len
;)
{
...
...
@@ -540,7 +554,7 @@ public class TestBtreeMapStore extends TestBase {
}
private
static
BtreeMapStore
openStore
(
String
fileName
)
{
BtreeMapStore
store
=
BtreeMapStore
.
open
(
fileName
,
new
Test
Type
Factory
());
BtreeMapStore
store
=
BtreeMapStore
.
open
(
fileName
,
new
Test
Map
Factory
());
store
.
setMaxPageSize
(
10
);
return
store
;
}
...
...
h2/src/test/org/h2/test/store/Test
Type
Factory.java
→
h2/src/test/org/h2/test/store/Test
Map
Factory.java
浏览文件 @
32bae90e
...
...
@@ -5,16 +5,29 @@
*/
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
Factory
;
import
org.h2.dev.store.btree.
Map
Factory
;
import
org.h2.dev.store.btree.StringType
;
/**
* A data type factory.
*/
public
class
Test
TypeFactory
implements
DataType
Factory
{
public
class
Test
MapFactory
implements
Map
Factory
{
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
)
{
return
new
StringType
();
}
...
...
@@ -27,9 +40,10 @@ public class TestTypeFactory implements DataTypeFactory {
throw
new
RuntimeException
(
"Unknown data type "
+
s
);
}
public
DataType
getDataType
(
Class
<?>
objectClass
)
{
@Override
public
String
getDataType
(
Class
<?>
objectClass
)
{
if
(
objectClass
==
Integer
.
class
)
{
return
new
IntegerType
()
;
return
"i"
;
}
throw
new
RuntimeException
(
"Unsupported object class "
+
objectClass
.
toString
());
}
...
...
h2/src/tools/org/h2/dev/store/btree/BtreeMap.java
浏览文件 @
32bae90e
...
...
@@ -7,6 +7,7 @@
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
;
...
...
@@ -19,6 +20,11 @@ import java.util.TreeMap;
*/
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
String
name
;
private
final
DataType
keyType
;
...
...
@@ -34,7 +40,8 @@ public class BtreeMap<K, V> {
private
Page
root
;
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
.
id
=
id
;
this
.
name
=
name
;
...
...
@@ -53,13 +60,125 @@ public class BtreeMap<K, V> {
checkWrite
();
Page
oldRoot
=
root
;
if
(
containsKey
(
key
))
{
root
=
Page
.
set
(
this
,
root
,
store
.
getCurrentVersion
(),
key
,
data
);
root
=
set
(
root
,
store
.
getCurrentVersion
(),
key
,
data
);
}
else
{
root
=
Page
.
add
(
this
,
root
,
store
.
getCurrentVersion
(),
key
,
data
);
root
=
add
(
root
,
store
.
getCurrentVersion
(),
key
,
data
);
}
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.
*
...
...
@@ -72,8 +191,102 @@ public class BtreeMap<K, V> {
if
(
root
==
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
)
{
return
get
(
key
)
!=
null
;
...
...
@@ -89,7 +302,31 @@ public class BtreeMap<K, V> {
if
(
root
==
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> {
checkWrite
();
if
(
containsKey
(
key
))
{
Page
oldRoot
=
root
;
root
=
Page
.
removeExisting
(
root
,
store
.
getCurrentVersion
(),
key
);
root
=
removeExisting
(
root
,
store
.
getCurrentVersion
(),
key
);
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
)
{
if
(
oldRoot
!=
root
)
{
long
v
=
store
.
getCurrentVersion
();
...
...
@@ -212,16 +499,17 @@ public class BtreeMap<K, V> {
*/
public
Iterator
<
K
>
keyIterator
(
K
from
)
{
checkOpen
();
return
new
Cursor
<
K
>(
root
,
from
);
return
new
Cursor
<
K
,
V
>(
this
,
root
,
from
);
}
public
Set
<
K
>
keySet
()
{
checkOpen
();
final
Page
root
=
this
.
root
;
return
new
AbstractSet
<
K
>()
{
@Override
public
Iterator
<
K
>
iterator
()
{
return
new
Cursor
<
K
>(
getRoot
()
,
null
);
return
new
Cursor
<
K
,
V
>(
BtreeMap
.
this
,
root
,
null
);
}
@Override
...
...
@@ -285,7 +573,7 @@ public class BtreeMap<K, V> {
}
}
void
stored
()
{
void
revertTemp
()
{
oldRoots
.
clear
();
}
...
...
@@ -339,8 +627,13 @@ public class BtreeMap<K, V> {
return
this
==
o
;
}
long
getCreate
d
Version
()
{
long
getCreateVersion
()
{
return
createVersion
;
}
@SuppressWarnings
(
"unchecked"
)
public
<
M
>
M
cast
()
{
return
(
M
)
this
;
}
}
h2/src/tools/org/h2/dev/store/btree/BtreeMapStore.java
浏览文件 @
32bae90e
...
...
@@ -38,7 +38,7 @@ header:
blockSize=4096
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
- map.getVersion and opening old maps read-only
- limited support for writing to old versions (branches)
...
...
@@ -55,6 +55,7 @@ TODO:
- check what happens on concurrent reads and 1 write; multiple writes
- support large binaries
- support stores that span multiple files (chunks stored in other files)
- triggers
*/
...
...
@@ -68,7 +69,7 @@ public class BtreeMapStore {
private
static
final
StringType
STRING_TYPE
=
new
StringType
();
private
final
String
fileName
;
private
final
DataTypeFactory
type
Factory
;
private
final
MapFactory
map
Factory
;
private
int
readCacheSize
=
2
*
1024
*
1024
;
...
...
@@ -110,9 +111,9 @@ public class BtreeMapStore {
private
int
readCount
;
private
int
writeCount
;
private
BtreeMapStore
(
String
fileName
,
DataTypeFactory
type
Factory
)
{
private
BtreeMapStore
(
String
fileName
,
MapFactory
map
Factory
)
{
this
.
fileName
=
fileName
;
this
.
typeFactory
=
type
Factory
;
this
.
mapFactory
=
map
Factory
;
}
/**
...
...
@@ -129,11 +130,11 @@ public class BtreeMapStore {
* Open a tree store.
*
* @param fileName the file name
* @param
typeFactory the type
factory
* @param
mapFactory the map
factory
* @return the store
*/
public
static
BtreeMapStore
open
(
String
fileName
,
DataTypeFactory
type
Factory
)
{
BtreeMapStore
s
=
new
BtreeMapStore
(
fileName
,
type
Factory
);
public
static
BtreeMapStore
open
(
String
fileName
,
MapFactory
map
Factory
)
{
BtreeMapStore
s
=
new
BtreeMapStore
(
fileName
,
map
Factory
);
s
.
open
();
return
s
;
}
...
...
@@ -144,12 +145,13 @@ public class BtreeMapStore {
* @param <K> the key type
* @param <V> the value type
* @param name the name of the map
* @param mapType the map type
* @param keyType the key type
* @param valueType the value type
* @return the map
*/
public
<
K
,
V
>
BtreeMap
<
K
,
V
>
openMap
(
String
name
,
DataType
keyType
,
DataType
valueType
)
{
@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
);
if
(
m
==
null
)
{
String
identifier
=
meta
.
get
(
"map."
+
name
);
...
...
@@ -158,21 +160,28 @@ public class BtreeMapStore {
long
createVersion
;
if
(
identifier
==
null
)
{
id
=
++
lastMapId
;
String
types
=
id
+
"/"
+
currentVersion
+
"/"
+
keyType
.
asString
()
+
"/"
+
valueType
.
asString
();
createVersion
=
currentVersion
;
String
types
=
id
+
"/"
+
createVersion
+
"/"
+
mapType
+
"/"
+
keyType
+
"/"
+
valueType
;
meta
.
put
(
"map."
+
name
,
types
);
root
=
0
;
createVersion
=
currentVersion
;
}
else
{
String
types
=
meta
.
get
(
"map."
+
name
);
String
[]
idTypeList
=
StringUtils
.
arraySplit
(
types
,
'/'
,
false
);
id
=
Integer
.
parseInt
(
idTypeList
[
0
]);
createVersion
=
Long
.
parseLong
(
idTypeList
[
1
]);
keyType
=
getDataType
(
idTypeList
[
2
]);
valueType
=
getDataType
(
idTypeList
[
3
]);
mapType
=
idTypeList
[
2
];
keyType
=
idTypeList
[
3
];
valueType
=
idTypeList
[
4
];
String
r
=
meta
.
get
(
"root."
+
id
);
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
);
m
.
setRootPos
(
root
);
}
...
...
@@ -223,9 +232,11 @@ public class BtreeMapStore {
* @return the map
*/
public
<
K
,
V
>
BtreeMap
<
K
,
V
>
openMap
(
String
name
,
Class
<
K
>
keyClass
,
Class
<
V
>
valueClass
)
{
DataType
keyType
=
getDataType
(
keyClass
);
DataType
valueType
=
getDataType
(
valueClass
);
return
openMap
(
name
,
keyType
,
valueType
);
String
keyType
=
getDataType
(
keyClass
);
String
valueType
=
getDataType
(
valueClass
);
@SuppressWarnings
(
"unchecked"
)
BtreeMap
<
K
,
V
>
m
=
(
BtreeMap
<
K
,
V
>)
openMap
(
name
,
""
,
keyType
,
valueType
);
return
m
;
}
void
removeMap
(
String
name
)
{
...
...
@@ -233,25 +244,25 @@ public class BtreeMapStore {
mapsChanged
.
remove
(
m
);
}
private
DataType
getDataType
(
Class
<?>
clazz
)
{
private
String
getDataType
(
Class
<?>
clazz
)
{
if
(
clazz
==
String
.
class
)
{
return
STRING_TYPE
;
return
""
;
}
return
get
Type
Factory
().
getDataType
(
clazz
);
return
get
Map
Factory
().
getDataType
(
clazz
);
}
private
DataType
getDataType
(
String
s
)
{
if
(
s
.
equals
(
""
))
{
private
DataType
buildDataType
(
String
dataType
)
{
if
(
dataType
.
equals
(
""
))
{
return
STRING_TYPE
;
}
return
get
TypeFactory
().
fromString
(
s
);
return
get
MapFactory
().
buildDataType
(
dataType
);
}
private
DataTypeFactory
getType
Factory
()
{
if
(
type
Factory
==
null
)
{
throw
new
RuntimeException
(
"No
data type
factory set"
);
private
MapFactory
getMap
Factory
()
{
if
(
map
Factory
==
null
)
{
throw
new
RuntimeException
(
"No factory set"
);
}
return
type
Factory
;
return
map
Factory
;
}
/**
...
...
@@ -657,6 +668,7 @@ public class BtreeMapStore {
if
(
percentTotal
>
fillRate
)
{
return
false
;
}
// calculate how many entries a chunk has on average
// TODO use the max size instead of the count
int
averageEntryCount
=
(
int
)
(
entryCountTotal
/
chunks
.
size
());
...
...
@@ -664,20 +676,27 @@ public class BtreeMapStore {
// the 'old' list contains the chunks we want to free up
ArrayList
<
Chunk
>
old
=
New
.
arrayList
();
for
(
Chunk
c
:
chunks
.
values
())
{
if
(
retainChunk
==
-
1
||
c
.
id
<
retainChunk
)
{
int
age
=
lastChunkId
-
c
.
id
+
1
;
c
.
collectPriority
=
c
.
getFillRate
()
/
age
;
old
.
add
(
c
);
}
}
if
(
old
.
size
()
==
0
)
{
return
false
;
}
// sort the list, so the first entry should be collected first
Collections
.
sort
(
old
,
new
Comparator
<
Chunk
>()
{
public
int
compare
(
Chunk
o1
,
Chunk
o2
)
{
return
new
Integer
(
o1
.
collectPriority
).
compareTo
(
o2
.
collectPriority
);
}
});
int
moveCount
=
0
;
Chunk
move
=
null
;
// find out up to were we need to move
// try to move one (average sized) chunk
int
moveCount
=
0
;
Chunk
move
=
null
;
for
(
Chunk
c
:
old
)
{
if
(
moveCount
+
c
.
liveCount
>
averageEntryCount
)
{
break
;
...
...
@@ -711,39 +730,39 @@ public class BtreeMapStore {
}
}
}
// the metaRootPos might not be set
move
=
readChunkHeader
(
move
.
start
);
log
(
" meta:"
+
move
.
id
+
"/"
+
move
.
metaRootPos
+
" start: "
+
move
.
start
);
// change at least one entry in the map
// to ensure a chunk will be written
// (even if there is nothing to move)
meta
.
put
(
"chunk."
+
move
.
id
,
move
.
toString
());
BtreeMap
<
String
,
String
>
oldMeta
=
new
BtreeMap
<
String
,
String
>(
this
,
0
,
"old-meta"
,
STRING_TYPE
,
STRING_TYPE
,
0
);
oldMeta
.
setRootPos
(
move
.
metaRootPos
);
Iterator
<
String
>
it
=
oldMeta
.
keyIterator
(
null
);
Iterator
<
String
>
it
=
oldMeta
.
keyIterator
(
"map."
);
while
(
it
.
hasNext
())
{
String
k
=
it
.
next
();
String
s
=
oldMeta
.
get
(
k
);
log
(
" "
+
k
+
" "
+
s
.
replace
(
'\n'
,
' '
));
if
(!
k
.
startsWith
(
"map."
))
{
continue
;
break
;
}
String
s
=
oldMeta
.
get
(
k
);
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
;
}
String
[]
idTypesList
=
StringUtils
.
arraySplit
(
s
,
'/'
,
false
);
int
id
=
Integer
.
parseInt
(
idTypesList
[
0
]);
DataType
kt
=
getDataType
(
idTypesList
[
2
]);
DataType
vt
=
getDataType
(
idTypesList
[
3
]);
log
(
" "
+
k
+
" "
+
s
.
replace
(
'\n'
,
' '
));
String
[]
idTypeList
=
StringUtils
.
arraySplit
(
s
,
'/'
,
false
);
int
id
=
Integer
.
parseInt
(
idTypeList
[
0
]);
DataType
kt
=
buildDataType
(
idTypeList
[
3
]);
DataType
vt
=
buildDataType
(
idTypeList
[
4
]);
long
oldDataRoot
=
Long
.
parseLong
(
oldMeta
.
get
(
"root."
+
id
));
if
(
oldDataRoot
!=
0
)
{
BtreeMap
<?,
?>
oldData
=
new
BtreeMap
<
Object
,
Object
>(
this
,
id
,
"old-"
+
k
,
kt
,
vt
,
0
);
if
(
oldDataRoot
==
0
)
{
// no rows
}
else
{
oldData
.
setRootPos
(
oldDataRoot
);
@SuppressWarnings
(
"unchecked"
)
BtreeMap
<
Object
,
Object
>
data
=
(
BtreeMap
<
Object
,
Object
>)
maps
.
get
(
k
);
Iterator
<?>
dataIt
=
oldData
.
keyIterator
(
null
);
while
(
dataIt
.
hasNext
())
{
Object
o
=
dataIt
.
next
();
...
...
@@ -952,7 +971,7 @@ public class BtreeMapStore {
}
}
for
(
BtreeMap
<?,
?>
m
:
maps
.
values
())
{
if
(
m
.
getCreate
d
Version
()
>
version
)
{
if
(
m
.
getCreateVersion
()
>
version
)
{
m
.
close
();
removeMap
(
m
.
getName
());
}
else
{
...
...
@@ -969,7 +988,7 @@ public class BtreeMapStore {
private
void
revertTemp
()
{
freedChunks
.
clear
();
for
(
BtreeMap
<?,
?>
m
:
mapsChanged
.
values
())
{
m
.
stored
();
m
.
revertTemp
();
}
mapsChanged
.
clear
();
temp
.
clear
();
...
...
h2/src/tools/org/h2/dev/store/btree/Cursor.java
浏览文件 @
32bae90e
...
...
@@ -13,14 +13,17 @@ import java.util.Iterator;
* A cursor to iterate over elements in ascending order.
*
* @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
;
Cursor
(
Page
root
,
K
from
)
{
Page
.
min
(
root
,
parents
,
from
);
Cursor
(
BtreeMap
<
K
,
V
>
map
,
Page
root
,
K
from
)
{
this
.
map
=
map
;
map
.
min
(
root
,
parents
,
from
);
fetchNext
();
}
...
...
@@ -34,7 +37,7 @@ class Cursor<K> implements Iterator<K> {
@SuppressWarnings
(
"unchecked"
)
private
void
fetchNext
()
{
current
=
(
K
)
Page
.
nextKey
(
parents
);
current
=
(
K
)
map
.
nextKey
(
parents
);
}
public
boolean
hasNext
()
{
...
...
h2/src/tools/org/h2/dev/store/btree/DataType.java
浏览文件 @
32bae90e
...
...
@@ -64,9 +64,10 @@ public interface DataType {
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
();
...
...
h2/src/tools/org/h2/dev/store/btree/
DataType
Factory.java
→
h2/src/tools/org/h2/dev/store/btree/
Map
Factory.java
浏览文件 @
32bae90e
...
...
@@ -9,15 +9,31 @@ package org.h2.dev.store.btree;
/**
* A factory for data types.
*/
public
interface
DataType
Factory
{
public
interface
Map
Factory
{
/**
*
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
*/
DataType
fromString
(
String
s
);
DataType
buildDataType
(
String
dataType
);
/**
* Get the data type object for the given class.
...
...
@@ -25,6 +41,6 @@ public interface DataTypeFactory {
* @param objectClass the class
* @return the data type object
*/
DataType
getDataType
(
Class
<?>
objectClass
);
String
getDataType
(
Class
<?>
objectClass
);
}
h2/src/tools/org/h2/dev/store/btree/Page.java
浏览文件 @
32bae90e
...
...
@@ -9,13 +9,12 @@ package org.h2.dev.store.btree;
import
java.io.IOException
;
import
java.nio.ByteBuffer
;
import
java.nio.channels.FileChannel
;
import
java.util.ArrayList
;
import
org.h2.compress.Compressor
;
/**
* A
btree
page (a node or a leaf).
* A page (a node or a leaf).
* <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.
* <p>
* File format: page length (including length): int check value: short map id:
...
...
@@ -25,11 +24,6 @@ import org.h2.compress.Compressor;
*/
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
long
version
;
private
long
pos
;
...
...
@@ -101,15 +95,33 @@ public class Page {
return
p
;
}
private
Page
copyOnWrite
(
long
writeVersion
)
{
if
(
version
==
writeVersion
)
{
return
this
;
Object
getKey
(
int
index
)
{
return
keys
[
index
];
}
getStore
().
removePage
(
pos
);
Page
newPage
=
create
(
map
,
writeVersion
,
keys
,
values
,
children
,
childrenSize
,
totalSize
);
newPage
.
cachedCompare
=
cachedCompare
;
return
newPage
;
Page
getChildPage
(
int
index
)
{
return
map
.
readPage
(
children
[
index
]);
}
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
()
{
...
...
@@ -133,62 +145,28 @@ public class Page {
return
buff
.
toString
();
}
/**
* Get the position of the page
*
* @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
];
Page
copyOnWrite
(
long
writeVersion
)
{
if
(
version
==
writeVersion
)
{
return
this
;
}
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
* @return the
pag
e or null
* @return the
valu
e or null
*/
Page
findPage
(
Object
key
)
{
int
x
=
findKey
(
key
);
if
(
children
!=
null
)
{
if
(
x
<
0
)
{
x
=
-
x
-
1
;
}
else
{
x
++;
}
Page
p
=
map
.
readPage
(
children
[
x
]);
return
p
.
findPage
(
key
);
}
if
(
x
>=
0
)
{
return
this
;
}
return
null
;
}
private
int
findKey
(
Object
key
)
{
int
binarySearch
(
Object
key
)
{
int
low
=
0
,
high
=
keys
.
length
-
1
;
int
x
=
cachedCompare
-
1
;
if
(
x
<
0
||
x
>
high
)
{
...
...
@@ -225,85 +203,7 @@ public class Page {
// return -(low + 1);
}
/**
* 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
)
{
Page
split
(
int
at
)
{
return
isLeaf
()
?
splitLeaf
(
at
)
:
splitNode
(
at
);
}
...
...
@@ -357,118 +257,6 @@ public class Page {
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
()
{
if
(
BtreeMapStore
.
ASSERT
)
{
long
check
=
0
;
...
...
@@ -487,7 +275,7 @@ public class Page {
return
totalSize
;
}
private
void
setChild
(
int
index
,
long
pos
,
long
childSize
)
{
void
setChild
(
int
index
,
long
pos
,
long
childSize
)
{
if
(
pos
!=
children
[
index
])
{
long
[]
newChildren
=
new
long
[
children
.
length
];
System
.
arraycopy
(
children
,
0
,
newChildren
,
0
,
newChildren
.
length
);
...
...
@@ -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,
// but avoid unnecessary cloning would require a "modified" flag
Object
[]
newValues
=
new
Object
[
values
.
length
];
...
...
@@ -521,65 +309,16 @@ public class Page {
for
(
long
c
:
children
)
{
int
type
=
DataUtils
.
getPageType
(
c
);
if
(
type
==
DataUtils
.
PAGE_TYPE_LEAF
)
{
getStore
().
removePage
(
c
);
map
.
getStore
().
removePage
(
c
);
}
else
{
map
.
readPage
(
c
).
removeAllRecursive
();
}
}
}
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
;
map
.
getStore
().
removePage
(
pos
);
}
private
void
insert
(
int
index
,
Object
key
,
Object
value
,
long
child
,
void
insert
(
int
index
,
Object
key
,
Object
value
,
long
child
,
long
childSize
)
{
Object
[]
newKeys
=
new
Object
[
keys
.
length
+
1
];
DataUtils
.
copyWithGap
(
keys
,
newKeys
,
keys
.
length
,
index
);
...
...
@@ -607,7 +346,7 @@ public class Page {
}
}
private
void
remove
(
int
index
)
{
void
remove
(
int
index
)
{
Object
[]
newKeys
=
new
Object
[
keys
.
length
-
1
];
int
keyIndex
=
index
>=
keys
.
length
?
index
-
1
:
index
;
DataUtils
.
copyExcept
(
keys
,
newKeys
,
keys
.
length
,
keyIndex
);
...
...
@@ -644,16 +383,14 @@ public class Page {
throw
new
RuntimeException
(
"Error reading page, expected map "
+
map
.
getId
()
+
" got "
+
mapId
);
}
int
len
=
DataUtils
.
readVarInt
(
buff
);
int
checkTest
=
DataUtils
.
getCheckValue
(
chunkId
)
^
DataUtils
.
getCheckValue
(
map
.
getId
())
^
DataUtils
.
getCheckValue
(
offset
)
^
DataUtils
.
getCheckValue
(
pageLength
)
^
DataUtils
.
getCheckValue
(
len
);
^
DataUtils
.
getCheckValue
(
pageLength
);
if
(
check
!=
(
short
)
checkTest
)
{
throw
new
RuntimeException
(
"Error in check value, expected "
+
checkTest
+
" got "
+
check
);
}
int
len
=
DataUtils
.
readVarInt
(
buff
);
keys
=
new
Object
[
len
];
int
type
=
buff
.
get
();
boolean
node
=
(
type
&
1
)
==
DataUtils
.
PAGE_TYPE_NODE
;
...
...
@@ -743,10 +480,8 @@ public class Page {
int
pageLength
=
buff
.
position
()
-
start
;
buff
.
putInt
(
start
,
pageLength
);
int
check
=
DataUtils
.
getCheckValue
(
chunkId
)
^
DataUtils
.
getCheckValue
(
map
.
getId
())
^
DataUtils
.
getCheckValue
(
start
)
^
DataUtils
.
getCheckValue
(
pageLength
)
^
DataUtils
.
getCheckValue
(
len
);
^
DataUtils
.
getCheckValue
(
pageLength
);
buff
.
putShort
(
start
+
4
,
(
short
)
check
);
this
.
pos
=
DataUtils
.
getPagePos
(
chunkId
,
start
,
pageLength
,
type
);
}
...
...
@@ -823,12 +558,4 @@ public class Page {
return
count
;
}
BtreeMapStore
getStore
()
{
return
map
.
getStore
();
}
long
getVersion
()
{
return
version
;
}
}
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论