/*
* 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.spi.block;
import com.facebook.presto.spi.PrestoException;
import com.facebook.presto.spi.type.Type;
import io.airlift.slice.Slice;
import org.openjdk.jol.info.ClassLayout;
import java.lang.invoke.MethodHandle;
import static com.facebook.presto.spi.StandardErrorCode.GENERIC_INTERNAL_ERROR;
import static com.facebook.presto.spi.block.AbstractMapBlock.HASH_MULTIPLIER;
import static com.facebook.presto.spi.block.BlockUtil.intSaturatedCast;
import static io.airlift.slice.SizeOf.sizeOf;
import static io.airlift.slice.SizeOf.sizeOfIntArray;
public class SingleMapBlock
extends AbstractSingleMapBlock
{
private static final int INSTANCE_SIZE = ClassLayout.parseClass(SingleMapBlockWriter.class).instanceSize();
private final int offset;
private final int positionCount;
private final Block keyBlock;
private final Block valueBlock;
private final int[] hashTable;
private final Type keyType;
private final MethodHandle keyNativeHashCode;
private final MethodHandle keyBlockNativeEquals;
SingleMapBlock(int offset, int positionCount, Block keyBlock, Block valueBlock, int[] hashTable, Type keyType, MethodHandle keyNativeHashCode, MethodHandle keyBlockNativeEquals)
{
super(offset, keyBlock, valueBlock);
this.offset = offset;
this.positionCount = positionCount;
this.keyBlock = keyBlock;
this.valueBlock = valueBlock;
this.hashTable = hashTable;
this.keyType = keyType;
this.keyNativeHashCode = keyNativeHashCode;
this.keyBlockNativeEquals = keyBlockNativeEquals;
}
@Override
public int getPositionCount()
{
return positionCount;
}
@Override
public int getSizeInBytes()
{
return intSaturatedCast(keyBlock.getRegionSizeInBytes(offset / 2, positionCount / 2) +
valueBlock.getRegionSizeInBytes(offset / 2, positionCount / 2) +
sizeOfIntArray(positionCount / 2 * HASH_MULTIPLIER));
}
@Override
public int getRetainedSizeInBytes()
{
return intSaturatedCast(INSTANCE_SIZE + keyBlock.getRetainedSizeInBytes() + valueBlock.getRetainedSizeInBytes() + sizeOf(hashTable));
}
@Override
public BlockEncoding getEncoding()
{
return new SingleMapBlockEncoding(keyType, keyNativeHashCode, keyBlockNativeEquals, keyBlock.getEncoding(), valueBlock.getEncoding());
}
public int getOffset()
{
return offset;
}
Block getKeyBlock()
{
return keyBlock;
}
Block getValueBlock()
{
return valueBlock;
}
int[] getHashTable()
{
return hashTable;
}
public int seekKey(Object nativeValue)
{
if (positionCount == 0) {
return -1;
}
long hashCode;
try {
hashCode = (long) keyNativeHashCode.invoke(nativeValue);
}
catch (Throwable throwable) {
throw handleThrowable(throwable);
}
int hashTableOffset = offset / 2 * HASH_MULTIPLIER;
int hashTableSize = positionCount / 2 * HASH_MULTIPLIER;
int hash = (int) Math.floorMod(hashCode, hashTableSize);
while (true) {
int keyPosition = hashTable[hashTableOffset + hash];
if (keyPosition == -1) {
return -1;
}
boolean match;
try {
match = (boolean) keyBlockNativeEquals.invoke(keyBlock, offset / 2 + keyPosition, nativeValue);
}
catch (Throwable throwable) {
throw handleThrowable(throwable);
}
if (match) {
return keyPosition * 2 + 1;
}
hash++;
if (hash == hashTableSize) {
hash = 0;
}
}
}
// The next 5 seekKeyExact functions are the same as seekKey
// except MethodHandle.invoke is replaced with invokeExact.
public int seekKeyExact(long nativeValue)
{
if (positionCount == 0) {
return -1;
}
long hashCode;
try {
hashCode = (long) keyNativeHashCode.invokeExact(nativeValue);
}
catch (Throwable throwable) {
throw handleThrowable(throwable);
}
int hashTableOffset = offset / 2 * HASH_MULTIPLIER;
int hashTableSize = positionCount / 2 * HASH_MULTIPLIER;
int hash = (int) Math.floorMod(hashCode, hashTableSize);
while (true) {
int keyPosition = hashTable[hashTableOffset + hash];
if (keyPosition == -1) {
return -1;
}
boolean match;
try {
match = (boolean) keyBlockNativeEquals.invokeExact(keyBlock, offset / 2 + keyPosition, nativeValue);
}
catch (Throwable throwable) {
throw handleThrowable(throwable);
}
if (match) {
return keyPosition * 2 + 1;
}
hash++;
if (hash == hashTableSize) {
hash = 0;
}
}
}
public int seekKeyExact(boolean nativeValue)
{
if (positionCount == 0) {
return -1;
}
long hashCode;
try {
hashCode = (long) keyNativeHashCode.invokeExact(nativeValue);
}
catch (Throwable throwable) {
throw handleThrowable(throwable);
}
int hashTableOffset = offset / 2 * HASH_MULTIPLIER;
int hashTableSize = positionCount / 2 * HASH_MULTIPLIER;
int hash = (int) Math.floorMod(hashCode, hashTableSize);
while (true) {
int keyPosition = hashTable[hashTableOffset + hash];
if (keyPosition == -1) {
return -1;
}
boolean match;
try {
match = (boolean) keyBlockNativeEquals.invokeExact(keyBlock, offset / 2 + keyPosition, nativeValue);
}
catch (Throwable throwable) {
throw handleThrowable(throwable);
}
if (match) {
return keyPosition * 2 + 1;
}
hash++;
if (hash == hashTableSize) {
hash = 0;
}
}
}
public int seekKeyExact(double nativeValue)
{
if (positionCount == 0) {
return -1;
}
long hashCode;
try {
hashCode = (long) keyNativeHashCode.invokeExact(nativeValue);
}
catch (Throwable throwable) {
throw handleThrowable(throwable);
}
int hashTableOffset = offset / 2 * HASH_MULTIPLIER;
int hashTableSize = positionCount / 2 * HASH_MULTIPLIER;
int hash = (int) Math.floorMod(hashCode, hashTableSize);
while (true) {
int keyPosition = hashTable[hashTableOffset + hash];
if (keyPosition == -1) {
return -1;
}
boolean match;
try {
match = (boolean) keyBlockNativeEquals.invokeExact(keyBlock, offset / 2 + keyPosition, nativeValue);
}
catch (Throwable throwable) {
throw handleThrowable(throwable);
}
if (match) {
return keyPosition * 2 + 1;
}
hash++;
if (hash == hashTableSize) {
hash = 0;
}
}
}
public int seekKeyExact(Slice nativeValue)
{
if (positionCount == 0) {
return -1;
}
long hashCode;
try {
hashCode = (long) keyNativeHashCode.invokeExact(nativeValue);
}
catch (Throwable throwable) {
throw handleThrowable(throwable);
}
int hashTableOffset = offset / 2 * HASH_MULTIPLIER;
int hashTableSize = positionCount / 2 * HASH_MULTIPLIER;
int hash = (int) Math.floorMod(hashCode, hashTableSize);
while (true) {
int keyPosition = hashTable[hashTableOffset + hash];
if (keyPosition == -1) {
return -1;
}
boolean match;
try {
match = (boolean) keyBlockNativeEquals.invokeExact(keyBlock, offset / 2 + keyPosition, nativeValue);
}
catch (Throwable throwable) {
throw handleThrowable(throwable);
}
if (match) {
return keyPosition * 2 + 1;
}
hash++;
if (hash == hashTableSize) {
hash = 0;
}
}
}
public int seekKeyExact(Block nativeValue)
{
if (positionCount == 0) {
return -1;
}
long hashCode;
try {
hashCode = (long) keyNativeHashCode.invokeExact(nativeValue);
}
catch (Throwable throwable) {
throw handleThrowable(throwable);
}
int hashTableOffset = offset / 2 * HASH_MULTIPLIER;
int hashTableSize = positionCount / 2 * HASH_MULTIPLIER;
int hash = (int) Math.floorMod(hashCode, hashTableSize);
while (true) {
int keyPosition = hashTable[hashTableOffset + hash];
if (keyPosition == -1) {
return -1;
}
boolean match;
try {
match = (boolean) keyBlockNativeEquals.invokeExact(keyBlock, offset / 2 + keyPosition, nativeValue);
}
catch (Throwable throwable) {
throw handleThrowable(throwable);
}
if (match) {
return keyPosition * 2 + 1;
}
hash++;
if (hash == hashTableSize) {
hash = 0;
}
}
}
private static RuntimeException handleThrowable(Throwable throwable)
{
if (throwable instanceof Error) {
throw (Error) throwable;
}
if (throwable instanceof PrestoException) {
throw (PrestoException) throwable;
}
throw new PrestoException(GENERIC_INTERNAL_ERROR, throwable);
}
}