/** * 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 io.netty.util.internal.PlatformDependent; import org.apache.tajo.common.TajoDataTypes.DataType; import org.apache.tajo.datum.IntervalDatum; import org.apache.tajo.datum.ProtobufDatum; import org.apache.tajo.datum.TextDatum; import org.apache.tajo.util.SizeOf; /** * * Row Record Structure * * | row length (4 bytes) | field 1 offset | field 2 offset | ... | field N offset| field 1 | field 2| ... | field N | * 4 bytes 4 bytes 4 bytes * */ public abstract class OffHeapRowWriter implements RowWriter { /** record capacity + offset list */ private final int headerSize; private final DataType[] dataTypes; private int curFieldIdx; private int curFieldOffset; private int curOffset; public OffHeapRowWriter(final DataType[] dataTypes) { this.dataTypes = dataTypes; this.headerSize = SizeOf.SIZE_OF_INT * (dataTypes.length + 1); this.curFieldOffset = SizeOf.SIZE_OF_INT; } /** * Current memory address of the row * * @return The memory address */ public long recordStartAddr() { return currentAddr() - curOffset; } /** * Memory address that point to the first byte of the buffer * * @return The memory address */ private long currentAddr() { return address() + position(); } /** * Current memory address of the buffer * * @return The memory address */ public abstract long address(); public abstract void ensureSize(int size); public int offset() { return position(); } /** * Current position * * @return The position */ public abstract int position(); /** * Forward the address; * * @param length Length to be forwarded */ public abstract void forward(int length); /** * Backward the address; * * @param length Length to be backwarded */ public abstract void backward(int length); @Override public void clear() { curOffset = 0; curFieldIdx = 0; curFieldOffset = SizeOf.SIZE_OF_INT; } @Override public DataType[] dataTypes() { return dataTypes; } @Override public boolean startRow() { ensureSize(headerSize); curOffset = headerSize; curFieldOffset = SizeOf.SIZE_OF_INT; curFieldIdx = 0; forward(headerSize); return true; } @Override public void endRow() { long rowHeaderPos = recordStartAddr(); // curOffset is equivalent to a byte length of this row. PlatformDependent.putInt(rowHeaderPos, curOffset); //forward (record offset + fields offset) rowHeaderPos += SizeOf.SIZE_OF_INT + curFieldOffset; // set remain header field length for (int i = curFieldIdx; i < dataTypes.length; i++) { PlatformDependent.putInt(rowHeaderPos, MemoryRowBlock.NULL_FIELD_OFFSET); rowHeaderPos += SizeOf.SIZE_OF_INT; } curOffset = 0; } @Override public void cancelRow() { // curOffset is equivalent to a byte length of current row. backward(curOffset); curOffset = 0; curFieldOffset = SizeOf.SIZE_OF_INT; curFieldIdx = 0; } @Override public void skipField() { // set header field length putFieldHeader(currentAddr(), MemoryRowBlock.NULL_FIELD_OFFSET); } /** * set current buffer position and forward field length * @param fieldLength */ private void forwardField(int fieldLength) { forward(fieldLength); curOffset += fieldLength; } private void putFieldHeader(long currentAddr, int length) { long currentHeaderAddr = currentAddr - curOffset + curFieldOffset; // set header field length PlatformDependent.putInt(currentHeaderAddr, length); curFieldOffset += SizeOf.SIZE_OF_INT; curFieldIdx++; } @Override public void putByte(byte val) { ensureSize(SizeOf.SIZE_OF_BYTE); long addr = currentAddr(); PlatformDependent.putByte(addr, val); putFieldHeader(addr, curOffset); forwardField(SizeOf.SIZE_OF_BYTE); } @Override public void putBool(boolean val) { ensureSize(SizeOf.SIZE_OF_BOOL); long addr = currentAddr(); PlatformDependent.putByte(addr, (byte) (val ? 0x01 : 0x00)); putFieldHeader(addr, curOffset); forwardField(SizeOf.SIZE_OF_BOOL); } @Override public void putInt2(short val) { ensureSize(SizeOf.SIZE_OF_SHORT); long addr = currentAddr(); PlatformDependent.putShort(addr, val); putFieldHeader(addr, curOffset); forwardField(SizeOf.SIZE_OF_SHORT); } @Override public void putInt4(int val) { ensureSize(SizeOf.SIZE_OF_INT); long addr = currentAddr(); PlatformDependent.putInt(addr, val); putFieldHeader(addr, curOffset); forwardField(SizeOf.SIZE_OF_INT); } @Override public void putInt8(long val) { ensureSize(SizeOf.SIZE_OF_LONG); long addr = currentAddr(); PlatformDependent.putLong(addr, val); putFieldHeader(addr, curOffset); forwardField(SizeOf.SIZE_OF_LONG); } @Override public void putFloat4(float val) { ensureSize(SizeOf.SIZE_OF_INT); long addr = currentAddr(); PlatformDependent.putInt(addr, Float.floatToRawIntBits(val)); putFieldHeader(addr, curOffset); forwardField(SizeOf.SIZE_OF_INT); } @Override public void putFloat8(double val) { ensureSize(SizeOf.SIZE_OF_LONG); long addr = currentAddr(); PlatformDependent.putLong(addr, Double.doubleToRawLongBits(val)); putFieldHeader(addr, curOffset); forwardField(SizeOf.SIZE_OF_LONG); } @Override public void putText(String val) { byte[] bytes = val.getBytes(TextDatum.DEFAULT_CHARSET); putText(bytes); } @Override public void putText(byte[] val) { putBlob(val); } @Override public void putBlob(byte[] val) { int bytesLen = val.length; int fieldLen = SizeOf.SIZE_OF_INT + bytesLen; ensureSize(fieldLen); long addr = currentAddr(); PlatformDependent.putInt(addr, bytesLen); PlatformDependent.copyMemory(val, 0, addr + SizeOf.SIZE_OF_INT, bytesLen); putFieldHeader(addr, curOffset); forwardField(fieldLen); } @Override public void putTimestamp(long val) { putInt8(val); } @Override public void putDate(int val) { putInt4(val); } @Override public void putTime(long val) { putInt8(val); } @Override public void putInterval(IntervalDatum val) { ensureSize(SizeOf.SIZE_OF_INT + SizeOf.SIZE_OF_LONG); long addr = currentAddr(); PlatformDependent.putInt(addr, val.getMonths()); PlatformDependent.putLong(addr + SizeOf.SIZE_OF_INT, val.getMilliSeconds()); putFieldHeader(addr, curOffset); forwardField(SizeOf.SIZE_OF_INT + SizeOf.SIZE_OF_LONG); } @Override public void putProtoDatum(ProtobufDatum val) { putBlob(val.asByteArray()); } protected void addTuple(UnSafeTuple tuple) { int length = tuple.getLength(); ensureSize(length); PlatformDependent.copyMemory(tuple.address(), address() + position(), length); forward(length); } }