/*
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
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 31, 2009
*/
package com.bigdata.btree.raba;
import java.io.DataInput;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.OutputStream;
import java.util.Iterator;
import com.bigdata.btree.raba.codec.ICodedRaba;
import com.bigdata.btree.raba.codec.IRabaCoder;
import com.bigdata.io.AbstractFixedByteArrayBuffer;
import com.bigdata.io.DataOutputBuffer;
import com.bigdata.io.LongPacker;
/**
* Coder conditionally applies other {@link IRabaCoder}s based on a condition,
* typically the branching factor or the #of elements in the {@link IRaba}.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*/
public class ConditionalRabaCoder implements IRabaCoder, Externalizable {
/**
*
*/
private static final long serialVersionUID = 167667045118062564L;
private int bigSize;
private IRabaCoder smallCoder;
private IRabaCoder bigCoder;
/**
* Return <code>true</code> iff the "small" {@link IRabaCoder} should be
* applied.
*
* @param size The size of the {@link IRaba} to be coded.
*
* @return
*/
protected boolean isSmall(final int size) {
return size < bigSize;
}
@Override
final public boolean isKeyCoder() {
return smallCoder.isKeyCoder() && bigCoder.isKeyCoder();
}
@Override
final public boolean isValueCoder() {
return smallCoder.isValueCoder() && bigCoder.isValueCoder();
}
@Override
public boolean isDuplicateKeys() {
return smallCoder.isDuplicateKeys() && bigCoder.isDuplicateKeys();
}
/**
* De-serialization ctor.
*/
public ConditionalRabaCoder() {
}
/**
*
* @param smallCoder
* The coder for a small {@link IRaba}.
* @param bigCoder
* The coder for a large {@link IRaba}.
* @param bigSize
* An {@link IRaba} with this many elements will be coded using
* the {@link #bigCoder}.
*/
public ConditionalRabaCoder(final IRabaCoder smallCoder,
final IRabaCoder bigCoder, final int bigSize) {
final boolean isKeyCoder = smallCoder.isKeyCoder()
&& bigCoder.isKeyCoder();
final boolean isValueCoder = smallCoder.isValueCoder()
&& bigCoder.isValueCoder();
if (!isKeyCoder && !isValueCoder)
throw new IllegalArgumentException();
this.smallCoder = smallCoder;
this.bigCoder = bigCoder;
this.bigSize = bigSize;
}
@Override
public ICodedRaba decode(final AbstractFixedByteArrayBuffer data) {
final boolean isSmall = data.getByte(0) == 1 ? true : false;
final AbstractFixedByteArrayBuffer delegateSlice = data
.slice(1, data.len() - 1);
final ICodedRaba codedRaba;
if (isSmall) {
codedRaba = smallCoder.decode(delegateSlice);
} else {
codedRaba = bigCoder.decode(delegateSlice);
}
// wraps coded raba to return the original data().
return new CodedRabaSlice(data, codedRaba);
}
@Override
public ICodedRaba encodeLive(final IRaba raba, final DataOutputBuffer buf) {
final int size = raba.size();
final boolean isSmall = isSmall(size);
final int O_origin = buf.pos();
buf.putByte((byte) (isSmall ? 1 : 0));
final ICodedRaba delegateCodedRaba;
if (isSmall) {
delegateCodedRaba = smallCoder.encodeLive(raba, buf);
} else {
delegateCodedRaba = bigCoder.encodeLive(raba, buf);
}
final AbstractFixedByteArrayBuffer delegateSlice = delegateCodedRaba
.data();
return new CodedRabaSlice(buf.slice(O_origin, delegateSlice.len() + 1),
delegateCodedRaba);
}
@Override
public AbstractFixedByteArrayBuffer encode(final IRaba raba,
final DataOutputBuffer buf) {
final int size = raba.size();
final boolean isSmall = isSmall(size);
final int O_origin = buf.pos();
buf.putByte((byte) (isSmall ? 1 : 0));
final AbstractFixedByteArrayBuffer slice;
if (isSmall) {
slice = smallCoder.encode(raba, buf);
} else {
slice = bigCoder.encode(raba, buf);
}
return buf.slice(O_origin, slice.len() + 1);
}
@Override
public void readExternal(final ObjectInput in) throws IOException,
ClassNotFoundException {
final byte version = in.readByte();
switch (version) {
case VERSION0:
break;
default:
throw new IOException();
}
bigSize = (int) LongPacker.unpackLong(in);
smallCoder = (IRabaCoder) in.readObject();
bigCoder = (IRabaCoder) in.readObject();
}
@Override
public void writeExternal(final ObjectOutput out) throws IOException {
out.writeByte(VERSION0);
LongPacker.packLong(out, bigSize);
out.writeObject(smallCoder);
out.writeObject(bigCoder);
}
private static final byte VERSION0 = 0x00;
/**
* Helper class used to wrap an {@link ICodedRaba} while returning the
* caller's slice for the backing data. We use this to have the
* {@link ConditionalRabaCoder} return the original slice, not the slice
* after the first byte (which indicates whether to use the small or large
* coder) has been chopped off.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan
* Thompson</a>
* @version $Id$
*/
private static class CodedRabaSlice implements ICodedRaba {
private final IRaba delegate;
private final AbstractFixedByteArrayBuffer data;
CodedRabaSlice(final AbstractFixedByteArrayBuffer data,
final IRaba delegate) {
this.delegate = delegate;
this.data = data;
}
@Override
public AbstractFixedByteArrayBuffer data() {
return data;
}
@Override
public int add(byte[] value, int off, int len) {
return delegate.add(value, off, len);
}
@Override
public int add(byte[] a) {
return delegate.add(a);
}
@Override
public int add(DataInput in, int len) throws IOException {
return delegate.add(in, len);
}
@Override
public int capacity() {
return delegate.capacity();
}
@Override
public int copy(final int index, final OutputStream os) {
return delegate.copy(index, os);
}
@Override
public byte[] get(final int index) {
return delegate.get(index);
}
@Override
public boolean isEmpty() {
return delegate.isEmpty();
}
@Override
public boolean isFull() {
return delegate.isFull();
}
@Override
public boolean isKeys() {
return delegate.isKeys();
}
@Override
public boolean isNull(final int index) {
return delegate.isNull(index);
}
@Override
public boolean isReadOnly() {
return delegate.isReadOnly();
}
@Override
public Iterator<byte[]> iterator() {
return delegate.iterator();
}
@Override
public int length(final int index) {
return delegate.length(index);
}
@Override
public int search(final byte[] searchKey) {
return delegate.search(searchKey);
}
@Override
public void set(final int index, final byte[] a) {
delegate.set(index, a);
}
@Override
public int size() {
return delegate.size();
}
}
}