com.bigdata.btree.MutableLeafData Maven / Gradle / Ivy
/*
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
[email protected]
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Aug 25, 2009
*/
package com.bigdata.btree;
import it.unimi.dsi.bits.BitVector;
import com.bigdata.btree.data.ILeafData;
import com.bigdata.btree.raba.IRaba;
import com.bigdata.btree.raba.MutableKeyBuffer;
import com.bigdata.btree.raba.MutableValueBuffer;
import com.bigdata.io.AbstractFixedByteArrayBuffer;
import com.bigdata.rawstore.IRawStore;
/**
* Implementation maintains Java objects corresponding to the persistent data
* and defines methods for a variety of mutations on the {@link ILeafData}
* record which operate by direct manipulation of the Java objects.
*
* Note: package private fields are used so that they may be directly accessed
* by the {@link Leaf} class.
*
* @author Bryan Thompson
* @version $Id$
*
* @todo consider mutable implementation based on a compacting record ala GOM.
*/
public class MutableLeafData implements ILeafData {
/**
* A representation of each key in the node or leaf. Each key is a variable
* length unsigned byte[]. There are various implementations of
* {@link IRaba} that are optimized for mutable and immutable keys.
*
* The #of keys depends on whether this is a {@link Node} or a {@link Leaf}.
* A leaf has one key per value - that is, the maximum #of keys for a leaf
* is specified by the branching factor. In contrast a node has m-1 keys
* where m is the maximum #of children (aka the branching factor).
*
* For both a {@link Node} and a {@link Leaf}, this array is dimensioned to
* accept one more key than the maximum capacity so that the key that causes
* overflow and forces the split may be inserted. This greatly simplifies
* the logic for computing the split point and performing the split.
* Therefore you always allocate this object with a capacity m
* keys for a {@link Node} and m+1
keys for a {@link Leaf}.
*
* @see IRaba#search(byte[])
*/
final MutableKeyBuffer keys;
/**
*
* The values of the tree. There is one value per key for a leaf.
*
*
* This array is dimensioned to one more than the maximum capacity so that
* the value corresponding to the key that causes overflow and forces the
* split may be inserted. This greatly simplifies the logic for computing
* the split point and performing the split.
*
*/
final MutableValueBuffer vals;
/**
* The deletion markers IFF isolation is supported by the {@link BTree}.
*
* @todo {@link BitVector}? The code in {@link Leaf} which makes changes to
* this array would have to be updated.
*/
final boolean[] deleteMarkers;
/**
* The version timestamps IFF isolation is supported by the {@link BTree}.
*/
final long[] versionTimestamps;
/**
* The minimum version timestamp.
*
* @todo these fields at 16 bytes to each {@link MutableLeafData} object
* even when we do not use them. It would be better to use a subclass
* or tack them onto the end of the {@link #versionTimestamps} array.
*/
long minimumVersionTimestamp;
long maximumVersionTimestamp;
/**
* Bit markers indicating whether the value associated with the tuple is a
* raw record or an inline byte[] stored within {@link #getValues() values
* raba}.
*
* @todo {@link BitVector}? The code in {@link Leaf} which makes changes to
* this array would have to be updated.
*/
final boolean[] rawRecords;
/**
* Create an empty data record with internal arrays dimensioned for the
* specified branching factor.
*
* @param branchingFactor
* The branching factor for the owning B+Tree.
* @param hasVersionTimestamps
* true
iff version timestamps will be maintained.
* @param hasDeleteMarkers
* true
iff delete markers will be maintained.
* @param hasRawRecords
* true
iff raw record markers will be maintained.
*/
public MutableLeafData(final int branchingFactor,
final boolean hasVersionTimestamps, final boolean hasDeleteMarkers,
final boolean hasRawRecords) {
keys = new MutableKeyBuffer(branchingFactor + 1);
vals = new MutableValueBuffer(branchingFactor + 1);
versionTimestamps = (hasVersionTimestamps ? new long[branchingFactor + 1]
: null);
// init per API specification.
minimumVersionTimestamp = Long.MAX_VALUE;
maximumVersionTimestamp = Long.MIN_VALUE;
deleteMarkers = (hasDeleteMarkers ? new boolean[branchingFactor + 1]
: null);
rawRecords = (hasRawRecords ? new boolean[branchingFactor + 1] : null);
}
/**
* Copy ctor.
*
* @param branchingFactor
* The branching factor for the owning B+Tree.
* @param src
* The source leaf.
*/
public MutableLeafData(final int branchingFactor, final ILeafData src) {
keys = new MutableKeyBuffer(branchingFactor + 1, src.getKeys());
vals = new MutableValueBuffer(branchingFactor + 1, src.getValues());
versionTimestamps = (src.hasVersionTimestamps() ? new long[branchingFactor + 1]
: null);
deleteMarkers = (src.hasDeleteMarkers() ? new boolean[branchingFactor + 1]
: null);
rawRecords = (src.hasRawRecords() ? new boolean[branchingFactor + 1]
: null);
final int nkeys = keys.size();
if (versionTimestamps != null) {
for (int i = 0; i < nkeys; i++) {
versionTimestamps[i] = src.getVersionTimestamp(i);
}
minimumVersionTimestamp = src.getMinimumVersionTimestamp();
maximumVersionTimestamp = src.getMaximumVersionTimestamp();
} else {
minimumVersionTimestamp = Long.MAX_VALUE;
maximumVersionTimestamp = Long.MIN_VALUE;
}
if (deleteMarkers != null) {
for (int i = 0; i < nkeys; i++) {
deleteMarkers[i] = src.getDeleteMarker(i);
}
}
if (rawRecords != null) {
for (int i = 0; i < nkeys; i++) {
rawRecords[i] = src.getRawRecord(i) != IRawStore.NULL;
}
}
}
/**
* Ctor based on just "data" -- used by unit tests.
*
* @param keys
* A representation of the defined keys in the leaf.
* @param values
* An array containing the values found in the leaf.
* @param versionTimestamps
* An array of the version timestamps (iff the version metadata
* is being maintained).
* @param deleteMarkers
* An array of the delete markers (iff the version metadata is
* being maintained).
*/
MutableLeafData(final MutableKeyBuffer keys,
final MutableValueBuffer values, final long[] versionTimestamps,
final boolean[] deleteMarkers, final boolean[] rawRecords) {
assert keys != null;
assert values != null;
assert keys.capacity() == values.capacity();
if (versionTimestamps != null) {
assert versionTimestamps.length == keys.capacity();
}
if (deleteMarkers != null) {
assert deleteMarkers.length == keys.capacity();
}
if (rawRecords != null) {
assert rawRecords.length == keys.capacity();
}
this.keys = keys;
this.vals = values;
this.versionTimestamps = versionTimestamps;
this.deleteMarkers = deleteMarkers;
this.rawRecords = rawRecords;
if (versionTimestamps != null)
recalcMinMaxVersionTimestamp();
}
/**
* Range check a tuple index.
*
* @param index
* The index of a tuple in [0:nkeys].
* @return true
*
* @throws IndexOutOfBoundsException
* if the index is not in the legal range.
*/
final protected boolean rangeCheckTupleIndex(final int index) {
if (index < 0 || index > getKeys().size())
throw new IndexOutOfBoundsException();
return true;
}
/**
* No - this is a mutable data record.
*/
final public boolean isReadOnly() {
return false;
}
/**
* No.
*/
final public boolean isCoded() {
return false;
}
final public AbstractFixedByteArrayBuffer data() {
throw new UnsupportedOperationException();
}
public final long getVersionTimestamp(final int index) {
if (versionTimestamps == null)
throw new UnsupportedOperationException();
assert rangeCheckTupleIndex(index);
return versionTimestamps[index];
}
final public long getMinimumVersionTimestamp() {
if (versionTimestamps == null)
throw new UnsupportedOperationException();
return minimumVersionTimestamp;
}
final public long getMaximumVersionTimestamp() {
if (versionTimestamps == null)
throw new UnsupportedOperationException();
return maximumVersionTimestamp;
}
public final boolean getDeleteMarker(final int index) {
if (deleteMarkers == null)
throw new UnsupportedOperationException();
assert rangeCheckTupleIndex(index);
return deleteMarkers[index];
}
public final long getRawRecord(final int index) {
if (rawRecords == null)
throw new UnsupportedOperationException();
assert rangeCheckTupleIndex(index);
if (!rawRecords[index])
return IRawStore.NULL;
final byte[] val = vals.get(index);
final long addr = AbstractBTree.decodeRecordAddr(val);
return addr;
}
final public IRaba getValues() {
return vals;
}
final public IRaba getKeys() {
return keys;
}
/**
* Always returns true
.
*/
final public boolean isLeaf() {
return true;
}
// /**
// * For a leaf the #of entries is always the #of keys.
// */
// final public int getSpannedTupleCount() {
//
// return getKeys().size();
//
// }
final public int getValueCount() {
return vals.size();
}
final public boolean hasRawRecords() {
return rawRecords != null;
}
final public boolean hasDeleteMarkers() {
return deleteMarkers != null;
}
final public boolean hasVersionTimestamps() {
return versionTimestamps != null;
}
final public int getKeyCount() {
return keys.size();
}
/**
* No - this class does not support double-linked leaves (only the
* {@link IndexSegment} actually uses double-linked leaves).
*/
final public boolean isDoubleLinked() {
return false;
}
final public long getNextAddr() {
throw new UnsupportedOperationException();
}
final public long getPriorAddr() {
throw new UnsupportedOperationException();
}
/**
* Recalculate the min/max version timestamp on the leaf. The caller is
* responsible for propagating the new min/max to the ancestors of the leaf.
*
* @throws UnsupportedOperationException
* if the leaf is not maintaining per-tuple version timestamps.
*/
void recalcMinMaxVersionTimestamp() {
// must be maintaining version timestamps.
if (versionTimestamps == null)
throw new UnsupportedOperationException();
final int nkeys = keys.nkeys;
long min = Long.MAX_VALUE;
long max = Long.MIN_VALUE;
for (int i = 0; i < nkeys; i++) {
final long t = versionTimestamps[i];
if (t < min)
min = t;
if (t > max)
max = t;
}
minimumVersionTimestamp = min;
maximumVersionTimestamp = max;
}
}