/*
* 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 com.facebook.presto.decoder.raw;
import com.facebook.presto.decoder.DecoderColumnHandle;
import com.facebook.presto.decoder.FieldDecoder;
import com.facebook.presto.decoder.FieldValueProvider;
import com.facebook.presto.spi.PrestoException;
import com.facebook.presto.spi.type.Varchars;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import io.airlift.slice.Slice;
import io.airlift.slice.Slices;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import static com.facebook.presto.decoder.DecoderErrorCode.DECODER_CONVERSION_NOT_SUPPORTED;
import static com.google.common.base.Preconditions.checkState;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
/**
* Default field decoder for raw (byte) columns.
*/
public class RawFieldDecoder
implements FieldDecoder<byte[]>
{
public enum FieldType
{
BYTE(Byte.SIZE),
SHORT(Short.SIZE),
INT(Integer.SIZE),
LONG(Long.SIZE),
FLOAT(Float.SIZE),
DOUBLE(Double.SIZE);
private final int size;
FieldType(int bitSize)
{
this.size = bitSize / 8;
}
public int getSize()
{
return size;
}
static FieldType forString(String value)
{
if (value != null) {
for (FieldType fieldType : values()) {
if (value.toUpperCase(Locale.ENGLISH).equals(fieldType.name())) {
return fieldType;
}
}
}
return null;
}
}
@Override
public Set<Class<?>> getJavaTypes()
{
return ImmutableSet.of(boolean.class, long.class, double.class, Slice.class);
}
@Override
public final String getRowDecoderName()
{
return RawRowDecoder.NAME;
}
@Override
public String getFieldDecoderName()
{
return FieldDecoder.DEFAULT_FIELD_DECODER_NAME;
}
@Override
public FieldValueProvider decode(byte[] value, DecoderColumnHandle columnHandle)
{
requireNonNull(columnHandle, "columnHandle is null");
requireNonNull(value, "value is null");
String mapping = columnHandle.getMapping();
FieldType fieldType = columnHandle.getDataFormat() == null ? FieldType.BYTE : FieldType.forString(columnHandle.getDataFormat());
int start = 0;
int end = value.length;
if (mapping != null) {
List<String> fields = ImmutableList.copyOf(Splitter.on(':').limit(2).split(mapping));
if (!fields.isEmpty()) {
start = Integer.parseInt(fields.get(0));
checkState(start >= 0 && start < value.length, "Found start %s, but only 0..%s is legal", start, value.length);
if (fields.size() > 1) {
end = Integer.parseInt(fields.get(1));
checkState(end > 0 && end <= value.length, "Found end %s, but only 1..%s is legal", end, value.length);
}
}
}
checkState(start <= end, "Found start %s and end %s. start must be smaller than end", start, end);
return new RawValueProvider(ByteBuffer.wrap(value, start, end - start), columnHandle, fieldType);
}
@Override
public String toString()
{
return format("FieldDecoder[%s/%s]", getRowDecoderName(), getFieldDecoderName());
}
public static class RawValueProvider
extends FieldValueProvider
{
protected final ByteBuffer value;
protected final DecoderColumnHandle columnHandle;
protected final FieldType fieldType;
protected final int size;
public RawValueProvider(ByteBuffer value, DecoderColumnHandle columnHandle, FieldType fieldType)
{
this.columnHandle = requireNonNull(columnHandle, "columnHandle is null");
this.fieldType = requireNonNull(fieldType, "fieldType is null");
this.size = value.limit() - value.position();
// check size for non-null fields
if (size > 0) {
checkState(size >= fieldType.getSize(), "minimum byte size is %s, found %s,", fieldType.getSize(), size);
}
this.value = value;
}
@Override
public final boolean accept(DecoderColumnHandle columnHandle)
{
return this.columnHandle.equals(columnHandle);
}
@Override
public final boolean isNull()
{
return size == 0;
}
@Override
public boolean getBoolean()
{
if (isNull()) {
return false;
}
switch (fieldType) {
case BYTE:
return value.get() != 0;
case SHORT:
return value.getShort() != 0;
case INT:
return value.getInt() != 0;
case LONG:
return value.getLong() != 0;
default:
throw new PrestoException(DECODER_CONVERSION_NOT_SUPPORTED, format("conversion %s to boolean not supported", fieldType));
}
}
@Override
public long getLong()
{
if (isNull()) {
return 0L;
}
switch (fieldType) {
case BYTE:
return value.get();
case SHORT:
return value.getShort();
case INT:
return value.getInt();
case LONG:
return value.getLong();
default:
throw new PrestoException(DECODER_CONVERSION_NOT_SUPPORTED, format("conversion %s to long not supported", fieldType));
}
}
@Override
public double getDouble()
{
if (isNull()) {
return 0.0d;
}
switch (fieldType) {
case FLOAT:
return value.getFloat();
case DOUBLE:
return value.getDouble();
default:
throw new PrestoException(DECODER_CONVERSION_NOT_SUPPORTED, format("conversion %s to double not supported", fieldType));
}
}
@Override
public Slice getSlice()
{
if (isNull()) {
return Slices.EMPTY_SLICE;
}
if (fieldType == FieldType.BYTE) {
Slice slice = Slices.wrappedBuffer(value.slice());
if (Varchars.isVarcharType(columnHandle.getType())) {
slice = Varchars.truncateToLength(slice, columnHandle.getType());
}
return slice;
}
throw new PrestoException(DECODER_CONVERSION_NOT_SUPPORTED, format("conversion %s to Slice not supported", fieldType));
}
}
}