Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
affe082
commit c428009
Showing
29 changed files
with
2,120 additions
and
1,322 deletions.
There are no files selected for viewing
370 changes: 370 additions & 0 deletions
370
lealone-aose/src/main/java/org/lealone/storage/aose/btree/BTreeLeafPage.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,370 @@ | |||
/* | |||
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, and the | |||
* EPL 1.0 (http://h2database.com/html/license.html). Initial Developer: H2 | |||
* Group | |||
*/ | |||
package org.lealone.storage.aose.btree; | |||
|
|||
import java.nio.ByteBuffer; | |||
import java.util.List; | |||
import java.util.Set; | |||
|
|||
import org.lealone.common.util.DataUtils; | |||
import org.lealone.db.DataBuffer; | |||
import org.lealone.net.NetEndpoint; | |||
import org.lealone.storage.LeafPageMovePlan; | |||
import org.lealone.storage.type.StorageDataType; | |||
|
|||
/** | |||
* A leaf page. | |||
* | |||
* @author H2 Group | |||
* @author zhh | |||
*/ | |||
public class BTreeLeafPage extends BTreeLocalPage { | |||
|
|||
/** | |||
* The values. | |||
* <p> | |||
* The array might be larger than needed, to avoid frequent re-sizing. | |||
*/ | |||
private Object[] values; | |||
|
|||
private List<String> replicationHostIds; | |||
private LeafPageMovePlan leafPageMovePlan; | |||
|
|||
BTreeLeafPage(BTreeMap<?, ?> map) { | |||
super(map); | |||
} | |||
|
|||
@Override | |||
public boolean isLeaf() { | |||
return true; | |||
} | |||
|
|||
@Override | |||
public List<String> getReplicationHostIds() { | |||
return replicationHostIds; | |||
} | |||
|
|||
@Override | |||
public void setReplicationHostIds(List<String> replicationHostIds) { | |||
this.replicationHostIds = replicationHostIds; | |||
} | |||
|
|||
@Override | |||
public LeafPageMovePlan getLeafPageMovePlan() { | |||
return leafPageMovePlan; | |||
} | |||
|
|||
@Override | |||
public void setLeafPageMovePlan(LeafPageMovePlan leafPageMovePlan) { | |||
this.leafPageMovePlan = leafPageMovePlan; | |||
} | |||
|
|||
@Override | |||
public Object getValue(int index) { | |||
return values[index]; | |||
} | |||
|
|||
@Override | |||
public Object setValue(int index, Object value) { | |||
Object old = values[index]; | |||
// this is slightly slower: | |||
// values = Arrays.copyOf(values, values.length); | |||
values = values.clone(); | |||
StorageDataType valueType = map.getValueType(); | |||
addMemory(valueType.getMemory(value) - valueType.getMemory(old)); | |||
values[index] = value; | |||
return old; | |||
} | |||
|
|||
@Override | |||
BTreeLeafPage split(int at) { // 小于split key的放在左边,大于等于split key放在右边 | |||
int a = at, b = keys.length - a; | |||
Object[] aKeys = new Object[a]; | |||
Object[] bKeys = new Object[b]; | |||
System.arraycopy(keys, 0, aKeys, 0, a); | |||
System.arraycopy(keys, a, bKeys, 0, b); | |||
keys = aKeys; | |||
Object[] aValues = new Object[a]; | |||
Object[] bValues = new Object[b]; | |||
System.arraycopy(values, 0, aValues, 0, a); | |||
System.arraycopy(values, a, bValues, 0, b); | |||
values = aValues; | |||
totalCount = a; | |||
BTreeLeafPage newPage = create(map, bKeys, bValues, bKeys.length, 0); | |||
newPage.replicationHostIds = replicationHostIds; | |||
recalculateMemory(); | |||
return newPage; | |||
} | |||
|
|||
@Override | |||
public long getTotalCount() { | |||
if (ASSERT) { | |||
long check = keys.length; | |||
if (check != totalCount) { | |||
throw DataUtils.newIllegalStateException(DataUtils.ERROR_INTERNAL, "Expected: {0} got: {1}", check, | |||
totalCount); | |||
} | |||
} | |||
return totalCount; | |||
} | |||
|
|||
@Override | |||
public void insertLeaf(int index, Object key, Object value) { | |||
int len = keys.length + 1; | |||
Object[] newKeys = new Object[len]; | |||
DataUtils.copyWithGap(keys, newKeys, len - 1, index); | |||
keys = newKeys; | |||
Object[] newValues = new Object[len]; | |||
DataUtils.copyWithGap(values, newValues, len - 1, index); | |||
values = newValues; | |||
keys[index] = key; | |||
values[index] = value; | |||
totalCount++; | |||
addMemory(map.getKeyType().getMemory(key) + map.getValueType().getMemory(value)); | |||
} | |||
|
|||
@Override | |||
public void remove(int index) { | |||
int keyLength = keys.length; | |||
super.remove(index); | |||
if (values != null) { | |||
Object old = values[index]; | |||
addMemory(-map.getValueType().getMemory(old)); | |||
Object[] newValues = new Object[keyLength - 1]; | |||
DataUtils.copyExcept(values, newValues, keyLength, index); | |||
values = newValues; | |||
totalCount--; | |||
} | |||
} | |||
|
|||
@Override | |||
void read(ByteBuffer buff, int chunkId, int offset, int maxLength, boolean isLeaf) { | |||
int start = buff.position(); | |||
int pageLength = buff.getInt(); | |||
checkPageLength(chunkId, pageLength, maxLength); | |||
|
|||
int oldLimit = buff.limit(); | |||
buff.limit(start + pageLength); | |||
|
|||
if (isLeaf) { | |||
int keyLength = buff.getInt(); | |||
if (keyLength != 0) { | |||
map.getKeyType().read(buff); // read first key | |||
} | |||
} | |||
|
|||
readCheckValue(buff, chunkId, offset, pageLength); | |||
|
|||
int keyLength = DataUtils.readVarInt(buff); | |||
keys = new Object[keyLength]; | |||
int type = buff.get(); | |||
|
|||
buff = expandPage(buff, type, start, pageLength); | |||
|
|||
map.getKeyType().read(buff, keys, keyLength); | |||
values = new Object[keyLength]; | |||
map.getValueType().read(buff, values, keyLength); | |||
totalCount = keyLength; | |||
replicationHostIds = readReplicationHostIds(buff); | |||
recalculateMemory(); | |||
buff.limit(oldLimit); | |||
} | |||
|
|||
@Override | |||
void writeLeaf(DataBuffer buff, boolean remote) { | |||
buff.put((byte) PageUtils.PAGE_TYPE_LEAF); | |||
writeReplicationHostIds(replicationHostIds, buff); | |||
buff.put((byte) (remote ? 1 : 0)); | |||
if (!remote) { | |||
StorageDataType kt = map.getKeyType(); | |||
StorageDataType vt = map.getValueType(); | |||
buff.putInt(keys.length); | |||
for (int i = 0; i < keys.length; i++) { | |||
kt.write(buff, keys[i]); | |||
vt.write(buff, values[i]); | |||
} | |||
} | |||
} | |||
|
|||
static BTreeLeafPage readLeafPage(BTreeMap<?, ?> map, ByteBuffer page) { | |||
List<String> replicationHostIds = readReplicationHostIds(page); | |||
boolean remote = page.get() == 1; | |||
BTreeLeafPage p; | |||
|
|||
if (remote) { | |||
p = BTreeLeafPage.createEmpty(map); | |||
} else { | |||
StorageDataType kt = map.getKeyType(); | |||
StorageDataType vt = map.getValueType(); | |||
int length = page.getInt(); | |||
Object[] keys = new Object[length]; | |||
Object[] values = new Object[length]; | |||
for (int i = 0; i < length; i++) { | |||
keys[i] = kt.read(page); | |||
values[i] = vt.read(page); | |||
} | |||
p = BTreeLeafPage.create(map, keys, values, length, 0); | |||
} | |||
|
|||
p.replicationHostIds = replicationHostIds; | |||
return p; | |||
} | |||
|
|||
@Override | |||
int write(BTreeChunk chunk, DataBuffer buff, boolean replicatePage) { | |||
int start = buff.position(); | |||
int keyLength = keys.length; | |||
int type = PageUtils.PAGE_TYPE_LEAF; | |||
buff.putInt(0); | |||
// write first key | |||
buff.putInt(keyLength); | |||
if (keyLength != 0) { | |||
map.getKeyType().write(buff, keys[0]); | |||
} | |||
// 不能这样,ValueNull.INSTANCE不会自动转成其他类型 | |||
// if (keyLength == 0) { | |||
// map.getKeyType().write(buff, ValueNull.INSTANCE); | |||
// } else { | |||
// map.getKeyType().write(buff, keys[0]); | |||
// } | |||
int checkPos = buff.position(); | |||
buff.putShort((short) 0).putVarInt(keyLength); | |||
int typePos = buff.position(); | |||
buff.put((byte) type); | |||
int compressStart = buff.position(); | |||
map.getKeyType().write(buff, keys, keyLength); | |||
map.getValueType().write(buff, values, keyLength); | |||
writeReplicationHostIds(replicationHostIds, buff); | |||
|
|||
compressPage(buff, compressStart, type, typePos); | |||
|
|||
int pageLength = buff.position() - start; | |||
buff.putInt(start, pageLength); | |||
int chunkId = chunk.id; | |||
|
|||
writeCheckValue(buff, chunkId, start, pageLength, checkPos); | |||
|
|||
if (replicatePage) { | |||
return typePos + 1; | |||
} | |||
|
|||
updateChunkAndCachePage(chunk, start, pageLength, type); | |||
|
|||
if (removedInMemory) { | |||
// if the page was removed _before_ the position was assigned, we | |||
// need to mark it removed here, so the fields are updated | |||
// when the next chunk is stored | |||
map.getBTreeStorage().removePage(pos, memory); | |||
} | |||
return typePos + 1; | |||
} | |||
|
|||
@Override | |||
void writeUnsavedRecursive(BTreeChunk chunk, DataBuffer buff) { | |||
if (pos != 0) { | |||
// already stored before | |||
return; | |||
} | |||
write(chunk, buff, false); | |||
} | |||
|
|||
@Override | |||
protected void recalculateMemory() { | |||
int mem = recalculateKeysMemory(); | |||
StorageDataType valueType = map.getValueType(); | |||
for (int i = 0; i < keys.length; i++) { | |||
mem += valueType.getMemory(values[i]); | |||
} | |||
addMemory(mem - memory); | |||
} | |||
|
|||
@Override | |||
public BTreeLeafPage copy() { | |||
return copy(true); | |||
} | |||
|
|||
private BTreeLeafPage copy(boolean removePage) { | |||
BTreeLeafPage newPage = create(map, keys, values, totalCount, getMemory()); | |||
newPage.cachedCompare = cachedCompare; | |||
newPage.replicationHostIds = replicationHostIds; | |||
newPage.leafPageMovePlan = leafPageMovePlan; | |||
if (removePage) { | |||
// mark the old as deleted | |||
removePage(); | |||
} | |||
return newPage; | |||
} | |||
|
|||
@Override | |||
void removeAllRecursive() { | |||
removePage(); | |||
} | |||
|
|||
/** | |||
* Create a new, empty page. | |||
* | |||
* @param map the map | |||
* @return the new page | |||
*/ | |||
static BTreeLeafPage createEmpty(BTreeMap<?, ?> map) { | |||
return create(map, EMPTY_OBJECT_ARRAY, EMPTY_OBJECT_ARRAY, 0, PageUtils.PAGE_MEMORY); | |||
} | |||
|
|||
static BTreeLeafPage create(BTreeMap<?, ?> map, Object[] keys, Object[] values, long totalCount, int memory) { | |||
BTreeLeafPage p = new BTreeLeafPage(map); | |||
// the position is 0 | |||
p.keys = keys; | |||
p.values = values; | |||
p.totalCount = totalCount; | |||
if (memory == 0) { | |||
p.recalculateMemory(); | |||
} else { | |||
p.addMemory(memory); | |||
} | |||
return p; | |||
} | |||
|
|||
@Override | |||
void moveAllLocalLeafPages(String[] oldEndpoints, String[] newEndpoints) { | |||
Set<NetEndpoint> candidateEndpoints = BTreeMap.getCandidateEndpoints(map.db, newEndpoints); | |||
map.replicateOrMovePage(null, null, this, 0, oldEndpoints, false, candidateEndpoints); | |||
} | |||
|
|||
@Override | |||
void replicatePage(DataBuffer buff, NetEndpoint localEndpoint) { | |||
BTreeLeafPage p = copy(false); | |||
BTreeChunk chunk = new BTreeChunk(0); | |||
buff.put((byte) PageUtils.PAGE_TYPE_LEAF); | |||
p.write(chunk, buff, true); | |||
} | |||
|
|||
@Override | |||
protected void toString0(StringBuilder buff) { | |||
for (int i = 0; i <= keys.length; i++) { | |||
if (i > 0) { | |||
buff.append(" "); | |||
} | |||
if (i < keys.length) { | |||
buff.append(keys[i]); | |||
if (values != null) { | |||
buff.append(':'); | |||
buff.append(values[i]); | |||
} | |||
} | |||
} | |||
} | |||
|
|||
@Override | |||
protected void getPrettyPageInfoRecursive(StringBuilder buff, String indent, PrettyPageInfo info) { | |||
buff.append(indent).append("values: "); | |||
for (int i = 0; i < keys.length; i++) { | |||
if (i > 0) | |||
buff.append(", "); | |||
buff.append(values[i]); | |||
} | |||
buff.append('\n'); | |||
} | |||
} |
Oops, something went wrong.