/*
* 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.util;
import com.facebook.presto.metadata.FunctionRegistry;
import com.facebook.presto.spi.PrestoException;
import com.facebook.presto.spi.type.Type;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import it.unimi.dsi.fastutil.Hash;
import it.unimi.dsi.fastutil.booleans.BooleanOpenHashSet;
import it.unimi.dsi.fastutil.doubles.DoubleHash;
import it.unimi.dsi.fastutil.doubles.DoubleOpenCustomHashSet;
import it.unimi.dsi.fastutil.longs.LongHash;
import it.unimi.dsi.fastutil.longs.LongOpenCustomHashSet;
import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.util.Collection;
import java.util.Set;
import static com.facebook.presto.spi.StandardErrorCode.GENERIC_INTERNAL_ERROR;
import static com.facebook.presto.spi.function.OperatorType.EQUAL;
import static com.facebook.presto.spi.function.OperatorType.HASH_CODE;
import static java.lang.Math.toIntExact;
public final class FastutilSetHelper
{
private FastutilSetHelper() {}
@SuppressWarnings({"unchecked"})
public static Set<?> toFastutilHashSet(Set<?> set, Type type, FunctionRegistry registry)
{
// 0.25 as the load factor is chosen because the argument set is assumed to be small (<10000),
// and the return set is assumed to be read-heavy.
// The performance of InCodeGenerator heavily depends on the load factor being small.
Class<?> javaElementType = type.getJavaType();
if (javaElementType == long.class) {
return new LongOpenCustomHashSet((Collection<Long>) set, 0.25f, new LongStrategy(registry, type));
}
if (javaElementType == double.class) {
return new DoubleOpenCustomHashSet((Collection<Double>) set, 0.25f, new DoubleStrategy(registry, type));
}
if (javaElementType == boolean.class) {
return new BooleanOpenHashSet((Collection<Boolean>) set, 0.25f);
}
else if (!type.getJavaType().isPrimitive()) {
return new ObjectOpenCustomHashSet(set, 0.25f, new ObjectStrategy(registry, type));
}
else {
throw new UnsupportedOperationException("Unsupported native type in set: " + type.getJavaType() + " with type " + type.getTypeSignature());
}
}
public static boolean in(boolean booleanValue, BooleanOpenHashSet set)
{
return set.contains(booleanValue);
}
public static boolean in(double doubleValue, DoubleOpenCustomHashSet set)
{
return set.contains(doubleValue);
}
public static boolean in(long longValue, LongOpenCustomHashSet set)
{
return set.contains(longValue);
}
public static boolean in(Object objectValue, ObjectOpenCustomHashSet<?> set)
{
return set.contains(objectValue);
}
private static final class LongStrategy
implements LongHash.Strategy
{
private final MethodHandle hashCodeHandle;
private final MethodHandle equalsHandle;
private LongStrategy(FunctionRegistry registry, Type type)
{
hashCodeHandle = registry.getScalarFunctionImplementation(registry.resolveOperator(HASH_CODE, ImmutableList.of(type))).getMethodHandle();
equalsHandle = registry.getScalarFunctionImplementation(registry.resolveOperator(EQUAL, ImmutableList.of(type, type))).getMethodHandle();
}
@Override
public int hashCode(long value)
{
try {
return Long.hashCode((long) hashCodeHandle.invokeExact(value));
}
catch (Throwable t) {
Throwables.propagateIfInstanceOf(t, Error.class);
Throwables.propagateIfInstanceOf(t, PrestoException.class);
throw new PrestoException(GENERIC_INTERNAL_ERROR, t);
}
}
@Override
public boolean equals(long a, long b)
{
try {
return (boolean) equalsHandle.invokeExact(a, b);
}
catch (Throwable t) {
Throwables.propagateIfInstanceOf(t, Error.class);
Throwables.propagateIfInstanceOf(t, PrestoException.class);
throw new PrestoException(GENERIC_INTERNAL_ERROR, t);
}
}
}
private static final class DoubleStrategy
implements DoubleHash.Strategy
{
private final MethodHandle hashCodeHandle;
private final MethodHandle equalsHandle;
private DoubleStrategy(FunctionRegistry registry, Type type)
{
hashCodeHandle = registry.getScalarFunctionImplementation(registry.resolveOperator(HASH_CODE, ImmutableList.of(type))).getMethodHandle();
equalsHandle = registry.getScalarFunctionImplementation(registry.resolveOperator(EQUAL, ImmutableList.of(type, type))).getMethodHandle();
}
@Override
public int hashCode(double value)
{
try {
return Long.hashCode((long) hashCodeHandle.invokeExact(value));
}
catch (Throwable t) {
Throwables.propagateIfInstanceOf(t, Error.class);
Throwables.propagateIfInstanceOf(t, PrestoException.class);
throw new PrestoException(GENERIC_INTERNAL_ERROR, t);
}
}
@Override
public boolean equals(double a, double b)
{
try {
return (boolean) equalsHandle.invokeExact(a, b);
}
catch (Throwable t) {
Throwables.propagateIfInstanceOf(t, Error.class);
Throwables.propagateIfInstanceOf(t, PrestoException.class);
throw new PrestoException(GENERIC_INTERNAL_ERROR, t);
}
}
}
private static final class ObjectStrategy
implements Hash.Strategy
{
private final MethodHandle hashCodeHandle;
private final MethodHandle equalsHandle;
private ObjectStrategy(FunctionRegistry registry, Type type)
{
hashCodeHandle = registry.getScalarFunctionImplementation(registry.resolveOperator(HASH_CODE, ImmutableList.of(type)))
.getMethodHandle()
.asType(MethodType.methodType(long.class, Object.class));
equalsHandle = registry.getScalarFunctionImplementation(registry.resolveOperator(EQUAL, ImmutableList.of(type, type)))
.getMethodHandle()
.asType(MethodType.methodType(boolean.class, Object.class, Object.class));
}
@Override
public int hashCode(Object value)
{
try {
return toIntExact(Long.hashCode((long) hashCodeHandle.invokeExact(value)));
}
catch (Throwable t) {
Throwables.propagateIfInstanceOf(t, Error.class);
Throwables.propagateIfInstanceOf(t, PrestoException.class);
throw new PrestoException(GENERIC_INTERNAL_ERROR, t);
}
}
@Override
public boolean equals(Object a, Object b)
{
try {
return (boolean) equalsHandle.invokeExact(a, b);
}
catch (Throwable t) {
Throwables.propagateIfInstanceOf(t, Error.class);
Throwables.propagateIfInstanceOf(t, PrestoException.class);
throw new PrestoException(GENERIC_INTERNAL_ERROR, t);
}
}
}
}