com.bigdata.htree.data.DefaultDirectoryPageCoder 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 28, 2009
*/
package com.bigdata.htree.data;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import com.bigdata.btree.data.AbstractReadOnlyNodeData;
import com.bigdata.btree.data.IAbstractNodeDataCoder;
import com.bigdata.io.AbstractFixedByteArrayBuffer;
import com.bigdata.io.DataOutputBuffer;
import com.bigdata.util.Bytes;
/**
* Default implementation for immutable {@link IDirectoryData} records.
*
* @author Bryan Thompson
* @version $Id: DefaultNodeCoder.java 4585 2011-06-01 13:42:56Z thompsonbry $
*
* TODO Support RWStore 4 byte native addrs.
*
* TODO Explicit test suite based on the B+Tree test suites.
*/
public class DefaultDirectoryPageCoder implements IAbstractNodeDataCoder,
Externalizable {
/**
* The initial version of the serialized representation of the
* {@link DefaultDirectoryPageCoder} class (versus the serialized
* representation of the node or leaf).
*/
private final static transient byte VERSION0 = 0x00;
public void readExternal(final ObjectInput in) throws IOException,
ClassNotFoundException {
final byte version = in.readByte();
switch(version) {
case VERSION0:
break;
default:
throw new IOException();
}
}
public void writeExternal(final ObjectOutput out) throws IOException {
out.write(VERSION0);
}
/** No. */
final public boolean isLeafDataCoder() {
return false;
}
/** Yes. */
public boolean isNodeDataCoder() {
return true;
}
// public String toString() {
//
// return super.toString() + "{keysCoder=" + keysCoder + "}";
//
// }
/**
* De-serialization ctor.
*/
public DefaultDirectoryPageCoder() {
}
public IDirectoryData decode(final AbstractFixedByteArrayBuffer data) {
return new ReadOnlyDirectoryData(data);
}
public IDirectoryData encodeLive(final IDirectoryData node, final DataOutputBuffer buf) {
if (node == null)
throw new IllegalArgumentException();
if (buf == null)
throw new IllegalArgumentException();
final short version = ReadOnlyDirectoryData.currentVersion;
// The byte offset of the start of the coded data in the buffer.
final int O_origin = buf.pos();
buf.putByte(ReadOnlyDirectoryData.NODE);
buf.putShort(version);
final boolean hasVersionTimestamps = node.hasVersionTimestamps();
short flags = 0;
if (hasVersionTimestamps) {
flags |= AbstractReadOnlyNodeData.FLAG_VERSION_TIMESTAMPS;
throw new UnsupportedOperationException();
}
if(node.isOverflowDirectory()) {
flags |= AbstractReadOnlyNodeData.FLAG_OVERFLOW_DIRECTORY;
}
buf.putShort(flags);
if (node.isOverflowDirectory()) {
final byte[] ok = node.getOverflowKey();
buf.putShort((short) ok.length);
buf.put(ok);
}
final int nchildren = node.getChildCount();
if (nchildren > Short.MAX_VALUE)
throw new UnsupportedOperationException();
buf.putShort((short) nchildren);
// childAddr[] : TODO code childAddr[] (if RWSTORE bit is set, covert to 4 byte native addr).
for (int i = 0; i < nchildren; i++) {
buf.putLong(node.getChildAddr(i));
}
// Slice onto the coded data record.
final AbstractFixedByteArrayBuffer slice = buf.slice(//
O_origin, buf.pos() - O_origin);
// Read-only coded IDataRecord.
return new ReadOnlyDirectoryData(slice);
}
public AbstractFixedByteArrayBuffer encode(final IDirectoryData node,
final DataOutputBuffer buf) {
return encodeLive(node, buf).data();
}
/**
* A read-only view of the data for a B+Tree node.
*
* Note: The leading byte of the record format codes for a leaf, a double-linked
* leaf or a node in a manner which is compatible with {@link ReadOnlyNodeData}.
*
* @author Bryan Thompson
* @version $Id: DefaultNodeCoder.java 4585 2011-06-01 13:42:56Z thompsonbry $
*/
static private class ReadOnlyDirectoryData extends AbstractReadOnlyNodeData
implements IDirectoryData {
/** The backing buffer */
private final AbstractFixedByteArrayBuffer b;
/** The record serialization version. */
private final short version;
// fields which are cached by the ctor.
private final short flags;
private final int nchildren;
private final byte[] overflowKey;
/**
* Offset of the encoded childAddr[] in the buffer.
*/
private final int O_childAddr;
final public AbstractFixedByteArrayBuffer data() {
return b;
}
//static int s_newKeys = 0;
/**
* Constructor used when the caller is encoding the {@link IDirectoryData}.
*
* @param buf
* The buffer containing the data for the node.
* @param keys The coded keys.
*/
public ReadOnlyDirectoryData(final AbstractFixedByteArrayBuffer buf) {
if (buf == null)
throw new IllegalArgumentException();
int pos = O_TYPE;
final byte type = buf.getByte(pos);
pos += SIZEOF_TYPE;
switch (type) {
case NODE:
break;
case LEAF:
throw new AssertionError();
case LINKED_LEAF:
throw new AssertionError();
default:
throw new AssertionError("type=" + type);
}
version = buf.getShort(pos);
pos += SIZEOF_VERSION;
switch (version) {
case VERSION0:
case VERSION1:
break;
default:
throw new AssertionError("version=" + version);
}
// flags
flags = buf.getShort(pos);
pos += SIZEOF_FLAGS;
if (isOverflowDirectory()) {
final int oksze = buf.getShort(pos);
pos += Bytes.SIZEOF_SHORT;
overflowKey = new byte[oksze];
//s_newKeys++;
buf.get(pos, overflowKey);
pos += oksze;
} else {
overflowKey = null;
}
nchildren = buf.getShort(pos);
pos += Bytes.SIZEOF_SHORT;
O_childAddr = pos;
// save reference to buffer
this.b = buf;
}
final public boolean hasVersionTimestamps() {
return ((flags & FLAG_VERSION_TIMESTAMPS) != 0);
}
final public long getMinimumVersionTimestamp() {
if(!hasVersionTimestamps())
throw new UnsupportedOperationException();
// Note: version timestamps not yet supported by HTree.
throw new UnsupportedOperationException();
}
final public long getMaximumVersionTimestamp() {
if(!hasVersionTimestamps())
throw new UnsupportedOperationException();
// Note: version timestamps not yet supported by HTree.
throw new UnsupportedOperationException();
}
final public boolean isOverflowDirectory() {
return (flags & AbstractReadOnlyNodeData.FLAG_OVERFLOW_DIRECTORY) != 0;
}
/**
* Always returns false
.
*/
final public boolean isLeaf() {
return false;
}
/**
* Yes.
*/
final public boolean isReadOnly() {
return true;
}
/**
* Yes.
*/
final public boolean isCoded() {
return true;
}
final public int getChildCount() {
return nchildren;
}
/**
* Bounds check.
*
* @throws IndexOutOfBoundsException
* if index is LT ZERO (0)
* @throws IndexOutOfBoundsException
* if index is GT nkeys+1
*/
protected boolean assertChildIndex(final int index) {
if (index < 0 || index > nchildren)
throw new IndexOutOfBoundsException("index=" + index
+ ", nchildren=" + nchildren);
return true;
}
final public long getChildAddr(final int index) {
assert assertChildIndex(index);
return b.getLong(O_childAddr + index * SIZEOF_ADDR);
}
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append(getClass().getName() + "{");
DefaultDirectoryPageCoder.toString(this, sb);
sb.append("}");
return sb.toString();
}
public byte[] getOverflowKey() {
return overflowKey;
}
}
/**
* Utility method formats the {@link IDirectoryData}.
*
* @param node
* A node data record.
* @param sb
* The representation will be written onto this object.
*
* @return The sb parameter.
*/
static public StringBuilder toString(final IDirectoryData node,
final StringBuilder sb) {
final int nchildren = node.getChildCount();
sb.append(", nchildren=" + nchildren);
{
sb.append(",\nchildAddr=[");
for (int i = 0; i < nchildren; i++) {
if (i > 0)
sb.append(", ");
sb.append(node.getChildAddr(i));
}
sb.append("]");
}
if(node.hasVersionTimestamps()) {
sb.append(",\nversionTimestamps={min="
+ node.getMinimumVersionTimestamp() + ",max="
+ node.getMaximumVersionTimestamp() + "}");
}
return sb;
}
}