| /*** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.apache.tajo.tuple.memory; |
| |
| import com.google.common.base.Preconditions; |
| import com.google.protobuf.InvalidProtocolBufferException; |
| import com.google.protobuf.Message; |
| import io.netty.util.internal.PlatformDependent; |
| import org.apache.tajo.common.TajoDataTypes; |
| import org.apache.tajo.datum.*; |
| import org.apache.tajo.exception.TajoRuntimeException; |
| import org.apache.tajo.exception.UnsupportedException; |
| import org.apache.tajo.storage.Tuple; |
| import org.apache.tajo.storage.VTuple; |
| import org.apache.tajo.util.SizeOf; |
| import org.apache.tajo.util.StringUtils; |
| import org.apache.tajo.util.UnsafeUtil; |
| import org.apache.tajo.util.datetime.TimeMeta; |
| import sun.misc.Unsafe; |
| |
| import java.nio.ByteBuffer; |
| import java.nio.charset.Charset; |
| import java.util.Arrays; |
| |
| import static org.apache.tajo.common.TajoDataTypes.DataType; |
| |
| public class UnSafeTuple extends ZeroCopyTuple { |
| private static final Unsafe UNSAFE = UnsafeUtil.unsafe; |
| |
| private MemoryBlock memoryBlock; |
| private DataType[] types; |
| |
| @Override |
| public void set(MemoryBlock memoryBlock, int relativePos, DataType[] types) { |
| Preconditions.checkArgument(memoryBlock.hasAddress()); |
| |
| this.memoryBlock = memoryBlock; |
| this.types = types; |
| super.set(relativePos); |
| } |
| |
| public void set(UnSafeTuple tuple) { |
| this.memoryBlock = tuple.memoryBlock; |
| this.types = tuple.types; |
| super.set(tuple.getRelativePos()); |
| } |
| |
| @Override |
| public int size() { |
| return types.length; |
| } |
| |
| @Override |
| public int getLength() { |
| return PlatformDependent.getInt(address()); |
| } |
| |
| @Override |
| public TajoDataTypes.Type type(int fieldId) { |
| return types[fieldId].getType(); |
| } |
| |
| @Override |
| public int size(int fieldId) { |
| return PlatformDependent.getInt(getFieldAddr(fieldId)); |
| } |
| |
| public void writeTo(ByteBuffer bb) { |
| if (bb.remaining() < getLength()) { |
| throw new IndexOutOfBoundsException("remaining length: " + bb.remaining() |
| + ", tuple length: " + getLength()); |
| } |
| |
| if (getLength() > 0) { |
| if (bb.isDirect()) { |
| PlatformDependent.copyMemory(address(), PlatformDependent.directBufferAddress(bb) + bb.position(), getLength()); |
| bb.position(bb.position() + getLength()); |
| } else { |
| PlatformDependent.copyMemory(address(), bb.array(), bb.arrayOffset() + bb.position(), getLength()); |
| bb.position(bb.position() + getLength()); |
| } |
| } |
| } |
| |
| public long address() { |
| return memoryBlock.address() + getRelativePos(); |
| } |
| |
| public HeapTuple toHeapTuple() { |
| HeapTuple heapTuple = new HeapTuple(); |
| byte [] bytes = new byte[getLength()]; |
| PlatformDependent.copyMemory(address(), bytes, 0, getLength()); |
| heapTuple.set(bytes, types); |
| return heapTuple; |
| } |
| |
| private int getFieldOffset(int fieldId) { |
| return PlatformDependent.getInt(address()+ (long)(SizeOf.SIZE_OF_INT + (fieldId * SizeOf.SIZE_OF_INT))); |
| } |
| |
| public long getFieldAddr(int fieldId) { |
| int fieldOffset = getFieldOffset(fieldId); |
| if (fieldOffset < 0 || fieldOffset > getLength()) { |
| throw new RuntimeException("Invalid Access. Field : " + fieldId |
| + ", Offset:" + fieldOffset + ", Record length:" + getLength()); |
| } |
| return address() + fieldOffset; |
| } |
| |
| @Override |
| public boolean contains(int fieldid) { |
| return getFieldOffset(fieldid) > MemoryRowBlock.NULL_FIELD_OFFSET; |
| } |
| |
| @Override |
| public boolean isBlank(int fieldid) { |
| return getFieldOffset(fieldid) == MemoryRowBlock.NULL_FIELD_OFFSET; |
| } |
| |
| @Override |
| public boolean isBlankOrNull(int fieldid) { |
| return getFieldOffset(fieldid) == MemoryRowBlock.NULL_FIELD_OFFSET; |
| } |
| |
| @Override |
| public void clear() { |
| // nothing to do |
| } |
| |
| @Override |
| public void put(int fieldId, Datum value) { |
| throw new TajoRuntimeException(new UnsupportedException()); |
| } |
| |
| @Override |
| public void insertTuple(int fieldId, Tuple tuple) { |
| throw new TajoRuntimeException(new UnsupportedException()); |
| } |
| |
| @Override |
| public void put(Datum[] values) { |
| throw new TajoRuntimeException(new UnsupportedException()); |
| } |
| |
| @Override |
| public Datum asDatum(int fieldId) { |
| if (isBlankOrNull(fieldId)) { |
| return NullDatum.get(); |
| } |
| |
| switch (types[fieldId].getType()) { |
| case BOOLEAN: |
| return DatumFactory.createBool(getBool(fieldId)); |
| case BIT: |
| return DatumFactory.createBit(getByte(fieldId)); |
| case INT1: |
| case INT2: |
| return DatumFactory.createInt2(getInt2(fieldId)); |
| case INT4: |
| return DatumFactory.createInt4(getInt4(fieldId)); |
| case INT8: |
| return DatumFactory.createInt8(getInt8(fieldId)); |
| case FLOAT4: |
| return DatumFactory.createFloat4(getFloat4(fieldId)); |
| case FLOAT8: |
| return DatumFactory.createFloat8(getFloat8(fieldId)); |
| case CHAR: |
| return DatumFactory.createChar(getBytes(fieldId)); |
| case TEXT: |
| return DatumFactory.createText(getBytes(fieldId)); |
| case BLOB: |
| return DatumFactory.createBlob(getBytes(fieldId)); |
| case TIMESTAMP: |
| return DatumFactory.createTimestamp(getInt8(fieldId)); |
| case DATE: |
| return DatumFactory.createDate(getInt4(fieldId)); |
| case TIME: |
| return DatumFactory.createTime(getInt8(fieldId)); |
| case INTERVAL: |
| return getInterval(fieldId); |
| case PROTOBUF: |
| return getProtobufDatum(fieldId); |
| case NULL_TYPE: |
| return NullDatum.get(); |
| default: |
| throw new TajoRuntimeException(new UnsupportedException("data type '" + types[fieldId] + "'")); |
| } |
| } |
| |
| @Override |
| public void clearOffset() { |
| } |
| |
| @Override |
| public void setOffset(long offset) { |
| } |
| |
| @Override |
| public long getOffset() { |
| return 0; |
| } |
| |
| @Override |
| public boolean getBool(int fieldId) { |
| return PlatformDependent.getByte(getFieldAddr(fieldId)) == 0x01; |
| } |
| |
| @Override |
| public byte getByte(int fieldId) { |
| return PlatformDependent.getByte(getFieldAddr(fieldId)); |
| } |
| |
| @Override |
| public char getChar(int fieldId) { |
| return UNSAFE.getChar(getFieldAddr(fieldId)); |
| } |
| |
| @Override |
| public byte[] getBytes(int fieldId) { |
| long pos = getFieldAddr(fieldId); |
| int len = PlatformDependent.getInt(pos); |
| pos += SizeOf.SIZE_OF_INT; |
| |
| byte [] bytes = new byte[len]; |
| PlatformDependent.copyMemory(pos, bytes, 0, len); |
| return bytes; |
| } |
| |
| @Override |
| public byte[] getTextBytes(int fieldId) { |
| return asDatum(fieldId).asTextBytes(); |
| } |
| |
| @Override |
| public short getInt2(int fieldId) { |
| long addr = getFieldAddr(fieldId); |
| return PlatformDependent.getShort(addr); |
| } |
| |
| @Override |
| public int getInt4(int fieldId) { |
| return PlatformDependent.getInt(getFieldAddr(fieldId)); |
| } |
| |
| @Override |
| public long getInt8(int fieldId) { |
| return PlatformDependent.getLong(getFieldAddr(fieldId)); |
| } |
| |
| @Override |
| public float getFloat4(int fieldId) { |
| return Float.intBitsToFloat(PlatformDependent.getInt(getFieldAddr(fieldId))); |
| } |
| |
| @Override |
| public double getFloat8(int fieldId) { |
| return Double.longBitsToDouble(PlatformDependent.getLong(getFieldAddr(fieldId))); |
| } |
| |
| @Override |
| public String getText(int fieldId) { |
| return new String(getBytes(fieldId), TextDatum.DEFAULT_CHARSET); |
| } |
| |
| @Override |
| public IntervalDatum getInterval(int fieldId) { |
| long pos = getFieldAddr(fieldId); |
| int months = PlatformDependent.getInt(pos); |
| pos += SizeOf.SIZE_OF_INT; |
| long millisecs = PlatformDependent.getLong(pos); |
| return new IntervalDatum(months, millisecs); |
| } |
| |
| @Override |
| public Datum getProtobufDatum(int fieldId) { |
| byte [] bytes = getBytes(fieldId); |
| |
| ProtobufDatumFactory factory = ProtobufDatumFactory.get(types[fieldId]); |
| Message.Builder builder = factory.newBuilder(); |
| try { |
| builder.mergeFrom(bytes); |
| } catch (InvalidProtocolBufferException e) { |
| return NullDatum.get(); |
| } |
| |
| return new ProtobufDatum(builder.build()); |
| } |
| |
| @Override |
| public char[] getUnicodeChars(int fieldId) { |
| long pos = getFieldAddr(fieldId); |
| int len = PlatformDependent.getInt(pos); |
| pos += SizeOf.SIZE_OF_INT; |
| |
| byte [] bytes = new byte[len]; |
| PlatformDependent.copyMemory(pos, bytes, 0, len); |
| return StringUtils.convertBytesToChars(bytes, Charset.forName("UTF-8")); |
| } |
| |
| @Override |
| public TimeMeta getTimeDate(int fieldId) { |
| return asDatum(fieldId).asTimeMeta(); |
| } |
| |
| @Override |
| public Tuple clone() throws CloneNotSupportedException { |
| return toHeapTuple(); |
| } |
| |
| @Override |
| public Datum[] getValues() { |
| Datum [] datums = new Datum[size()]; |
| for (int i = 0; i < size(); i++) { |
| if (contains(i)) { |
| datums[i] = asDatum(i); |
| } else { |
| datums[i] = NullDatum.get(); |
| } |
| } |
| return datums; |
| } |
| |
| @Override |
| public int hashCode() { |
| return Arrays.hashCode(getValues()); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (obj instanceof Tuple) { |
| Tuple other = (Tuple) obj; |
| return Arrays.equals(getValues(), other.getValues()); |
| } |
| return false; |
| } |
| @Override |
| public String toString() { |
| return VTuple.toDisplayString(getValues()); |
| } |
| |
| public void release() { |
| |
| } |
| } |