/* * Copyright (C) 2015 SoftIndex LLC. * * 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 io.datakernel.serializer.asm; import io.datakernel.bytebuf.SerializationUtils; import io.datakernel.codegen.Expression; import io.datakernel.codegen.ForVar; import io.datakernel.codegen.Variable; import io.datakernel.serializer.CompatibilityLevel; import io.datakernel.serializer.NullableOptimization; import io.datakernel.serializer.SerializerBuilder; import java.util.HashMap; import java.util.Map; import static io.datakernel.codegen.Expressions.*; import static io.datakernel.codegen.utils.Preconditions.check; import static io.datakernel.codegen.utils.Preconditions.checkNotNull; public class SerializerGenHppcSet implements SerializerGen, NullableOptimization { private static Map<Class<?>, SerializerGen> primitiveSerializers = new HashMap<Class<?>, SerializerGen>() {{ put(byte.class, new SerializerGenByte()); put(short.class, new SerializerGenShort()); put(int.class, new SerializerGenInt(true)); put(long.class, new SerializerGenLong(false)); put(float.class, new SerializerGenFloat()); put(double.class, new SerializerGenDouble()); put(char.class, new SerializerGenChar()); }}; private static String toUpperCamel(String s) { StringBuilder sb = new StringBuilder(); sb.append(Character.toUpperCase(s.charAt(0))); sb.append(s.substring(1)); return sb.toString(); } public static SerializerGenBuilder serializerGenBuilder(final Class<?> setType, final Class<?> valueType) { String prefix = toUpperCamel(valueType.getSimpleName()); check(setType.getSimpleName().startsWith(prefix), "Expected setType '%s', but was begin '%s'", setType.getSimpleName(), prefix); return new SerializerGenBuilder() { @Override public SerializerGen serializer(Class<?> type, final SerializerForType[] generics, SerializerGen fallback) { SerializerGen valueSerializer; if (generics.length == 1) { check(valueType == Object.class, "valueType must be Object.class"); valueSerializer = generics[0].serializer; } else { valueSerializer = primitiveSerializers.get(valueType); } return new SerializerGenHppcSet(setType, valueType, checkNotNull(valueSerializer)); } }; } private final Class<?> setType; private final Class<?> hashSetType; private final Class<?> iteratorType; private final Class<?> valueType; private final SerializerGen valueSerializer; private final boolean nullable; public SerializerGenHppcSet(Class<?> setType, Class<?> valueType, SerializerGen valueSerializer, boolean nullable) { this.setType = setType; this.valueType = valueType; this.valueSerializer = valueSerializer; this.nullable = nullable; try { String prefix = toUpperCamel(valueType.getSimpleName()); this.iteratorType = Class.forName("com.carrotsearch.hppc.cursors." + prefix + "Cursor"); this.hashSetType = Class.forName("com.carrotsearch.hppc." + prefix + "OpenHashSet"); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } public SerializerGenHppcSet(Class<?> setType, Class<?> valueType, SerializerGen valueSerializer) { this(setType, valueType, valueSerializer, false); } @Override public void getVersions(VersionsCollector versions) { versions.addRecursive(valueSerializer); } @Override public boolean isInline() { return true; } @Override public Class<?> getRawType() { return setType; } @Override public void prepareSerializeStaticMethods(int version, SerializerBuilder.StaticMethods staticMethods, CompatibilityLevel compatibilityLevel) { valueSerializer.prepareSerializeStaticMethods(version, staticMethods, compatibilityLevel); } @Override public Expression serialize(final Expression byteArray, final Variable off, Expression value, final int version, final SerializerBuilder.StaticMethods staticMethods, final CompatibilityLevel compatibilityLevel) { Expression length = call(value, "size"); Expression writeLength = set(off, callStatic(SerializationUtils.class, "writeVarInt", byteArray, off, (!nullable ? length : inc(length)))); Expression hppcSetForEach = hppcSetForEach(iteratorType, value, new ForVar() { @Override public Expression forVar(Expression it) { return set(off, valueSerializer.serialize(byteArray, off, cast(it, valueSerializer.getRawType()), version, staticMethods, compatibilityLevel)); } }); if (!nullable) { return sequence(writeLength, hppcSetForEach, off); } else { return ifThenElse(isNull(value), sequence(set(off, callStatic(SerializationUtils.class, "writeVarInt", byteArray, off, value(0))), off), sequence(writeLength, hppcSetForEach, off)); } } @Override public void prepareDeserializeStaticMethods(int version, SerializerBuilder.StaticMethods staticMethods, CompatibilityLevel compatibilityLevel) { valueSerializer.prepareDeserializeStaticMethods(version, staticMethods, compatibilityLevel); } @Override public Expression deserialize(Class<?> targetType, final int version, final SerializerBuilder.StaticMethods staticMethods, final CompatibilityLevel compatibilityLevel) { final Class<?> valueType = valueSerializer.getRawType(); Expression length = let(call(arg(0), "readVarInt")); final Expression set = let(constructor(hashSetType)); Expression expressionFor = expressionFor((!nullable ? length : dec(length)), new ForVar() { @Override public Expression forVar(Expression it) { return sequence( call(set, "add", cast(valueSerializer.deserialize(valueType, version, staticMethods, compatibilityLevel), SerializerGenHppcSet.this.valueType)), voidExp() ); } }); if (!nullable) { return sequence(set, expressionFor, set); } else { return ifThenElse(cmpEq(length, value(0)), nullRef(hashSetType), sequence(set, expressionFor, set)); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SerializerGenHppcSet that = (SerializerGenHppcSet) o; if (nullable != that.nullable) return false; if (setType != null ? !setType.equals(that.setType) : that.setType != null) return false; if (hashSetType != null ? !hashSetType.equals(that.hashSetType) : that.hashSetType != null) return false; if (iteratorType != null ? !iteratorType.equals(that.iteratorType) : that.iteratorType != null) return false; if (valueType != null ? !valueType.equals(that.valueType) : that.valueType != null) return false; return !(valueSerializer != null ? !valueSerializer.equals(that.valueSerializer) : that.valueSerializer != null); } @Override public int hashCode() { int result = setType != null ? setType.hashCode() : 0; result = 31 * result + (hashSetType != null ? hashSetType.hashCode() : 0); result = 31 * result + (iteratorType != null ? iteratorType.hashCode() : 0); result = 31 * result + (valueType != null ? valueType.hashCode() : 0); result = 31 * result + (valueSerializer != null ? valueSerializer.hashCode() : 0); result = 31 * result + (nullable ? 1 : 0); return result; } @Override public SerializerGen setNullable() { return new SerializerGenHppcSet(setType, valueType, valueSerializer, true); } }