co.cask.tigon.sql.io.GDATEncoder Maven / Gradle / Ivy
/*
* Copyright © 2014 Cask Data, 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 co.cask.tigon.sql.io;
import co.cask.tigon.io.Encoder;
import co.cask.tigon.sql.flowlet.GDATRecordType;
import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.primitives.Doubles;
import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.List;
import java.util.Map;
/**
* A {@link Encoder} that performs all writes to an in memory buffer.
*/
public class GDATEncoder implements Encoder {
private static final ByteOrder byteOrder = ByteOrder.LITTLE_ENDIAN;
private final OutStream buffer;
// Location, String
private final List> stringOffsetLocations = Lists.newArrayList();
//Size of String Payload
private int stringPayloadSize = 0;
/** ByteBuffer objects shared by all functions in this object **/
private final ByteBuffer sharedByteBuffer;
/**
* GDATEncoder Constructor
*/
public GDATEncoder() {
buffer = new OutStream();
sharedByteBuffer = ByteBuffer.allocate(8).order(byteOrder);
}
/**
* Customized child class of ByteArrayOutputStream that allows direct access to the underlying byte array.
* [Created to avoid duplicating byte arrays]
*/
private static final class OutStream extends ByteArrayOutputStream {
/**
* @return reference to the underlying byte array
*/
public byte[] getByteArray() {
return buf;
}
}
/**
* Write Length in Big Endian Order as per GDAT format specification.
*/
private void writeLengthToStream(int length, OutputStream out) throws IOException {
sharedByteBuffer.clear();
sharedByteBuffer.order(ByteOrder.BIG_ENDIAN).putInt(length);
sharedByteBuffer.flip();
out.write(sharedByteBuffer.array(), 0, Ints.BYTES);
sharedByteBuffer.order(byteOrder);
}
private void writeIntToStream(int val, OutputStream out) throws IOException {
sharedByteBuffer.clear();
sharedByteBuffer.putInt(val);
sharedByteBuffer.flip();
out.write(sharedByteBuffer.array(), 0, Ints.BYTES);
}
@Override
public Encoder writeNull() throws IOException {
return this;
}
@Override
public Encoder writeBool(boolean b) throws IOException {
return writeInt(b ? 1 : 0);
}
@Override
public Encoder writeInt(int i) throws IOException {
//Get all four Bytes of Signed Integer.
writeIntToStream(i, buffer);
return this;
}
@Override
public Encoder writeLong(long l) throws IOException {
sharedByteBuffer.clear();
sharedByteBuffer.putLong(l);
sharedByteBuffer.flip();
buffer.write(sharedByteBuffer.array(), 0, Longs.BYTES);
return this;
}
@Override
public Encoder writeFloat(float v) throws IOException {
//GDAT format will only contain double data type values and will not contain any float type values
throw new UnsupportedOperationException("Does not support Float type objects, use Double");
}
@Override
public Encoder writeDouble(double v) throws IOException {
//Get all 8 Bytes of a Double.
sharedByteBuffer.clear();
sharedByteBuffer.putDouble(v);
sharedByteBuffer.flip();
buffer.write(sharedByteBuffer.array(), 0, Doubles.BYTES);
return this;
}
@Override
public Encoder writeString(String s) throws IOException {
// String data to be inserted at location -> buffer.size()
byte[] stringBytes = s.getBytes(Charsets.UTF_8);
stringPayloadSize += stringBytes.length;
stringOffsetLocations.add(Maps.immutableEntry(buffer.size(), stringBytes));
return this;
}
@Override
public Encoder writeBytes(byte[] bytes) throws IOException {
throw new UnsupportedOperationException("Does not support reading/writing Bytes");
}
@Override
public Encoder writeBytes(byte[] bytes, int i, int i2) throws IOException {
throw new UnsupportedOperationException("Does not support reading/writing Bytes");
}
@Override
public Encoder writeBytes(ByteBuffer byteBuffer) throws IOException {
throw new UnsupportedOperationException("Does not support reading/writing Bytes");
}
private void writeToRecord(OutputStream outputStream, byte recordMarker) throws IOException {
buffer.write(recordMarker);
// Maintains the location of the next byte to be written to the outputStream
int outputArrayCounter = 0;
int offsetPointer = buffer.size() + stringOffsetLocations.size() * 3 * Ints.BYTES;
byte[] bufferByteArray = buffer.getByteArray();
//4 Bytes unsigned int to represent length of the data record.
writeLengthToStream(offsetPointer + stringPayloadSize, outputStream);
for (Map.Entry stringEntry : stringOffsetLocations) {
// Bytes preceding location of string and after last outputArrayCounter
outputStream.write(bufferByteArray, outputArrayCounter, stringEntry.getKey() - outputArrayCounter);
byte[] stringBytes = stringEntry.getValue();
// Write length of string
writeIntToStream(stringBytes.length, outputStream);
// Write offset of string
writeIntToStream(offsetPointer, outputStream);
// Write reserved
writeIntToStream(0, outputStream);
outputArrayCounter = stringEntry.getKey();
offsetPointer += stringBytes.length;
}
// Write the remaining bytes from buffer
outputStream.write(bufferByteArray, outputArrayCounter, buffer.size() - outputArrayCounter);
// Write String values at the end of the record
for (Map.Entry entry : stringOffsetLocations) {
outputStream.write(entry.getValue());
}
buffer.reset();
}
/**
* Writes a GDAT format DATA record to the provided outputStream. This method is to be called after calling all the
* required write*() methods.
*
* @param outputStream GDAT record is written to this outputStream
* @throws IOException
*/
public void writeTo(OutputStream outputStream) throws IOException {
writeToRecord(outputStream, GDATRecordType.DATA.getRecordMarker());
}
/**
* Writes a GDAT format EOF record to the provided outputStream. This method is to be called after calling all the
* required write*() methods.
*
* @param outputStream GDAT record is written to this outputStream
* @throws IOException
*/
public void writeEOFRecord(OutputStream outputStream) throws IOException {
writeToRecord(outputStream, GDATRecordType.EOF.getRecordMarker());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy