/**
* Copyright 2013 Cloudera Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.kitesdk.data.hbase.avro.io;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import org.apache.avro.io.BinaryDecoder;
import org.apache.avro.io.Decoder;
import org.apache.avro.io.DecoderFactory;
import org.apache.avro.util.Utf8;
import org.apache.hadoop.hbase.util.Bytes;
/**
* An Avro Decoder implementation used for decoding Avro instances from HBase
* columns. This is basically an Avro BinaryDecoder with custom encoding of int,
* long, and String types.
*
* int and long are serialized in standard 4 and 8 byte format (instead of
* Avro's ZigZag encoding) so that we can use HBase's atomic increment
* functionality on columns.
*
* Strings are encoded as UTF-8 bytes. This is consistent
* with HBase, and will allow appends in the future.
*/
public class ColumnDecoder extends Decoder {
private final BinaryDecoder wrappedDecoder;
private final InputStream in;
private final DataInputStream dataIn;
public ColumnDecoder(InputStream in) {
this.in = in;
this.wrappedDecoder = new DecoderFactory().binaryDecoder(in, null);
this.dataIn = new DataInputStream(in);
}
@Override
public void readNull() throws IOException {
wrappedDecoder.readNull();
}
@Override
public boolean readBoolean() throws IOException {
return wrappedDecoder.readBoolean();
}
@Override
public int readInt() throws IOException {
byte[] b = new byte[4];
dataIn.readFully(b);
return Bytes.toInt(b);
}
@Override
public long readLong() throws IOException {
byte[] b = new byte[8];
dataIn.readFully(b);
return Bytes.toLong(b);
}
@Override
public float readFloat() throws IOException {
return wrappedDecoder.readFloat();
}
@Override
public double readDouble() throws IOException {
return wrappedDecoder.readDouble();
}
@Override
public Utf8 readString(Utf8 old) throws IOException {
int bytesAvailable = in.available(); // assumes 'in' is ByteArrayInputStream so knows length
byte[] bytes = new byte[bytesAvailable];
in.read(bytes);
return new Utf8(bytes);
}
@Override
public String readString() throws IOException {
return readString(null).toString();
}
@Override
public void skipString() throws IOException {
int bytesAvailable = in.available(); // assumes 'in' is ByteArrayInputStream so knows length
in.skip(bytesAvailable);
}
@Override
public ByteBuffer readBytes(ByteBuffer old) throws IOException {
return wrappedDecoder.readBytes(old);
}
@Override
public void skipBytes() throws IOException {
wrappedDecoder.skipBytes();
}
@Override
public void readFixed(byte[] bytes, int start, int length) throws IOException {
wrappedDecoder.readFixed(bytes, start, length);
}
@Override
public void skipFixed(int length) throws IOException {
wrappedDecoder.skipFixed(length);
}
@Override
public int readEnum() throws IOException {
return wrappedDecoder.readEnum();
}
@Override
public long readArrayStart() throws IOException {
return wrappedDecoder.readArrayStart();
}
@Override
public long arrayNext() throws IOException {
return wrappedDecoder.arrayNext();
}
@Override
public long skipArray() throws IOException {
return wrappedDecoder.skipArray();
}
@Override
public long readMapStart() throws IOException {
return wrappedDecoder.readMapStart();
}
@Override
public long mapNext() throws IOException {
return wrappedDecoder.mapNext();
}
@Override
public long skipMap() throws IOException {
return wrappedDecoder.skipMap();
}
@Override
public int readIndex() throws IOException {
return wrappedDecoder.readIndex();
}
}